Lossless waveform value-change codecs compared: per-signal adaptivity buys ~3%; dedupe, entropy stage and wide signals matter most
Measured on 494 signals (590M value changes) from a SoC simulation dump. Each signal is a stream of (time, value) changes, cut into blocks, then zstd. Bytes per change at zstd-3, with zstd-19/xz in brackets: - raw (u64 time + 2 value planes): 3.68 - u64 time delta + one char per bit, deflate-1 (a typical simple cache format): 4.15 - periodic model (predict from change i-2, plus exceptions): 0.53 (0.37) - XOR with the previous value: 0.41 (0.28) - dictionary (repeat index, or an XOR literal): 0.36 (0.26) - best of {tiny, periodic, xor, dict, byte-plane} per signal: 0.35 (0.25), only ~3% better than dict alone - plus storing exact-duplicate streams once: 0.19 (0.14)
What each signal ended up choosing: clocks and toggles periodic (~0 bytes), short signals 'tiny', buses mostly dict, very wide signals split between xor/byte-plane/dict.
Where the bytes go: signals wider than 256 bits held 66% of the bytes from only 7% of the changes. Port copies of the same net were ~45% of bytes, which is why dedupe is the biggest lever. Value-only copies (pipeline-delayed) were rare.
Not tried, and probably next: a time base shared per clock domain (most signals change on the same edges, but each stores its own times); splitting wide packed structs into fields, or coding only the changed bits; cross-signal correlation beyond exact or shifted copies; context-model entropy coding for the few heaviest signals.
Other notes: decode speed is about the same for zstd-3 and zstd-19 (~5 ns/change), so the level is purely an encode-time cost. 64K-change blocks are 10-15% smaller than 16K but make cold random-access decode 4x bigger.