@@ -77,7 +77,9 @@ import type {
7777 StackAddressInfo ,
7878 AddressTimings ,
7979 Address ,
80+ IndexIntoAddressSetTable ,
8081} from 'firefox-profiler/types' ;
82+ import { IntSetTableBuilder } from 'firefox-profiler/utils/intset-table' ;
8183
8284import { getMatchingAncestorStackForInvertedCallNode } from './profile-data' ;
8385import type { CallNodeInfo , CallNodeInfoInverted } from './call-node-info' ;
@@ -135,48 +137,32 @@ export function getStackAddressInfo(
135137 _funcTable : FuncTable ,
136138 nativeSymbol : IndexIntoNativeSymbolTable
137139) : StackAddressInfo {
138- // "self address" == "the address which a stack's self time is contributed to"
139- const selfAddressForAllStacks = [ ] ;
140- // "total addresses" == "the set of addresses whose total time this stack contributes to"
141- const totalAddressesForAllStacks : Array < Set < Address > | null > = [ ] ;
140+ const builder = new IntSetTableBuilder ( ) ;
141+ const stackIndexToAddressSetIndex = new Int32Array ( stackTable . length ) ;
142142
143- // This loop takes advantage of the fact that the stack table is topologically ordered:
144- // Prefix stacks are always visited before their descendants.
145- // Each stack inherits the "total" addresses from its parent stack, and then adds its
146- // self address to that set. If the stack doesn't have a self address in the library, we just
147- // re-use the prefix's set object without copying it.
148143 for ( let stackIndex = 0 ; stackIndex < stackTable . length ; stackIndex ++ ) {
149- const frame = stackTable . frame [ stackIndex ] ;
150144 const prefixStack = stackTable . prefix [ stackIndex ] ;
151- const nativeSymbolOfThisStack = frameTable . nativeSymbol [ frame ] ;
145+ const prefixAddressSet : IndexIntoAddressSetTable | - 1 =
146+ prefixStack !== null ? stackIndexToAddressSetIndex [ prefixStack ] : - 1 ;
152147
153- let selfAddress : Address | null = null ;
154- let totalAddresses : Set < Address > | null =
155- prefixStack !== null ? totalAddressesForAllStacks [ prefixStack ] : null ;
148+ const frame = stackTable . frame [ stackIndex ] ;
149+ const nativeSymbolOfThisStack = frameTable . nativeSymbol [ frame ] ;
150+ const matchesNativeSymbol = nativeSymbolOfThisStack === nativeSymbol ;
151+ if ( prefixAddressSet === - 1 && ! matchesNativeSymbol ) {
152+ stackIndexToAddressSetIndex [ stackIndex ] = - 1 ;
153+ } else {
154+ const selfAddress = matchesNativeSymbol ? frameTable . address [ frame ] : - 1 ;
156155
157- if ( nativeSymbolOfThisStack === nativeSymbol ) {
158- selfAddress = frameTable . address [ frame ] ;
159- if ( selfAddress !== - 1 ) {
160- // Add this stack's address to this stack's totalAddresses. The rest of this stack's
161- // totalAddresses is the same as for the parent stack.
162- // We avoid creating new Set objects unless the new set is actually
163- // different.
164- if ( totalAddresses === null ) {
165- // None of the ancestor stack nodes have hit a address in the given library.
166- totalAddresses = new Set ( [ selfAddress ] ) ;
167- } else if ( ! totalAddresses . has ( selfAddress ) ) {
168- totalAddresses = new Set ( totalAddresses ) ;
169- totalAddresses . add ( selfAddress ) ;
170- }
171- }
156+ stackIndexToAddressSetIndex [ stackIndex ] =
157+ builder . indexForSetWithParentAndSelf (
158+ prefixAddressSet !== - 1 ? prefixAddressSet : null ,
159+ selfAddress
160+ ) ;
172161 }
173-
174- selfAddressForAllStacks . push ( selfAddress ) ;
175- totalAddressesForAllStacks . push ( totalAddresses ) ;
176162 }
177163 return {
178- selfAddress : selfAddressForAllStacks ,
179- stackAddresses : totalAddressesForAllStacks ,
164+ stackIndexToAddressSetIndex ,
165+ addressSetTable : builder . finish ( ) ,
180166 } ;
181167}
182168
@@ -502,30 +488,58 @@ export function getAddressTimings(
502488 if ( stackAddressInfo === null ) {
503489 return emptyAddressTimings ;
504490 }
505- const { selfAddress, stackAddresses } = stackAddressInfo ;
506- const totalAddressHits : Map < Address , number > = new Map ( ) ;
507- const selfAddressHits : Map < Address , number > = new Map ( ) ;
508491
509- // Iterate over all the samples, and aggregate the sample's weight into the
510- // addresses which are hit by the sample's stack.
511- // TODO: Maybe aggregate sample count per stack first, and then visit each stack only once?
492+ const { stackIndexToAddressSetIndex, addressSetTable } = stackAddressInfo ;
493+ // Iterate over all the samples, and aggregate the sample's weight into
494+ // selfPerAddressSet.
495+ const selfPerAddressSet = new Float64Array ( addressSetTable . length ) ;
512496 for ( let sampleIndex = 0 ; sampleIndex < samples . length ; sampleIndex ++ ) {
513497 const stackIndex = samples . stack [ sampleIndex ] ;
514498 if ( stackIndex === null ) {
515499 continue ;
516500 }
517- const weight = samples . weight ? samples . weight [ sampleIndex ] : 1 ;
518- const setOfHitAddresses = stackAddresses [ stackIndex ] ;
519- if ( setOfHitAddresses !== null ) {
520- for ( const address of setOfHitAddresses ) {
521- const oldHitCount = totalAddressHits . get ( address ) ?? 0 ;
522- totalAddressHits . set ( address , oldHitCount + weight ) ;
501+ const addressSetIndex = stackIndexToAddressSetIndex [ stackIndex ] ;
502+ if ( addressSetIndex !== - 1 ) {
503+ const weight = samples . weight ? samples . weight [ sampleIndex ] : 1 ;
504+ selfPerAddressSet [ addressSetIndex ] += weight ;
505+ }
506+ }
507+
508+ const totalAddressHits : Map < Address , number > = new Map ( ) ;
509+ const selfAddressHits : Map < Address , number > = new Map ( ) ;
510+ const selfSumOfAddressSetDescendants = new Float64Array (
511+ addressSetTable . length
512+ ) ;
513+
514+ for (
515+ let addressSetIndex = addressSetTable . length - 1 ;
516+ addressSetIndex >= 0 ;
517+ addressSetIndex --
518+ ) {
519+ const selfWeight = selfPerAddressSet [ addressSetIndex ] ;
520+ if ( selfWeight !== 0 ) {
521+ const selfAddress = addressSetTable . self [ addressSetIndex ] ;
522+ if ( selfAddress !== - 1 ) {
523+ const oldHitCount = selfAddressHits . get ( selfAddress ) ?? 0 ;
524+ selfAddressHits . set ( selfAddress , oldHitCount + selfWeight ) ;
523525 }
524526 }
525- const address = selfAddress [ stackIndex ] ;
526- if ( address !== null ) {
527- const oldHitCount = selfAddressHits . get ( address ) ?? 0 ;
528- selfAddressHits . set ( address , oldHitCount + weight ) ;
527+
528+ const selfSumOfThisAddressSetDescendants =
529+ selfSumOfAddressSetDescendants [ addressSetIndex ] ;
530+ const thisAddressSetWeight =
531+ selfWeight + selfSumOfThisAddressSetDescendants ;
532+ const AddressSetPrefix = addressSetTable . prefix [ addressSetIndex ] ;
533+ if ( AddressSetPrefix !== null ) {
534+ selfSumOfAddressSetDescendants [ AddressSetPrefix ] += thisAddressSetWeight ;
535+ }
536+
537+ if ( thisAddressSetWeight !== 0 ) {
538+ const address = addressSetTable . value [ addressSetIndex ] ;
539+ if ( address !== - 1 ) {
540+ const oldHitCount = totalAddressHits . get ( address ) ?? 0 ;
541+ totalAddressHits . set ( address , oldHitCount + thisAddressSetWeight ) ;
542+ }
529543 }
530544 }
531545 return { totalAddressHits, selfAddressHits } ;
0 commit comments