Skip to content

Commit e8bf7e3

Browse files
committed
Associate rows with RA-hash and a name-with-pipeline hash
1 parent c724577 commit e8bf7e3

File tree

1 file changed

+49
-17
lines changed

1 file changed

+49
-17
lines changed

extensions/ql-vscode/src/view/compare-performance/ComparePerformance.tsx

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ function isPresent<T>(x: Optional<T>): x is T {
3535
}
3636

3737
interface PredicateInfo {
38+
name: string;
39+
raHash: string;
3840
tuples: number;
3941
evaluationCount: number;
4042
iterationCount: number;
@@ -43,23 +45,37 @@ interface PredicateInfo {
4345
}
4446

4547
class ComparisonDataset {
48+
/**
49+
* Predicates indexed by a key consisting of the name and its pipeline hash.
50+
* Unlike the RA hash, the pipeline hash only depends on the predicate's own pipeline.
51+
*/
52+
public keyToIndex = new Map<string, number>();
53+
public raToIndex = new Map<string, number>();
4654
public nameToIndex = new Map<string, number>();
4755
public cacheHitIndices: Set<number>;
4856
public sentinelEmptyIndices: Set<number>;
4957

50-
constructor(public data: PerformanceComparisonDataFromLog) {
51-
const { names } = data;
52-
const { nameToIndex } = this;
58+
constructor(private data: PerformanceComparisonDataFromLog) {
59+
const { names, raHashes, pipelineSummaryList } = data;
60+
const { keyToIndex, raToIndex, nameToIndex } = this;
5361
for (let i = 0; i < names.length; i++) {
54-
nameToIndex.set(names[i], i);
62+
const name = names[i];
63+
const pipelineHash = getPipelineSummaryHash(pipelineSummaryList[i]);
64+
keyToIndex.set(`${name}@${pipelineHash}`, i);
65+
nameToIndex.set(name, i);
66+
raToIndex.set(raHashes[i], i);
5567
}
5668
this.cacheHitIndices = new Set(data.cacheHitIndices);
5769
this.sentinelEmptyIndices = new Set(data.sentinelEmptyIndices);
5870
}
5971

60-
getTupleCountInfo(name: string): Optional<PredicateInfo> {
61-
const { data, nameToIndex, cacheHitIndices, sentinelEmptyIndices } = this;
62-
const index = nameToIndex.get(name);
72+
keys() {
73+
return Array.from(this.keyToIndex.keys());
74+
}
75+
76+
getTupleCountInfo(key: string): Optional<PredicateInfo> {
77+
const { data, keyToIndex, cacheHitIndices, sentinelEmptyIndices } = this;
78+
const index = keyToIndex.get(key);
6379
if (index == null) {
6480
return AbsentReason.NotSeen;
6581
}
@@ -72,6 +88,8 @@ class ComparisonDataset {
7288
}
7389
}
7490
return {
91+
name: data.names[index],
92+
raHash: data.raHashes[index],
7593
evaluationCount: data.evaluationCounts[index],
7694
iterationCount: data.iterationCounts[index],
7795
timeCost: data.timeCosts[index],
@@ -336,6 +354,7 @@ function HighLevelStats(props: HighLevelStatsProps) {
336354
}
337355

338356
interface Row {
357+
key: string;
339358
name: string;
340359
before: Optional<PredicateInfo>;
341360
after: Optional<PredicateInfo>;
@@ -480,19 +499,16 @@ function ComparePerformanceWithData(props: {
480499

481500
const [isPerEvaluation, setPerEvaluation] = useState(false);
482501

483-
const nameSet = useMemo(
484-
() => union(from.data.names, to.data.names),
485-
[from, to],
486-
);
502+
const keySet = useMemo(() => union(from.keys(), to.keys()), [from, to]);
487503

488504
const hasCacheHitMismatch = useRef(false);
489505

490506
const rows: Row[] = useMemo(() => {
491507
hasCacheHitMismatch.current = false;
492-
return Array.from(nameSet)
493-
.map((name) => {
494-
const before = from.getTupleCountInfo(name);
495-
const after = to.getTupleCountInfo(name);
508+
return Array.from(keySet)
509+
.map((key) => {
510+
const before = from.getTupleCountInfo(key);
511+
const after = to.getTupleCountInfo(key);
496512
const beforeValue = metricGetOptional(metric, before, isPerEvaluation);
497513
const afterValue = metricGetOptional(metric, after, isPerEvaluation);
498514
if (beforeValue === afterValue) {
@@ -510,11 +526,16 @@ function ComparePerformanceWithData(props: {
510526
const diff =
511527
(isPresent(afterValue) ? afterValue : 0) -
512528
(isPresent(beforeValue) ? beforeValue : 0);
513-
return { name, before, after, diff } satisfies Row;
529+
const name = isPresent(before)
530+
? before.name
531+
: isPresent(after)
532+
? after.name
533+
: key;
534+
return { key, name, before, after, diff } satisfies Row;
514535
})
515536
.filter((x) => !!x)
516537
.sort(getSortOrder(sortOrder));
517-
}, [nameSet, from, to, metric, hideCacheHits, sortOrder, isPerEvaluation]);
538+
}, [keySet, from, to, metric, hideCacheHits, sortOrder, isPerEvaluation]);
518539

519540
const { totalBefore, totalAfter, totalDiff } = useMemo(() => {
520541
let totalBefore = 0;
@@ -860,3 +881,14 @@ function collatePipelines(
860881
function samePipeline(a: string[], b: string[]) {
861882
return a.length === b.length && a.every((x, i) => x === b[i]);
862883
}
884+
885+
function getPipelineSummaryHash(pipelines: Record<string, PipelineSummary>) {
886+
// Note: we can't import "crypto" here because it is not available in the browser,
887+
// so we just concatenate the hashes of the individual pipelines.
888+
const keys = Object.keys(pipelines).sort();
889+
let result = "";
890+
for (const key of keys) {
891+
result += `${pipelines[key].hash};`;
892+
}
893+
return result;
894+
}

0 commit comments

Comments
 (0)