Change profile.shared.stackTable.prefix to prefixOffset (delta from current index)#6089
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6089 +/- ##
==========================================
+ Coverage 83.47% 83.50% +0.03%
==========================================
Files 342 342
Lines 36097 36183 +86
Branches 10098 10133 +35
==========================================
+ Hits 30131 30216 +85
- Misses 5538 5539 +1
Partials 428 428 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
372c5dd to
b581752
Compare
2b6a379 to
c8f1a69
Compare
c8f1a69 to
74b6c2e
Compare
|
This is now ready for review. @canova, how do you feel about this? People will have to change any code that generates profiles, but I actually think the changes would be pretty straightforward and non-controversial. Also, I can fold the two versions into one if you'd prefer that, so that we don't have an intermediate version that uses |
74b6c2e to
610f1a9
Compare
|
When we compute the derived stack table, we turn |
canova
left a comment
There was a problem hiding this comment.
Thanks for the PR! I mostly added a few questions, they don't necessarily have to be changed if the existing version makes sense, but wanted to understand them first.
@canova, how do you feel about this? People will have to change any code that generates profiles, but I actually think the changes would be pretty straightforward and non-controversial.
I think prefixOffset sounds like a great idea! The size improvements looks really good!
Also, I can fold the two versions into one if you'd prefer that, so that we don't have an intermediate version that uses -1 values.
Yeah, I think I would prefer to have a single upgrader for it instead of having 2.
One thing I don't fully understand is the new normalization step. Why didn't we have that for the frame column? I wanna r+ it after I fully understand. I also added a comment below.
Also, the first commit message says: "so the processed profile version is bumped to 63 ..." but it adds 66 (we've updated a lot lately! 😄) Could you update that as well?
| function _normalizeAfterUpgrade(profile: any): void { | ||
| const stackTable = profile.shared?.stackTable ?? null; | ||
| if (stackTable && !(stackTable.prefix instanceof Int32Array)) { | ||
| stackTable.prefix = new Int32Array(stackTable.prefix); |
There was a problem hiding this comment.
Hm, why do we add this normalize step now? We had converted the frame column before but we didn't need to do it for it?
There was a problem hiding this comment.
Huh oops no I don't want this either. Thanks for catching it!
| // Mirror the normalization that unserializeProfileOfArbitraryFormat | ||
| // applies when the store loads the JSON profile, so `toEqual` matches | ||
| // the in-memory profile shape after loading. | ||
| profile.shared.stackTable.prefix = new Int32Array( | ||
| profile.shared.stackTable.prefix | ||
| ); |
There was a problem hiding this comment.
This part looks a bit fragile, especially if we are going to add more to the normalization step later. Could we maybe use unserializeProfileOfArbitraryFormat directly here?
There was a problem hiding this comment.
Huh, what the heck. No I'm just deleting this added code entirely.
| // Maps each stack to its parent stack, or -1 if it is a root. | ||
| prefix: Array<IndexIntoStackTable | -1> | Int32Array<ArrayBuffer>; | ||
|
|
||
| prefixOffset: Int32Array<ArrayBuffer>; |
There was a problem hiding this comment.
Why not Uint32Array for the prefixOffset?
There was a problem hiding this comment.
Because accessing an Int32Array is ever-so-slightly faster than accessing a Uint32Array, in Spidermonkey. see this report from Claude
There was a problem hiding this comment.
Oh that's very interesting. Thanks for the report!
Nit: It would be nice to have a code comment also mentioning that Int32Array is a conscious choice with the reason why, so we won't forget and try to refactor later.
There was a problem hiding this comment.
Ah, yes. Added this comment:
// We use Int32Array instead of Uint32Array so that Spidermonkey
// doesn't need to check for i32 overflow on element access.
prefixOffset: number[] | Int32Array<ArrayBuffer>;I also noticed that I forgot to allow plain arrays in the type; I've added number[] | now. I could make a OffsetIntoStackTableFromCurrentIndex typedef if you prefer.
610f1a9 to
f576e2f
Compare
Now we use `-1` (instead of `null`) to indicate that a stack node is a root. This only changes the derived `StackTable`; the `RawStackTable` stored in the files still uses a regular array with nulls.
3078888 to
179c6aa
Compare
Sounds good! I've squashed and re-split; the first commit now only affects the derived stack table and not the raw format. The second commit makes the format change and introduces the offset representation.
Sorry, looks like I hadn't reviewed it properly myself. I've removed the normalization.
Hah! Removed from the commit message. |
| // Maps each stack to its parent stack, or -1 if it is a root. | ||
| prefix: Array<IndexIntoStackTable | -1> | Int32Array<ArrayBuffer>; | ||
|
|
||
| prefixOffset: Int32Array<ArrayBuffer>; |
There was a problem hiding this comment.
Oh that's very interesting. Thanks for the report!
Nit: It would be nice to have a code comment also mentioning that Int32Array is a conscious choice with the reason why, so we won't forget and try to refactor later.
Replace `RawStackTable.prefix` with a `prefixOffset` column. For each stack i, `prefixOffset[i] === 0` means i is a root, and `prefixOffset[i] === k` (with k > 0) means i's parent is at index i - k. All values are non-negative because the stack table is stored in topological order. This results in large size savings after gzipping, because chains of `1,1,1,1` compress much better than chains of `123,124,125,126`. It's common to have these "immediate precedent is the prefix" chains because of how the stack table is usually created: You have a list of frames and you want to make a stack node for it. You find an existing stack node for a prefix that already exists, and then you create the other stack nodes for the remainder. Those new stack nodes form such a chain. The `prefixOffset` change is only made for the `RawStackTable`, i.e. for what we store in the file. The derived `StackTable` still uses `prefix`, with `-1` indicating roots.
179c6aa to
2880a96
Compare
Another new version on top of #6087:
i - prefix[i]offsets for non-roots)Here's how this change impacts profile sizes and loading times on this profile: https://storage.googleapis.com/profiler-get-symbols-fixtures/large-speedometer3-profile.json.gz (with this corresponding size profile)
125 MB -> 89 MB on the compressed size! That's a 29% reduction!