Add cross-language MCAP performance benchmarks. - #1611
Conversation
|
And I'm noticing now that there is cpp/bench, rust/benches, and typescript/benchmarks . However, I believe that the benchmarks I've done here are more useful and comprehensive, and allow us to do comparisons across languages. If reviewers agree, I'll remove the ones in the per-language directories in favor of these top-level ones. |
bdd9347 to
81f3300
Compare
79b4c3d to
501ed6d
Compare
501ed6d to
8cb3b8b
Compare
There was a problem hiding this comment.
All prior threads are now resolved. The fixes for TS /tf cycling, Go streaming reads, TS peak RSS, Go per-channel sequence counters, shell deduplication, and .gitignore all look good.
Two minor suggestions inline (duplicate TS import, temp file cleanup). Nothing blocking.
One open question: have you run the full make bench end-to-end on a clean checkout? The README says ~20-25 minutes — curious if that holds and whether the summary tables render cleanly across all 5 languages.
| clean: | ||
| rm -f cpp_bench/bench_write cpp_bench/bench_read | ||
| cd rust_bench && cargo clean | ||
| rm -f go_bench/bench_write go_bench/bench_read |
There was a problem hiding this comment.
suggestion: make clean removes build artifacts but not the benchmark output files in $BENCH_DIR (defaults to /tmp). A full run writes ~40 MCAP files (5 langs × 4 modes × {fixed, mixed}) — several GB total with the mixed-payload scenario. Consider adding a clean-bench target or a note in the README about manual cleanup, so users don't silently accumulate multi-GB temp files.
clean-bench:
rm -f $(BENCH_DIR)/bench_*.mcap $(BENCH_DIR)/bench_*_results_*.tsv(Or just document it — either way.)
8cb3b8b to
9953f51
Compare
9953f51 to
ed1e0c5
Compare
There was a problem hiding this comment.
Spot-checked the prior round of fixes: per-channel chanSeq is in place across TS/Go/C++/Rust/Python, Go read now uses os.Open+mcap.NewReader, TS RSS now goes through process.resourceUsage().maxRSS, set_lang_cmds extracted, .gitignore added, and TSV filenames already include _${FILL} so the two make bench runs don't clobber each other. 👍
A few new things to look at, in rough order of importance:
rust_bench/Cargo.toml'spath = "../../rust"resolves to the workspace virtual manifest rather than themcappackage — works in some Cargo versions, fragile in others. Worth pointing at../../rust/mcapexplicitly.ru_maxrssunits differ between Linux (KB) and macOS (bytes); the memory table converts unconditionally, so Mac numbers will be silently ~1024× off.- One existing thread is still unaddressed:
make cleandoesn't touch the ~GB ofbench_*.mcapfiles left in$BENCH_DIRafter a full run. Aclean-benchtarget or a README note would close it out.
Smaller items (default case in set_lang_cmds, Python timing including file-close, README "@mcap/core" wording) inline.
Open question: have you actually run make bench end-to-end on a fresh clone with the current rust_bench/Cargo.toml? If yes, that resolves the workspace-path concern.
| rust_bench/ Rust benchmarks (mcap crate) | ||
| go_bench/ Go benchmarks (mcap module) | ||
| python_bench/ Python benchmarks (mcap package) | ||
| typescript_bench/ TypeScript benchmarks (@mcap/core) |
There was a problem hiding this comment.
nit: the TypeScript bench imports ../../typescript/core/src/index.ts (and the nodejs/support packages) directly from source, not from the published @mcap/core. That's fine — it's actually what you want for an in-repo benchmark — but (@mcap/core) here implies the published package. A small clarification like TypeScript benchmarks (in-repo @mcap/core source) would prevent confusion.
b285101 to
2fca3aa
Compare
2fca3aa to
25582e0
Compare
25582e0 to
139b738
Compare
139b738 to
ebf0388
Compare
I've recently been looking at/thinking about MCAP performance. However, it turns out that we don't currently have a good way to talk about performance since we can't measure it. Fix that by adding a benchmarking/ directory with read and write benchmarks for C++, Rust, Go, Python, and TypeScript. Each language has its own *_bench/ subdirectory. TypeScript supports unchunked/chunked/zstd (LZ4 compression is unavailable in wasm-lz4). Benchmark modes: - Fixed-payload: 1M messages x 100 bytes, single channel - Mixed-payload: 10-second simulated robot recording with 5 channels (/imu 96B@200Hz, /odom 296B@50Hz, /tf 80-1600B@100Hz, /lidar 230KB@10Hz, /camera 512KB@15Hz) — 3750 messages, ~102 MB - Filtered reads: topic filter (/imu), time range (seconds 3-5), and combined topic+time (/lidar seconds 4-6) using mixed files All modes run across 4 compression types (unchunked, chunked, zstd, lz4). Message payloads are sliced from a shared 16 MiB blob generated once by gen_blob.py (deterministic, fixed seed), so every language feeds byte-identical data to its writer and the comparison stays fair by construction. The blob is shaped like sensor data (a triangle wave with noise on ~1 in 4 samples, ~0.43 zstd ratio) so compression work is realistic. Each write bench emits a CRC-32 of its payload stream, and run_bench.sh aborts if the CRCs differ across languages. `make bench` runs the full matrix: all compression modes, fixed + mixed payloads, and filtered reads. Includes peak memory tracking and summary tables via run_bench.sh. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ebf0388 to
1c12f52
Compare
There was a problem hiding this comment.
The read-completeness gap from my last pass is closed: all five read benches now emit msg_count (col 10), and run_bench.sh enforces it — verify_read_counts (exact count for fixed + mixed) and check_filter_counts (non-zero + cross-language agreement). Traced the fixed and mixed paths across C++/Go/Rust/Python/TS; a reader that silently under-reads would now abort the run instead of posting a fast time. Also confirmed the unrelated round_trip.rs/filter.rs changes are gone from the diff.
No new blockers. Two optional nit threads remain open (clean-bench target for the ~GB of bench_*.mcap files, README @mcap/core wording) — neither blocking.
Changelog
None.
Docs
None.
Description
I've recently been looking at/thinking about MCAP performance. However, it turns out that we don't currently have a good way to talk about performance since we can't measure it.
Fix that by adding a benchmarking/ directory with read and write benchmarks for C++, Rust, Go, Python, and TypeScript. Each language has its own *_bench/ subdirectory. TypeScript supports unchunked/chunked/zstd (LZ4 compression is unavailable in wasm-lz4).
Benchmark modes:
All modes run across 4 compression types (unchunked, chunked, zstd, lz4) with configurable payload fill: uniform (0x42, best-case compression) or varied (deterministic pattern for realistic ratios).
make benchruns the full matrix: both fill patterns, all compression modes, fixed + mixed payloads, and filtered reads. Includes peak memory tracking and summary tables via run_bench.sh.