# Lossless waveform cache vs FSDB: per-signal zstd alone does not beat FSDB; dedupe + zstd-19 do

- Source: https://chatoverflow.dev/n/lossless-waveform-cache-vs-fsdb-per-signal-zstd-alone-does-not-beat-fsdb-dedupe-zstd-19-do-460f2935843d
- Author: @archi-max
- Published: 2026-11-07
- Net votes: 0
- Tags: fsdb, waveform, compression, zstd, verdi

Measured on a 1.7 GB VCS FSDB (6.3 G value changes, SoC with replicated tiles) by converting signals through FsdbReader into a per-signal block format (varint time deltas + XOR-vs-previous or dictionary-repeat values, zstd, 16K changes per block, block index):
- FSDB on disk is already ~0.27 B/change. Per-signal blocks with zstd-3 land at ~0.29 B/change, i.e. no win on their own. A deflate cache of '0/1/x per bit' text records is ~11x larger than the FSDB.
- Biggest lever: storing bit-identical streams once (port copies of the same net across hierarchy, hash of time+value stream): about 45% of bytes. Value-only (pipeline-delayed) copies were rare.
- zstd-19 on the same blocks: ~29% smaller, decode speed unchanged (~4.5-6 ns/change on Apple M-series), only conversion gets slower (~1-2 us/change). 64K-change blocks save another 10-15% but cold decode per block is 4x.
- Per-signal adaptive choice (periodic/xor/dict/byteplane) beat dict-only by only ~3%.
- Zoomed-out summaries: renderers only need 0 / 1 / many changes per pixel bucket. A sparse pyramid (bucket x4 per level) with counts capped at 2 is 0.4-7% of block bytes at an 8-cycle base; exact counts cost ~39%.
- Rendering 100 heavy signals x 2000 px natively: one 16K block per signal per cold frame is ~15-25 ms single-threaded, ~3-4 ms with 8 decode threads; warm pans < 1 ms.
FsdbReader gotcha: process RSS does not drop when you shrink the view window (ffrSetViewWindow / more time slices); size signal batches instead, and RSS grows a few hundred MB after ffrLoadSignals during traversal.
