Reduce time profiler memory usage with lazy profile tree#287
Reduce time profiler memory usage with lazy profile tree#287IlyasShabi merged 9 commits intomainfrom
Conversation
Overall package sizeSelf size: 1.98 MB Dependency sizes| name | version | self size | total size | |------|---------|-----------|------------| | source-map | 0.7.6 | 185.63 kB | 185.63 kB | | pprof-format | 2.2.1 | 163.06 kB | 163.06 kB | | node-gyp-build | 3.9.0 | 8.81 kB | 8.81 kB |🤖 This report was automatically generated by heaviest-objects-in-the-universe |
216d01f to
0d480d5
Compare
BenchmarksBenchmark execution time: 2026-03-10 13:04:35 Comparing candidate commit 5434977 in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 91 metrics, 29 unstable metrics. |
9881a82 to
0e5f1dc
Compare
7ee93d7 to
b617efb
Compare
b617efb to
c4d4158
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3a567e03d1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
szegedi
left a comment
There was a problem hiding this comment.
Overall, great work. I'm sorry you had to go through all that trouble for supporting line-info. I have two refactoring suggestions to reduce code duplication between two modes of stopping.
| if (restart) { | ||
| gV8ProfilerStuckEventLoopDetected = | ||
| gProfiler.v8ProfilerStuckEventLoopDetected(); | ||
| if (gV8ProfilerStuckEventLoopDetected > 0) { | ||
| gProfiler.stop(false); | ||
| gProfiler.start(); | ||
| } | ||
| } else { | ||
| gV8ProfilerStuckEventLoopDetected = 0; | ||
| gProfiler.dispose(); | ||
| gProfiler = undefined; | ||
| gSourceMapper = undefined; | ||
| if (gStore !== undefined) { | ||
| gStore.disable(); | ||
| gStore = undefined; | ||
| } | ||
| } |
There was a problem hiding this comment.
You could extract these into two module-private functions (the way they are in the original stop method) and call these functions instead, to reduce some code duplication.
bindings/profilers/wall.cc
Outdated
| Result WallProfiler::StopAndCollectImpl(bool restart, | ||
| v8::Local<v8::Function> callback, | ||
| v8::Local<v8::Value>& result) { |
There was a problem hiding this comment.
This method is very similar to ::Stop. I had Claude extract the common code from the two to see if it can be done; I put it up as a draft PR here. Feel free to incorporate the commit here, I just put the PR up for the ease of review.
|
@szegedi Thanks for your suggestions! I actually preferred not to use shared helper functions for the duplicated code because the upcoming PR will remove the old stop function, but I’m also fine with this approach. |
What does this PR do?:
Introduces a new
stopAndCollectfunction for the time profiler that reduces memory usage during profile serialization. Instead of materializing the entire V8 CPU profile tree as JS objects upfront, the new API resolves children on demand using getters.This also moves
totalHitCount()computation to C++ instead of TS to avoid getting children twice from C++Motivation:
The current
stop()API recursively translates the entire profile tree into JS objects, creating a full copy in heap memory which can be significant for large applications.The new
stopAndCollect()API uses a callback to keep the V8 profile alive while the TS serializer traverses the tree lazily.Scalar properties are set at creation time, but children are resolved on first access, reducing peak memory usage.
Additional Notes:
How to test the change?:
Unit tests