-
Notifications
You must be signed in to change notification settings - Fork 0
Benchmarks
ZArchiveSharp ships a BenchmarkDotNet suite (ZArchiveSharp.Benchmarks) covering the zstd codec and the archive container. This page records the current baseline and explains how to run and interpret the benchmarks.
dotnet run -c Release -f net10.0 --project ZArchiveSharp.BenchmarksBenchmarkDotNet prompts for a benchmark filter interactively; pass --filter to select directly:
# Compress benchmarks only
dotnet run -c Release --project ZArchiveSharp.Benchmarks -- --filter *ZstdCompress*
# Everything
dotnet run -c Release --project ZArchiveSharp.Benchmarks -- --filter *Always run in Release — Debug numbers are meaningless. A full run takes several minutes (the btultra2/level-19 rows dominate).
17 benchmarks across three classes:
Single-shot ZstdCompressor.CompressBlock frames at levels 1 / 6 / 19 over four payload kinds:
| Payload | Content | Purpose |
|---|---|---|
Text8k |
Phrase-cycle text | Compressible fast path |
Random8k |
Seeded random bytes | Raw-fallback path |
Hetero64k |
16 KiB text + 8 KiB random + 8 KiB text, zero-padded to 64 KiB | The ZAR container's real-world block shape |
Text200k |
200 KiB phrase-cycle text | Multi-block (128 KiB) path |
Levels 1 and 6 are the ZAR container's hot path (default 6); level 19 pins the btultra2 binary-tree finder cost.
Decoding the same payload kinds — five decode benchmarks.
End-to-end container operations: pack and extract of a 4 × 64 KiB file tree.
net10.0, Release, x64 (indicative — rerun on your hardware):
| Benchmark | Mean | Throughput |
|---|---|---|
| L1 compress, hetero 64 KiB | 49 µs | ~1.3 GB/s |
| L6 compress, hetero 64 KiB | 145 µs | ~450 MB/s |
| L6 compress, 200 KiB text | 427 µs | ~470 MB/s |
| L19 compress, hetero 64 KiB | 1.70 ms | ~38 MB/s |
| Decode (all payloads) | — | ~860 MB/s |
| Pack 4 × 64 KiB tree | 869 µs | — |
| Extract 4 × 64 KiB tree | 310 µs | — |
Measured 2026-09-10: the hot path runs at native parity (L6 hetero 64 KiB ~1.0× stock libzstd 1.5.7, decode ~1.0×). For reference, the ZstdSharp port (with unsafe + IL-weaving) measures several× faster than stock libzstd itself on these payloads — a harsh oracle, not a peer. ZArchiveSharp deliberately chooses:
- Byte-exact parity over speed — every strategy, entropy decision and splitter boundary must match libzstd 1.5.7 byte-for-byte
-
Safe code — no
unsafe, no P/Invoke, BCL only, AOT-compatible
The known optimization lever: the compress path allocates ~1 bound-size buffer per frame (e.g. 3.7 MB for 200 KiB input). ArrayPool rental is the obvious next step. Span-specializing the hot match-finder loop is the other tracked follow-up.
- The match finders (
ZstdFast,ZstdDoubleFast,ZstdLazyEngine) dominate levels 1–9 - The binary-tree optimal parser (
ZstdOpt+ZstdBinaryTree) dominates 13–22 - Entropy coding (
ZstdFseEncoder,ZstdHuffmanEncoder) is the second hot spot at every level - Decode is a single
ZstdDecompressorpath — already near the safe-C# ceiling
- Payloads are deterministic and frozen (
BenchmarkCorpus): fixed seeds only, neverHashCode.Combine(process-randomized — would silently redraw vectors per run) - Buffers are built once in
[GlobalSetup]and never mutated; the compressor/reader never modify inputs -
[MemoryDiagnoser]is on, so allocation columns are trustworthy - Iterations are bounded (
MinIterationCount(5),MaxIterationCount(20)) to keep the level-19 rows tolerable
If you need native-class speed and can accept a dependency, the IZarBlockCompressor seam lets you plug any block compressor (e.g. a native interop adapter) without touching the container — Zarchive.exe parity is only guaranteed with the built-in ZstdCompressor. Note this is opt-in only; the default must stay byte-identical.
ZArchiveSharp
Getting Started
Core Concepts
API
Advanced
Links