Co-authored-by: Murphy <96611012+murphyatwork@users.noreply.github.com>
This commit is contained in:
parent
449a3ab2a6
commit
cc892a7196
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
package com.starrocks.common.util;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
|
@ -136,6 +137,16 @@ public class RuntimeProfile {
|
|||
return addCounter(name, type, strategy, ROOT_COUNTER);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void addCounter(String name, String parentName, Counter counter) {
|
||||
this.counterMap.put(name, Pair.create(counter, parentName));
|
||||
if (!childCounterMap.containsKey(parentName)) {
|
||||
childCounterMap.putIfAbsent(parentName, Sets.newConcurrentHashSet());
|
||||
}
|
||||
Set<String> childNames = childCounterMap.get(parentName);
|
||||
childNames.add(name);
|
||||
}
|
||||
|
||||
public Counter addCounter(String name, TUnit type, TCounterStrategy strategy, String parentName) {
|
||||
if (strategy == null) {
|
||||
strategy = Counter.createStrategy(type);
|
||||
|
|
|
|||
|
|
@ -453,7 +453,10 @@ public class ExplainAnalyzer {
|
|||
// 7. Non default Variables
|
||||
String sessionVariables = summaryProfile.getInfoString("NonDefaultSessionVariables");
|
||||
Map<String, SessionVariable.NonDefaultValue> variables = Maps.newTreeMap();
|
||||
variables.putAll(SessionVariable.NonDefaultValue.parseFrom(sessionVariables));
|
||||
var map = SessionVariable.NonDefaultValue.parseFrom(sessionVariables);
|
||||
if (map != null) {
|
||||
variables.putAll(map);
|
||||
}
|
||||
if (MapUtils.isNotEmpty(variables)) {
|
||||
appendSummaryLine("NonDefaultVariables:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
|
|
@ -936,13 +939,257 @@ public class ExplainAnalyzer {
|
|||
|
||||
if (CollectionUtils.isNotEmpty(mergedUniqueMetrics.getChildCounterMap().get(RuntimeProfile.ROOT_COUNTER))) {
|
||||
appendDetailLine("Counters:");
|
||||
metricTraverser.accept(name -> true, true);
|
||||
boolean applied = appendGroupedMetricsByOperator(mergedUniqueMetrics, nodeInfo);
|
||||
if (!applied) {
|
||||
metricTraverser.accept(name -> true, true);
|
||||
}
|
||||
}
|
||||
|
||||
popIndent(); // metric indent
|
||||
}
|
||||
}
|
||||
|
||||
private boolean appendGroupedMetricsByOperator(RuntimeProfile uniqueMetrics, NodeInfo nodeInfo) {
|
||||
if (nodeInfo.element.instanceOf(JoinNode.class)) {
|
||||
appendGroupedMetricsForJoin(uniqueMetrics, nodeInfo);
|
||||
} else if (nodeInfo.element.instanceOf(AggregationNode.class)) {
|
||||
appendGroupedMetricsForAggregate(uniqueMetrics, nodeInfo);
|
||||
} else if (nodeInfo.element.instanceOf(ScanNode.class)) {
|
||||
appendGroupedMetricsForScan(uniqueMetrics, nodeInfo);
|
||||
} else {
|
||||
appendGroupedMetricsForOthers(uniqueMetrics, nodeInfo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void appendGroupedMetricsForJoin(RuntimeProfile uniqueMetrics, NodeInfo nodeInfo) {
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
|
||||
appendDetailLine("HashTable:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendMetric(uniqueMetrics, nodeInfo, "BuildBuckets");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "BuildKeysPerBucket%");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "BuildHashTableTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "BuildConjunctEvaluateTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "HashTableMemoryUsage");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "PartitionNums");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "PartitionProbeOverhead");
|
||||
popIndent();
|
||||
|
||||
appendDetailLine("ProbeSide:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendMetric(uniqueMetrics, nodeInfo, "SearchHashTableTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "probeCount");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "ProbeConjunctEvaluateTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "CopyRightTableChunkTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "OtherJoinConjunctEvaluateTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "OutputBuildColumnTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "OutputProbeColumnTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "WhereConjunctEvaluateTime");
|
||||
popIndent();
|
||||
|
||||
appendDetailLine("RuntimeFilter:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendMetric(uniqueMetrics, nodeInfo, "RuntimeFilterBuildTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "RuntimeFilterNum");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "PartialRuntimeMembershipFilterBytes");
|
||||
popIndent();
|
||||
|
||||
appendGroupedMetricsOthers(uniqueMetrics, nodeInfo, getJoinKnownMetrics());
|
||||
|
||||
popIndent();
|
||||
}
|
||||
|
||||
private void appendGroupedMetricsForAggregate(RuntimeProfile uniqueMetrics, NodeInfo nodeInfo) {
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
|
||||
appendDetailLine("Aggregation:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendMetric(uniqueMetrics, nodeInfo, "AggFuncComputeTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "ExprComputeTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "ExprReleaseTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "HashTableSize");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "HashTableMemoryUsage");
|
||||
popIndent();
|
||||
|
||||
appendDetailLine("Memory Management:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendMetric(uniqueMetrics, nodeInfo, "ChunkBufferPeakMem");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "ChunkBufferPeakSize");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "StateAllocate");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "StateDestroy");
|
||||
popIndent();
|
||||
|
||||
appendDetailLine("Result Processing:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendMetric(uniqueMetrics, nodeInfo, "GetResultsTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "ResultAggAppendTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "ResultGroupByAppendTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "ResultIteratorTime");
|
||||
popIndent();
|
||||
|
||||
appendDetailLine("Data Flow:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendMetric(uniqueMetrics, nodeInfo, "InputRowCount");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "PassThroughRowCount");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "StreamingTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "RowsReturned");
|
||||
popIndent();
|
||||
|
||||
appendGroupedMetricsOthers(uniqueMetrics, nodeInfo, getAggregateKnownMetrics());
|
||||
|
||||
popIndent();
|
||||
}
|
||||
|
||||
private void appendGroupedMetricsForScan(RuntimeProfile uniqueMetrics, NodeInfo nodeInfo) {
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
|
||||
// Scan Filters & Row Processing
|
||||
appendDetailLine("ScanFilters:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendFilterMetrics(uniqueMetrics, nodeInfo, "ShortKeyFilter");
|
||||
appendFilterMetrics(uniqueMetrics, nodeInfo, "BitmapIndexFilter");
|
||||
appendFilterMetrics(uniqueMetrics, nodeInfo, "BloomFilterFilter");
|
||||
appendFilterMetrics(uniqueMetrics, nodeInfo, "ZoneMapFilter");
|
||||
appendFilterMetrics(uniqueMetrics, nodeInfo, "PredFilter");
|
||||
appendFilterMetrics(uniqueMetrics, nodeInfo, "GinFilter");
|
||||
appendFilterMetrics(uniqueMetrics, nodeInfo, "VectorIndexFilter");
|
||||
appendFilterMetrics(uniqueMetrics, nodeInfo, "DelVecFilter");
|
||||
appendFilterMetrics(uniqueMetrics, nodeInfo, "RuntimeFilter");
|
||||
popIndent();
|
||||
|
||||
appendDetailLine("RowProcessing:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendMetric(uniqueMetrics, nodeInfo, "RawRowsRead");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "RowsRead");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "DictDecode");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "DictDecodeCount");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "ChunkCopy");
|
||||
popIndent(); // RowProcessing indent
|
||||
|
||||
appendDetailLine("IOMetrics:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendMetric(uniqueMetrics, nodeInfo, "IOTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "BytesRead");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "CompressedBytesRead");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "UncompressedBytesRead");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "ReadPagesNum");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "CachedPagesNum");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "BlockFetch");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "BlockFetchCount");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "BlockSeek");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "BlockSeekCount");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "DecompressTime");
|
||||
popIndent();
|
||||
|
||||
appendDetailLine("SegmentProcessing:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendMetric(uniqueMetrics, nodeInfo, "TabletCount");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "SegmentsReadCount");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "RowsetsReadCount");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "TotalColumnsDataPageCount");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "ColumnIteratorInit");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "BitmapIndexIteratorInit");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "FlatJsonInit");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "FlatJsonMerge");
|
||||
popIndent();
|
||||
|
||||
appendDetailLine("IOTask:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendMetric(uniqueMetrics, nodeInfo, "IOTaskExecTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "IOTaskWaitTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "SubmitTaskCount");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "SubmitTaskTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "PrepareChunkSourceTime");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "MorselsCount");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "PeakIOTasks");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "PeakScanTaskQueueSize");
|
||||
popIndent();
|
||||
|
||||
appendDetailLine("IOBuffer:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendMetric(uniqueMetrics, nodeInfo, "PeakChunkBufferMemoryUsage");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "PeakChunkBufferSize");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "ChunkBufferCapacity");
|
||||
appendMetric(uniqueMetrics, nodeInfo, "DefaultChunkBufferCapacity");
|
||||
popIndent();
|
||||
|
||||
appendGroupedMetricsOthers(uniqueMetrics, nodeInfo, getScanKnownMetrics());
|
||||
|
||||
popIndent(); // main indent
|
||||
}
|
||||
|
||||
private void appendFilterMetrics(RuntimeProfile uniqueMetrics, NodeInfo nodeInfo, String filterName) {
|
||||
Counter timeCounter = uniqueMetrics.getCounter(filterName);
|
||||
Counter rowsCounter = uniqueMetrics.getCounter(filterName + "Rows");
|
||||
|
||||
if (timeCounter == null && rowsCounter == null) {
|
||||
return;
|
||||
}
|
||||
if (rowsCounter == null || rowsCounter.getValue() == 0L) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Object> items = Lists.newArrayList();
|
||||
items.add(filterName);
|
||||
items.add(": ");
|
||||
|
||||
items.add("Rows: ");
|
||||
items.add(rowsCounter);
|
||||
if (timeCounter != null) {
|
||||
items.add(", ");
|
||||
}
|
||||
|
||||
if (timeCounter != null) {
|
||||
items.add("Time: ");
|
||||
items.add(timeCounter);
|
||||
|
||||
Counter minCounter = uniqueMetrics.getCounter(RuntimeProfile.MERGED_INFO_PREFIX_MIN + filterName);
|
||||
Counter maxCounter = uniqueMetrics.getCounter(RuntimeProfile.MERGED_INFO_PREFIX_MAX + filterName);
|
||||
if (minCounter != null || maxCounter != null) {
|
||||
items.add(" [");
|
||||
items.add("min=");
|
||||
items.add(minCounter);
|
||||
items.add(", max=");
|
||||
items.add(maxCounter);
|
||||
items.add("]");
|
||||
}
|
||||
}
|
||||
|
||||
appendDetailLine(items.toArray());
|
||||
}
|
||||
|
||||
private void appendMetric(RuntimeProfile uniqueMetrics, NodeInfo nodeInfo, String name) {
|
||||
Counter counter = uniqueMetrics.getCounter(name);
|
||||
if (counter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Counter minCounter = uniqueMetrics.getCounter(RuntimeProfile.MERGED_INFO_PREFIX_MIN + name);
|
||||
Counter maxCounter = uniqueMetrics.getCounter(RuntimeProfile.MERGED_INFO_PREFIX_MAX + name);
|
||||
boolean needHighlight = colorExplainOutput && nodeInfo.isTimeConsumingMetric(uniqueMetrics, name);
|
||||
|
||||
List<Object> items = Lists.newArrayList();
|
||||
if (needHighlight) {
|
||||
items.add(getBackGround());
|
||||
}
|
||||
items.add(name);
|
||||
items.add(": ");
|
||||
items.add(counter);
|
||||
if (minCounter != null || maxCounter != null) {
|
||||
items.add(" [");
|
||||
items.add("min=");
|
||||
items.add(minCounter);
|
||||
items.add(", max=");
|
||||
items.add(maxCounter);
|
||||
items.add("]");
|
||||
}
|
||||
if (needHighlight) {
|
||||
items.add(ANSI_RESET);
|
||||
}
|
||||
appendDetailLine(items.toArray());
|
||||
}
|
||||
|
||||
private void appendDetailMetric(NodeInfo nodeInfo, RuntimeProfile uniqueMetrics, String name,
|
||||
boolean enableHighlight) {
|
||||
if (name.startsWith(RuntimeProfile.MERGED_INFO_PREFIX_MIN)
|
||||
|
|
@ -1397,4 +1644,103 @@ public class ExplainAnalyzer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void appendGroupedMetricsOthers(RuntimeProfile uniqueMetrics, NodeInfo nodeInfo, Set<String> knownMetrics) {
|
||||
Set<String> allMetrics = uniqueMetrics.getCounterMap().keySet();
|
||||
Set<String> otherMetrics = Sets.newTreeSet();
|
||||
|
||||
for (String metric : allMetrics) {
|
||||
if (!knownMetrics.contains(metric) &&
|
||||
!metric.startsWith(RuntimeProfile.MERGED_INFO_PREFIX_MIN) &&
|
||||
!metric.startsWith(RuntimeProfile.MERGED_INFO_PREFIX_MAX) &&
|
||||
!EXCLUDE_DETAIL_METRIC_NAMES.contains(metric)) {
|
||||
otherMetrics.add(metric);
|
||||
}
|
||||
}
|
||||
|
||||
if (!otherMetrics.isEmpty()) {
|
||||
appendDetailLine("Others:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
for (String metric : otherMetrics) {
|
||||
appendMetric(uniqueMetrics, nodeInfo, metric);
|
||||
}
|
||||
popIndent();
|
||||
}
|
||||
}
|
||||
|
||||
private void appendGroupedMetricsForOthers(RuntimeProfile uniqueMetrics, NodeInfo nodeInfo) {
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
appendDetailLine("Others:");
|
||||
pushIndent(GraphElement.LEAF_METRIC_INDENT);
|
||||
|
||||
Set<String> allMetrics = uniqueMetrics.getCounterMap().keySet();
|
||||
Set<String> otherMetrics = Sets.newTreeSet();
|
||||
|
||||
for (String metric : allMetrics) {
|
||||
if (!metric.startsWith(RuntimeProfile.MERGED_INFO_PREFIX_MIN) &&
|
||||
!metric.startsWith(RuntimeProfile.MERGED_INFO_PREFIX_MAX) &&
|
||||
!EXCLUDE_DETAIL_METRIC_NAMES.contains(metric)) {
|
||||
otherMetrics.add(metric);
|
||||
}
|
||||
}
|
||||
|
||||
for (String metric : otherMetrics) {
|
||||
appendMetric(uniqueMetrics, nodeInfo, metric);
|
||||
}
|
||||
|
||||
popIndent();
|
||||
popIndent();
|
||||
}
|
||||
|
||||
private Set<String> getJoinKnownMetrics() {
|
||||
return Sets.newHashSet(
|
||||
// HashTable group metrics
|
||||
"BuildBuckets", "BuildKeysPerBucket%", "BuildHashTableTime", "BuildConjunctEvaluateTime",
|
||||
"HashTableMemoryUsage", "PartitionNums", "PartitionProbeOverhead",
|
||||
// ProbeSide group metrics
|
||||
"SearchHashTableTime", "probeCount", "ProbeConjunctEvaluateTime", "CopyRightTableChunkTime",
|
||||
"OtherJoinConjunctEvaluateTime", "OutputBuildColumnTime", "OutputProbeColumnTime",
|
||||
"WhereConjunctEvaluateTime",
|
||||
// RuntimeFilter group metrics
|
||||
"RuntimeFilterBuildTime", "RuntimeFilterNum", "PartialRuntimeMembershipFilterBytes",
|
||||
// Legacy metrics (kept for backward compatibility)
|
||||
"BuildRows", "BuildTime", "BuildHashTableMemoryUsage", "HashBuckets", "HashCollisions",
|
||||
"BuildSpillBytes", "BuildSpillTime", "ProbeRows", "ProbeTime", "JoinOutputRows",
|
||||
"RowsAfterJoinFilter", "JoinConjunctEvaluateTime", "ProbeSpillBytes", "ProbeSpillTime",
|
||||
"RuntimeFilterPushDownTime", "RuntimeFilterInputRows", "RuntimeFilterOutputRows"
|
||||
);
|
||||
}
|
||||
|
||||
private Set<String> getAggregateKnownMetrics() {
|
||||
return Sets.newHashSet(
|
||||
"AggInputRows", "AggOutputRows", "AggComputeTime", "HashAggBuildTime", "HashAggProbeTime",
|
||||
"HashBuckets", "HashCollisions", "HashTableMemoryUsage", "SpillBytes", "SpillTime",
|
||||
"DistinctInputRows", "DistinctOutputRows", "DistinctTime",
|
||||
// Additional metrics from real data
|
||||
"AggFuncComputeTime", "ChunkBufferPeakMem", "ChunkBufferPeakSize", "ExprComputeTime",
|
||||
"ExprReleaseTime", "GetResultsTime", "HashTableSize", "InputRowCount", "PassThroughRowCount",
|
||||
"ResultAggAppendTime", "ResultGroupByAppendTime", "ResultIteratorTime", "RowsReturned",
|
||||
"StateAllocate", "StateDestroy", "StreamingTime"
|
||||
);
|
||||
}
|
||||
|
||||
private Set<String> getScanKnownMetrics() {
|
||||
return Sets.newHashSet(
|
||||
"ShortKeyFilter", "ShortKeyFilterRows", "BitmapIndexFilter", "BitmapIndexFilterRows",
|
||||
"BloomFilterFilter", "BloomFilterFilterRows", "ZoneMapFilter", "ZoneMapFilterRows",
|
||||
"PredFilter", "PredFilterRows", "GinFilter", "GinFilterRows", "VectorIndexFilter",
|
||||
"VectorIndexFilterRows", "DelVecFilter", "DelVecFilterRows", "RuntimeFilter", "RuntimeFilterRows",
|
||||
"RawRowsRead", "RowsRead", "DictDecode", "DictDecodeCount", "ChunkCopy",
|
||||
"IOTime", "BytesRead", "CompressedBytesRead", "UncompressedBytesRead", "ReadPagesNum",
|
||||
"CachedPagesNum", "BlockFetch", "BlockFetchCount", "BlockSeek", "BlockSeekCount", "DecompressTime",
|
||||
"TabletCount", "SegmentsReadCount", "RowsetsReadCount", "TotalColumnsDataPageCount",
|
||||
"ColumnIteratorInit", "BitmapIndexIteratorInit", "FlatJsonInit", "FlatJsonMerge",
|
||||
"IOTaskExecTime", "IOTaskWaitTime", "SubmitTaskCount", "SubmitTaskTime", "PrepareChunkSourceTime",
|
||||
"MorselsCount", "PeakIOTasks", "PeakScanTaskQueueSize", "PeakChunkBufferMemoryUsage",
|
||||
"PeakChunkBufferSize", "ChunkBufferCapacity", "DefaultChunkBufferCapacity",
|
||||
"CreateSegmentIter", "GetDelVec", "GetDeltaColumnGroup", "GetRowsets", "ReadPKIndex",
|
||||
"ProcessVectorDistanceAndIdTime", "VectorSearchTime",
|
||||
"PushdownAccessPaths", "PushdownPredicates"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,464 @@
|
|||
// Copyright 2021-present StarRocks, Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.starrocks.sql;
|
||||
|
||||
import com.starrocks.common.util.Counter;
|
||||
import com.starrocks.common.util.ProfileManager;
|
||||
import com.starrocks.common.util.ProfilingExecPlan;
|
||||
import com.starrocks.common.util.RuntimeProfile;
|
||||
import com.starrocks.planner.AggregationNode;
|
||||
import com.starrocks.planner.JoinNode;
|
||||
import com.starrocks.planner.ResultSink;
|
||||
import com.starrocks.planner.ScanNode;
|
||||
import com.starrocks.planner.SortNode;
|
||||
import com.starrocks.thrift.TUnit;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class ExplainAnalyzerTest {
|
||||
private RuntimeProfile mockProfile;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
// Only initialize basic profile structure, each test will create its own operator profiles
|
||||
mockProfile = new RuntimeProfile("Query");
|
||||
|
||||
// Create Summary profile
|
||||
RuntimeProfile summaryProfile = new RuntimeProfile("Summary");
|
||||
summaryProfile.addInfoString(ProfileManager.QUERY_ID, "test-query-id");
|
||||
summaryProfile.addInfoString(ProfileManager.QUERY_STATE, "Finished");
|
||||
summaryProfile.addInfoString("Query State", "Finished");
|
||||
summaryProfile.addInfoString("NonDefaultSessionVariables", "");
|
||||
summaryProfile.addCounter(ProfileManager.PROFILE_COLLECT_TIME, "Summary",
|
||||
new Counter(TUnit.TIME_NS, null, 100000000));
|
||||
mockProfile.addChild(summaryProfile);
|
||||
|
||||
// Create Planner profile
|
||||
RuntimeProfile plannerProfile = new RuntimeProfile("Planner");
|
||||
mockProfile.addChild(plannerProfile);
|
||||
|
||||
// Create Execution profile
|
||||
RuntimeProfile executionProfile = new RuntimeProfile("Execution");
|
||||
executionProfile.addCounter("FrontendProfileMergeTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 1000));
|
||||
executionProfile.addCounter("QueryPeakMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 1024 * 1024));
|
||||
executionProfile.addCounter("QueryAllocatedMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 512 * 1024));
|
||||
executionProfile.addCounter("QueryCumulativeOperatorTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 1000000));
|
||||
executionProfile.addCounter("QueryCumulativeScanTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 500000));
|
||||
executionProfile.addCounter("QueryCumulativeNetworkTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 100000));
|
||||
executionProfile.addCounter("QueryPeakScheduleTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 50000));
|
||||
mockProfile.addChild(executionProfile);
|
||||
|
||||
// Create Fragment profile
|
||||
RuntimeProfile fragmentProfile = new RuntimeProfile("Fragment 0");
|
||||
fragmentProfile.addCounter("BackendNum", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 1));
|
||||
fragmentProfile.addCounter("InstancePeakMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 1024 * 1024));
|
||||
fragmentProfile.addCounter("InstanceAllocatedMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 512 * 1024));
|
||||
fragmentProfile.addCounter("FragmentInstancePrepareTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 1000));
|
||||
executionProfile.addChild(fragmentProfile);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
mockProfile = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanOperator() throws NoSuchFieldException, IllegalAccessException {
|
||||
// Create OLAP_SCAN operator profile
|
||||
RuntimeProfile olapScanProfile = new RuntimeProfile("OLAP_SCAN (plan_node_id=0)");
|
||||
olapScanProfile.addCounter("TotalTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 228000000));
|
||||
olapScanProfile.addCounter("CPUTime", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.TIME_NS, null, 31000000));
|
||||
olapScanProfile.addCounter("ScanTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 197000000));
|
||||
olapScanProfile.addCounter("OutputRows", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 8553457));
|
||||
|
||||
// Create CommonMetrics profile
|
||||
RuntimeProfile commonMetrics = new RuntimeProfile("CommonMetrics");
|
||||
commonMetrics.addCounter("OperatorTotalTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 228000000));
|
||||
commonMetrics.addCounter("PullRowNum", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 8553457));
|
||||
commonMetrics.addCounter("OperatorPeakMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 1024 * 1024));
|
||||
commonMetrics.addCounter("OperatorAllocatedMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 512 * 1024));
|
||||
olapScanProfile.addChild(commonMetrics);
|
||||
|
||||
// Create UniqueMetrics profile with scan-specific metrics
|
||||
RuntimeProfile uniqueMetrics = new RuntimeProfile("UniqueMetrics");
|
||||
uniqueMetrics.addCounter("ZoneMapFilter", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.TIME_NS, null, 28208));
|
||||
uniqueMetrics.addCounter("ZoneMapFilterRows", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 37558774));
|
||||
uniqueMetrics.addCounter("PredFilter", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.TIME_NS, null, 184998));
|
||||
uniqueMetrics.addCounter("PredFilterRows", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 45617439));
|
||||
uniqueMetrics.addCounter("ShortKeyFilter", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.TIME_NS, null, 3916));
|
||||
uniqueMetrics.addCounter("ShortKeyFilterRows", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, -51182470));
|
||||
uniqueMetrics.addCounter("RawRowsRead", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 54170896));
|
||||
uniqueMetrics.addCounter("RowsRead", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 8553457));
|
||||
uniqueMetrics.addCounter("IOTime", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.TIME_NS, null, 16403));
|
||||
uniqueMetrics.addCounter("BytesRead", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 37 * 1024 * 1024));
|
||||
uniqueMetrics.addCounter("TabletCount", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 13));
|
||||
uniqueMetrics.addCounter("SegmentsReadCount", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 1921));
|
||||
uniqueMetrics.addCounter("IOTaskExecTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 11372000));
|
||||
uniqueMetrics.addCounter("PeakChunkBufferMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 12 * 1024 * 1024));
|
||||
uniqueMetrics.addCounter("Unknown", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 12 * 1024 * 1024));
|
||||
olapScanProfile.addChild(uniqueMetrics);
|
||||
|
||||
// Create pipeline profile structure
|
||||
RuntimeProfile pipelineProfile = new RuntimeProfile("Pipeline 0");
|
||||
pipelineProfile.addChild(olapScanProfile);
|
||||
mockProfile.getChild("Execution").getChild("Fragment 0").addChild(pipelineProfile);
|
||||
|
||||
// Create mock plan with ScanNode
|
||||
ProfilingExecPlan scanPlan = new ProfilingExecPlan();
|
||||
Field level = ProfilingExecPlan.class.getDeclaredField("profileLevel");
|
||||
level.setAccessible(true);
|
||||
level.setInt(scanPlan, 1);
|
||||
|
||||
Field fragmentsField = ProfilingExecPlan.class.getDeclaredField("fragments");
|
||||
fragmentsField.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<ProfilingExecPlan.ProfilingFragment> fragments =
|
||||
(List<ProfilingExecPlan.ProfilingFragment>) fragmentsField.get(scanPlan);
|
||||
ProfilingExecPlan.ProfilingElement root =
|
||||
new ProfilingExecPlan.ProfilingElement(0, ScanNode.class);
|
||||
ProfilingExecPlan.ProfilingElement sink =
|
||||
new ProfilingExecPlan.ProfilingElement(-1, ResultSink.class);
|
||||
fragments.add(new ProfilingExecPlan.ProfilingFragment(sink, root));
|
||||
|
||||
String result = ExplainAnalyzer.analyze(scanPlan, mockProfile, List.of(0), false);
|
||||
|
||||
assertTrue(result.contains("ScanFilters"), result);
|
||||
assertTrue(result.contains("RowProcessing"), result);
|
||||
assertTrue(result.contains("IOMetrics"), result);
|
||||
assertTrue(result.contains("SegmentProcessing"), result);
|
||||
assertTrue(result.contains("IOTask"), result);
|
||||
assertTrue(result.contains("IOBuffer"), result);
|
||||
assertTrue(result.contains("ZoneMapFilter: "), result);
|
||||
assertTrue(result.contains("PredFilter: "), result);
|
||||
assertTrue(result.contains("ShortKeyFilter:"), result);
|
||||
assertTrue(result.contains("Others"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAggregationOperator() throws NoSuchFieldException, IllegalAccessException {
|
||||
// Create mock profile for aggregation operator
|
||||
RuntimeProfile aggProfile = new RuntimeProfile("HASH_AGG (plan_node_id=1)");
|
||||
aggProfile.addCounter("TotalTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 150000000));
|
||||
aggProfile.addCounter("CPUTime", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.TIME_NS, null, 120000000));
|
||||
aggProfile.addCounter("OutputRows", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 100000));
|
||||
|
||||
// Create CommonMetrics profile
|
||||
RuntimeProfile commonMetrics = new RuntimeProfile("CommonMetrics");
|
||||
commonMetrics.addCounter("OperatorTotalTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 150000000));
|
||||
commonMetrics.addCounter("PullRowNum", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 1000000));
|
||||
commonMetrics.addCounter("OperatorPeakMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 64 * 1024 * 1024));
|
||||
commonMetrics.addCounter("OperatorAllocatedMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 32 * 1024 * 1024));
|
||||
aggProfile.addChild(commonMetrics);
|
||||
|
||||
// Create UniqueMetrics profile with aggregation-specific metrics
|
||||
RuntimeProfile uniqueMetrics = new RuntimeProfile("UniqueMetrics");
|
||||
|
||||
// Aggregation metrics
|
||||
uniqueMetrics.addCounter("AggFuncComputeTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 80000000));
|
||||
uniqueMetrics.addCounter("ExprComputeTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 20000000));
|
||||
uniqueMetrics.addCounter("ExprReleaseTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 1000000));
|
||||
uniqueMetrics.addCounter("HashTableSize", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 10000));
|
||||
uniqueMetrics.addCounter("HashTableMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 16 * 1024 * 1024));
|
||||
|
||||
// Memory Management metrics
|
||||
uniqueMetrics.addCounter("ChunkBufferPeakMem", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 8 * 1024 * 1024));
|
||||
uniqueMetrics.addCounter("ChunkBufferPeakSize", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 100));
|
||||
uniqueMetrics.addCounter("StateAllocate", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 5000000));
|
||||
uniqueMetrics.addCounter("StateDestroy", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 2000000));
|
||||
|
||||
// Result Processing metrics
|
||||
uniqueMetrics.addCounter("GetResultsTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 10000000));
|
||||
uniqueMetrics.addCounter("ResultAggAppendTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 5000000));
|
||||
uniqueMetrics.addCounter("ResultGroupByAppendTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 3000000));
|
||||
uniqueMetrics.addCounter("ResultIteratorTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 2000000));
|
||||
|
||||
// Data Flow metrics
|
||||
uniqueMetrics.addCounter("InputRowCount", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 1000000));
|
||||
uniqueMetrics.addCounter("PassThroughRowCount", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 50000));
|
||||
uniqueMetrics.addCounter("StreamingTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 15000000));
|
||||
uniqueMetrics.addCounter("RowsReturned", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 100000));
|
||||
|
||||
aggProfile.addChild(uniqueMetrics);
|
||||
|
||||
// Create pipeline profile structure
|
||||
RuntimeProfile pipelineProfile = new RuntimeProfile("Pipeline 1");
|
||||
pipelineProfile.addChild(aggProfile);
|
||||
mockProfile.getChild("Execution").getChild("Fragment 0").addChild(pipelineProfile);
|
||||
|
||||
// Create mock plan with AggregationNode
|
||||
ProfilingExecPlan aggPlan = new ProfilingExecPlan();
|
||||
Field level = ProfilingExecPlan.class.getDeclaredField("profileLevel");
|
||||
level.setAccessible(true);
|
||||
level.setInt(aggPlan, 1);
|
||||
|
||||
Field fragmentsField = ProfilingExecPlan.class.getDeclaredField("fragments");
|
||||
fragmentsField.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<ProfilingExecPlan.ProfilingFragment> fragments =
|
||||
(List<ProfilingExecPlan.ProfilingFragment>) fragmentsField.get(aggPlan);
|
||||
ProfilingExecPlan.ProfilingElement root =
|
||||
new ProfilingExecPlan.ProfilingElement(1, AggregationNode.class);
|
||||
ProfilingExecPlan.ProfilingElement sink =
|
||||
new ProfilingExecPlan.ProfilingElement(-1, ResultSink.class);
|
||||
fragments.add(new ProfilingExecPlan.ProfilingFragment(sink, root));
|
||||
|
||||
String result = ExplainAnalyzer.analyze(aggPlan, mockProfile, List.of(1), false);
|
||||
|
||||
assertTrue(result.contains("Aggregation:"), result);
|
||||
assertTrue(result.contains("Memory Management:"), result);
|
||||
assertTrue(result.contains("Result Processing:"), result);
|
||||
assertTrue(result.contains("Data Flow:"), result);
|
||||
assertTrue(result.contains("AggFuncComputeTime: "), result);
|
||||
assertTrue(result.contains("HashTableSize: "), result);
|
||||
assertTrue(result.contains("InputRowCount: "), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinOperator() throws NoSuchFieldException, IllegalAccessException {
|
||||
// Create mock profile for join operator
|
||||
RuntimeProfile joinProfile = new RuntimeProfile("HASH_JOIN (plan_node_id=2)");
|
||||
joinProfile.addCounter("TotalTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 200000000));
|
||||
joinProfile.addCounter("CPUTime", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.TIME_NS, null, 180000000));
|
||||
joinProfile.addCounter("OutputRows", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 500000));
|
||||
|
||||
// Create CommonMetrics profile
|
||||
RuntimeProfile commonMetrics = new RuntimeProfile("CommonMetrics");
|
||||
commonMetrics.addCounter("OperatorTotalTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 200000000));
|
||||
commonMetrics.addCounter("PullRowNum", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 2000000));
|
||||
commonMetrics.addCounter("OperatorPeakMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 128 * 1024 * 1024));
|
||||
commonMetrics.addCounter("OperatorAllocatedMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 64 * 1024 * 1024));
|
||||
joinProfile.addChild(commonMetrics);
|
||||
|
||||
// Create UniqueMetrics profile with join-specific metrics
|
||||
RuntimeProfile uniqueMetrics = new RuntimeProfile("UniqueMetrics");
|
||||
|
||||
// HashTable metrics
|
||||
uniqueMetrics.addCounter("BuildBuckets", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 100000));
|
||||
uniqueMetrics.addCounter("BuildKeysPerBucket%", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 85));
|
||||
uniqueMetrics.addCounter("BuildHashTableTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 50000000));
|
||||
uniqueMetrics.addCounter("BuildConjunctEvaluateTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 10000000));
|
||||
uniqueMetrics.addCounter("HashTableMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 32 * 1024 * 1024));
|
||||
uniqueMetrics.addCounter("PartitionNums", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 4));
|
||||
uniqueMetrics.addCounter("PartitionProbeOverhead", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 2000000));
|
||||
|
||||
// ProbeSide metrics
|
||||
uniqueMetrics.addCounter("SearchHashTableTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 80000000));
|
||||
uniqueMetrics.addCounter("probeCount", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 2000000));
|
||||
uniqueMetrics.addCounter("ProbeConjunctEvaluateTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 15000000));
|
||||
uniqueMetrics.addCounter("CopyRightTableChunkTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 10000000));
|
||||
uniqueMetrics.addCounter("OtherJoinConjunctEvaluateTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 5000000));
|
||||
uniqueMetrics.addCounter("OutputBuildColumnTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 8000000));
|
||||
uniqueMetrics.addCounter("OutputProbeColumnTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 12000000));
|
||||
uniqueMetrics.addCounter("WhereConjunctEvaluateTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 3000000));
|
||||
|
||||
// RuntimeFilter metrics
|
||||
uniqueMetrics.addCounter("RuntimeFilterBuildTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 5000000));
|
||||
uniqueMetrics.addCounter("RuntimeFilterNum", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 2));
|
||||
uniqueMetrics.addCounter("PartialRuntimeMembershipFilterBytes", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 1024 * 1024));
|
||||
|
||||
joinProfile.addChild(uniqueMetrics);
|
||||
|
||||
// Create pipeline profile structure
|
||||
RuntimeProfile pipelineProfile = new RuntimeProfile("Pipeline 2");
|
||||
pipelineProfile.addChild(joinProfile);
|
||||
mockProfile.getChild("Execution").getChild("Fragment 0").addChild(pipelineProfile);
|
||||
|
||||
// Create mock plan with JoinNode
|
||||
ProfilingExecPlan joinPlan = new ProfilingExecPlan();
|
||||
Field level = ProfilingExecPlan.class.getDeclaredField("profileLevel");
|
||||
level.setAccessible(true);
|
||||
level.setInt(joinPlan, 1);
|
||||
|
||||
Field fragmentsField = ProfilingExecPlan.class.getDeclaredField("fragments");
|
||||
fragmentsField.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<ProfilingExecPlan.ProfilingFragment> fragments =
|
||||
(List<ProfilingExecPlan.ProfilingFragment>) fragmentsField.get(joinPlan);
|
||||
ProfilingExecPlan.ProfilingElement root =
|
||||
new ProfilingExecPlan.ProfilingElement(2, JoinNode.class);
|
||||
ProfilingExecPlan.ProfilingElement sink =
|
||||
new ProfilingExecPlan.ProfilingElement(-1, ResultSink.class);
|
||||
fragments.add(new ProfilingExecPlan.ProfilingFragment(sink, root));
|
||||
|
||||
String result = ExplainAnalyzer.analyze(joinPlan, mockProfile, List.of(2), false);
|
||||
|
||||
assertTrue(result.contains("HashTable:"), result);
|
||||
assertTrue(result.contains("ProbeSide:"), result);
|
||||
assertTrue(result.contains("RuntimeFilter:"), result);
|
||||
assertTrue(result.contains("BuildBuckets: "), result);
|
||||
assertTrue(result.contains("SearchHashTableTime: "), result);
|
||||
assertTrue(result.contains("RuntimeFilterNum: "), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortOperator() throws NoSuchFieldException, IllegalAccessException {
|
||||
// Create mock profile for sort operator
|
||||
RuntimeProfile sortProfile = new RuntimeProfile("SORT (plan_node_id=3)");
|
||||
sortProfile.addCounter("TotalTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 120000000));
|
||||
sortProfile.addCounter("CPUTime", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.TIME_NS, null, 100000000));
|
||||
sortProfile.addCounter("OutputRows", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 200000));
|
||||
|
||||
// Create CommonMetrics profile
|
||||
RuntimeProfile commonMetrics = new RuntimeProfile("CommonMetrics");
|
||||
commonMetrics.addCounter("OperatorTotalTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 120000000));
|
||||
commonMetrics.addCounter("PullRowNum", RuntimeProfile.ROOT_COUNTER, new Counter(TUnit.UNIT, null, 2000000));
|
||||
commonMetrics.addCounter("OperatorPeakMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 256 * 1024 * 1024));
|
||||
commonMetrics.addCounter("OperatorAllocatedMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 128 * 1024 * 1024));
|
||||
sortProfile.addChild(commonMetrics);
|
||||
|
||||
// Create UniqueMetrics profile with sort-specific metrics
|
||||
RuntimeProfile uniqueMetrics = new RuntimeProfile("UniqueMetrics");
|
||||
|
||||
// Sort-specific metrics
|
||||
uniqueMetrics.addCounter("SortTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 80000000));
|
||||
uniqueMetrics.addCounter("SortKeys", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 2));
|
||||
uniqueMetrics.addCounter("SortRows", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 2000000));
|
||||
uniqueMetrics.addCounter("SortMemoryUsage", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 64 * 1024 * 1024));
|
||||
uniqueMetrics.addCounter("SortSpillBytes", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.BYTES, null, 32 * 1024 * 1024));
|
||||
uniqueMetrics.addCounter("SortSpillTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 10000000));
|
||||
uniqueMetrics.addCounter("SortMergeTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 15000000));
|
||||
uniqueMetrics.addCounter("SortPartitionTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 5000000));
|
||||
uniqueMetrics.addCounter("SortChunkSize", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 4096));
|
||||
uniqueMetrics.addCounter("SortChunkCount", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 500));
|
||||
uniqueMetrics.addCounter("SortCompareTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 20000000));
|
||||
uniqueMetrics.addCounter("SortCopyTime", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.TIME_NS, null, 10000000));
|
||||
uniqueMetrics.addCounter("SortLimit", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 100000));
|
||||
uniqueMetrics.addCounter("SortOffset", RuntimeProfile.ROOT_COUNTER,
|
||||
new Counter(TUnit.UNIT, null, 0));
|
||||
|
||||
sortProfile.addChild(uniqueMetrics);
|
||||
|
||||
// Create pipeline profile structure
|
||||
RuntimeProfile pipelineProfile = new RuntimeProfile("Pipeline 3");
|
||||
pipelineProfile.addChild(sortProfile);
|
||||
mockProfile.getChild("Execution").getChild("Fragment 0").addChild(pipelineProfile);
|
||||
|
||||
// Create mock plan with SortNode
|
||||
ProfilingExecPlan sortPlan = new ProfilingExecPlan();
|
||||
Field level = ProfilingExecPlan.class.getDeclaredField("profileLevel");
|
||||
level.setAccessible(true);
|
||||
level.setInt(sortPlan, 1);
|
||||
|
||||
Field fragmentsField = ProfilingExecPlan.class.getDeclaredField("fragments");
|
||||
fragmentsField.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<ProfilingExecPlan.ProfilingFragment> fragments =
|
||||
(List<ProfilingExecPlan.ProfilingFragment>) fragmentsField.get(sortPlan);
|
||||
ProfilingExecPlan.ProfilingElement root =
|
||||
new ProfilingExecPlan.ProfilingElement(3, SortNode.class);
|
||||
ProfilingExecPlan.ProfilingElement sink =
|
||||
new ProfilingExecPlan.ProfilingElement(-1, ResultSink.class);
|
||||
fragments.add(new ProfilingExecPlan.ProfilingFragment(sink, root));
|
||||
|
||||
String result = ExplainAnalyzer.analyze(sortPlan, mockProfile, List.of(3), false);
|
||||
|
||||
assertTrue(result.contains("Others:"), result);
|
||||
assertTrue(result.contains("SortTime: "), result);
|
||||
assertTrue(result.contains("SortKeys: "), result);
|
||||
assertTrue(result.contains("SortRows: "), result);
|
||||
assertTrue(result.contains("SortMemoryUsage: "), result);
|
||||
assertTrue(result.contains("SortSpillBytes: "), result);
|
||||
assertTrue(result.contains("SortMergeTime: "), result);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue