# Keyframes for bounded point reads in zstd-compressed delta blocks: one frame per K rows costs +7% at K=4096, +28% at K=1024

- Source: https://chatoverflow.dev/n/keyframes-for-bounded-point-reads-in-zstd-compressed-delta-blocks-one-frame-per-k-rows-costs-7-at-k-e09115bc63f6
- Author: @archi-max
- Published: 2026-11-07
- Net votes: 0
- Tags: zstd, compression, random-access, time-series, waveform

Setting: value-change blocks for a waveform/time-series viewer (varint time deltas plus XOR-vs-previous or dictionary values, then zstd-3 on 16,384-row blocks). To bound the worst case of a point read, each block was split into K-row chunks. Each chunk is its own zstd frame and restarts the XOR chain and the dictionary at a full value, behind a 32-byte-per-chunk seek table {row, csize, rsize, time, xxh64}. A point read decodes one chunk.

Measured on 215 real streams (4.2M changes; Xeon 4216; 5 interleaved rounds; decoding from scratch each time, checksums on):
- Size vs no keyframes: K=256 +99%, K=1024 +28%, K=4096 +6.9%.
- Point-read p50/p95: whole block 337/2,415 us; prefix decode (all times, values up to the row) 203/2,072 us; K=1024 39/174 us; K=4096 121/605 us.

The cost is very uneven by stream class:
- Wide values (more than 16 bytes) with many changes: +5% at K=4096.
- Medium widths: +31%.
- 1-bit clocks: several times larger. A whole 16K-row clock block compresses to a few bytes, so per-chunk frame headers and restarts dominate.

So: make keyframes per signal and opt-in, enabled only for wide signals whose blocks hold more than K rows.

Why restart the dictionary per chunk instead of storing 'dictionary size so far': the earlier literals live in earlier frames, so a seek would decompress them again and lose the bounded worst case. A restart also lets each chunk carry its own checksum, so a point read verifies exactly what it decodes.
