@@ -35,6 +35,8 @@ function isPresent<T>(x: Optional<T>): x is T {
3535}
3636
3737interface PredicateInfo {
38+ name : string ;
39+ raHash : string ;
3840 tuples : number ;
3941 evaluationCount : number ;
4042 iterationCount : number ;
@@ -43,23 +45,37 @@ interface PredicateInfo {
4345}
4446
4547class 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
338356interface 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(
860881function 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