Skip to content

perf(runner-shared): decode memtrack frames for module events in parallel - #578

Merged
not-matthias merged 3 commits into
mainfrom
cod-3819-parallel-module-event-scan
Oct 12, 2026
Merged

not-matthias merged 3 commits into
mainfrom
cod-3819-parallel-module-event-scan

Conversation

@not-matthias

@not-matthias not-matthias commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

TLDR: Finding the module events decoded the whole memtrack artifact on one thread through a streaming deserializer. Every frame is a self-contained zstd frame, so frames are now decoded in parallel from an in-memory buffer (stacked on #576).

  • decode_module_events splits the artifact into its zstd frames and decodes them on the rayon pool. Each worker decompresses a frame into a reused buffer and deserializes MemtrackEvent from that slice, so values are read in place instead of copied out of a stream first. Results keep artifact order.
  • The runner maps the artifact file instead of streaming it.
  • The Mapping/Fork/Exec filter moves to MemtrackEventKind::is_module_event.
  • A new test checks the decoder against a filtered full decode over every event kind, several frames and a truncated last frame.
  • The bench gets back its 10M and 100M sizes, now fast enough to run. The 100M artifact with stacks is about 4.65 GB on disk.

memtrack_reader bench, local walltime on a 32-thread machine:

Events without stacks with stacks
1M 258 → 15.4 ms 267 → 17.1 ms
10M 2.59 s → 101 ms 2.73 s → 114 ms
100M 26.0 s → 1.00 s 27.1 s → 1.05 s

Review notes

  • decode_module_events now takes &[u8] and returns a Vec.
  • A truncated last frame is still streamed, so the events before the cut are found as before. A frame in the middle that fails to decompress now returns an error; the streamed decoder stopped there silently.
  • CodSpeed simulation runs threads one at a time, so it only shows the single-threaded gain from decoding a buffer instead of a stream. The 10M and 100M cases will still take several minutes there, mostly generating the artifact.

@codspeed

codspeed Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Merging this PR will improve performance by 29.44%

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

⚠️ 8 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

ℹ️ 6 benchmarks have no flame graph

The flamegraph could not be generated because we failed to resolve the symbols in the profiling trace. This happens when we fail to harvest information about language frames.

⚡ 2 improved benchmarks
❌ 2 (👁 2) regressed benchmarks
✅ 33 untouched benchmarks
🆕 4 new benchmarks
⏩ 6 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
⚡ ℹ️ WallTime find_module_events[1000000] 1,269.3 ms 186.8 ms ×6.8
⚡ ℹ️ WallTime find_module_events_with_stacks[1000000] 1,305.6 ms 194.4 ms ×6.7
🆕 ℹ️ WallTime find_module_events_with_stacks[10000000] N/A 1.1 s N/A
🆕 ℹ️ WallTime find_module_events_with_stacks[100000000] N/A 10.9 s N/A
🆕 ℹ️ WallTime find_module_events[10000000] N/A 1 s N/A
🆕 ℹ️ WallTime find_module_events[100000000] N/A 10.4 s N/A
👁 Memory find_module_events_with_stacks[1000000] 1.1 MB 4.6 MB -75.18%
👁 Memory find_module_events[1000000] 1.1 MB 4.4 MB -75.2%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing cod-3819-parallel-module-event-scan (5ff86d7) with cod-3819-memtrack-investigate-8-minutes-spent-in-teardown (2f1f983)

Open in CodSpeed

Footnotes

  1. 6 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@not-matthias
not-matthias marked this pull request as ready for review October 9, 2026 12:58
@not-matthias
not-matthias force-pushed the cod-3819-parallel-module-event-scan branch from b4b751a to caf6e0d Compare October 9, 2026 12:58
@greptile-apps

greptile-apps Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no blocking issue was found.

Summary

The PR finds module events by decoding separate zstd frames in parallel. It keeps artifact order, streams a shortened final frame, maps artifact files, and restores larger walltime benchmarks.

  • Both callers use the new slice input and returned Vec.
  • The new test compares complete and shortened artifacts with a filtered streamed decode.
  • No new actionable issues were found.
  • The previous RAM thread was manually resolved by not-matthias without an explanatory reply. The current code streams each frame instead of expanding the whole frame into a buffer.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Map artifact file] --> B[Split complete zstd frames]
  B --> C[Stream complete frames in parallel]
  C --> D[Keep Mapping, Fork, and Exec events]
  D --> E[Combine results in frame order]
  B --> F[Stream remaining tail]
  F --> G[Append module events before the cut]
  E --> G
  G --> H[Rebuild process mappings]
Loading

Reviews (2) · Last reviewed commit: "perf(runner-shared): stream each memtrac..." · Reviewed by Greptile

Comment thread crates/runner-shared/src/artifacts/memtrack/mod.rs Outdated
@not-matthias
not-matthias force-pushed the cod-3819-parallel-module-event-scan branch 2 times, most recently from 8a11ab9 to e66e598 Compare October 9, 2026 14:15
@not-matthias
not-matthias force-pushed the cod-3819-parallel-module-event-scan branch from e66e598 to f18817f Compare October 9, 2026 15:22

@GuillaumeLagrange GuillaumeLagrange left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quite neat, olgtm

Comment thread crates/runner-shared/benches/memtrack_reader.rs Outdated
Comment thread crates/runner-shared/src/artifacts/memtrack/mod.rs Outdated
…llel

Finding the Mapping, Fork and Exec events decoded the whole artifact
through one streaming deserializer on one thread. The streaming reader
copies every key, string and stack payload into a fresh allocation before
serde sees it.

Every frame the encoder writes is a self-contained zstd frame, so
decode_module_events now splits the artifact into its frames and decodes
them on the rayon pool. Each worker decompresses a frame into a reused
buffer and decodes events from that slice, so values are read in place
instead of copied out of a stream. Results are concatenated in artifact
order. A truncated last frame cannot be split off and is still streamed,
keeping the events before the cut. The runner maps the artifact instead of
streaming it from the file.

The Mapping/Fork/Exec filter moves to MemtrackEventKind::is_module_event,
and a test checks the new decoder against a filtered full decode over every
event kind, several frames and a truncated last frame.
…ule event bench

With frames decoded in parallel, CI-sized artifacts are fast enough to
benchmark again: add the 10M and 100M event sizes the streamed decoder was
too slow for. The 100M artifact with stacks is about 5 GB on disk and is
mapped, not held in memory.
…vents

Decompressing a whole frame before filtering it ties a worker's memory to
the frame's decoded size, which the event count per frame does not bound:
a frame of captured stacks decodes to hundreds of MB, held by every rayon
worker at once. Stream each frame through the existing event decoder
instead, so a worker holds about one zstd window and one event. Frames are
still decoded in parallel and events keep their artifact order.

This trades speed for bounded memory: on a 32-thread machine the 100M
event search takes about 1.85 s instead of 1.0 s, still well ahead of the
single-threaded streamed decoder.
@not-matthias
not-matthias force-pushed the cod-3819-parallel-module-event-scan branch from f18817f to 5ff86d7 Compare October 12, 2026 14:44
Base automatically changed from cod-3819-memtrack-investigate-8-minutes-spent-in-teardown to main October 12, 2026 15:22
@not-matthias
not-matthias merged commit 5ff86d7 into main Oct 12, 2026
62 checks passed
@not-matthias
not-matthias deleted the cod-3819-parallel-module-event-scan branch October 12, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants