Docs Performance

Encoders

zstd at three levels against brotli on the shared board, and the heap each encoder holds per stream: the bytes on the wire, the CPU and the memory each setting trades.

Every stream holds an encoder for as long as its tab is open, so an encoder costs three things: bytes on the wire per frame, CPU per frame, and heap per stream. sse.Options.ZstdLevel, ZstdWindow, ZstdLowMemory, BrotliQuality and BrotliWindow choose among them.

On the shared board

BenchmarkBoardZstdLevels is the shared board with every stream on zstd, at the fastest level (lith's default), the default level and the better-compression level. Measured on 2026-10-13, medians of three runs; brotli from BenchmarkSharedBoard the same day:

Bytes per frame of the 140 KB board, 200 streamsbench/RESULTS.md
01,0002,0003,000bytes per frameencoderbytes per frame, encoder zstd fastest: 2,266 bytes per framezstd fastestbytes per frame, encoder zstd default: 1,138 bytes per framezstd defaultbytes per frame, encoder zstd better: 365 bytes per framezstd betterbytes per frame, encoder brotli q4: 955 bytes per framebrotli q42,2661,138365955
The numbers
encoderbytes per frame
zstd fastest2,266
zstd default1,138
zstd better365
brotli q4955
Cores busy in the same runsbench/RESULTS.md
01234coresencodercores busy, encoder zstd fastest: 1.32 coreszstd fastestcores busy, encoder zstd default: 1.07 coreszstd defaultcores busy, encoder zstd better: 3.11 coreszstd bettercores busy, encoder brotli q4: 3.31 coresbrotli q41.321.073.113.31
The numbers
encodercores busy
zstd fastest1.32
zstd default1.07
zstd better3.11
brotli q43.31
Encoder Bytes per frame Cores busy Heap per stream Tick p99
zstd fastest (lith's default) 2,266 (2,183 to 2,283) 1.32 (1.05 to 1.41) 1.68 MiB 5.2 ms
zstd default 1,138 (1,087 to 1,153) 1.07 (1.07 to 1.15) 2.76 MiB 2.5 ms
zstd better compression 365 (364 to 366) 3.11 (3.08 to 3.20) 5.56 MiB 2.8 ms
brotli quality 4 955 3.31 2.13 MiB 2.7 ms
  • zstd's default level halves the bytes of the fastest for no more CPU in these runs, and holds 1.1 MiB more per stream: about 220 MiB more for 200 tabs.
  • Better compression sends a third of the default's bytes, fewer than brotli, for about brotli's CPU and two and a half times its heap.
  • Every run sent 3,920 to 3,969 frames a second and dropped none. The fastest level varied the most between runs, as other agents' work on the machine changed.

Heap per stream

TestHeapPerStream (sse/heap_test.go) measures the encoders on one core, without the render loop: 200 frames of a 57 KB page, of which five rows change from one frame to the next, to each of 20 streams. Three runs gave the same bytes each time:

Encoder Bytes per frame Time per frame Heap per stream
zstd fastest 405 36 to 41 µs 1.65 MiB
zstd fastest, ZstdLowMemory 405 55 to 58 µs 1.02 MiB
zstd default 228 31 µs 2.73 MiB
zstd better 108 184 to 201 µs 5.54 MiB
brotli quality 4 121 251 to 265 µs 2.02 MiB
  • zstd keeps a history of twice its window, 1 MiB for the 512 KiB window. ZstdLowMemory keeps it at the window: 0.63 MiB less per stream, for half again the CPU per frame here, and twice with 200 streams, whose encoders no longer fit in the CPU's cache.
  • Datastar's Go SDK, given WithBrotli without WithBrotliLGWin, keeps brotli's 4 MiB window: 9.9 MiB of heap per stream, measured the same way at quality 5. 1,000 open tabs hold about 9.7 GiB there, against 1.0 GiB with zstd and ZstdLowMemory, or 1.6 GiB with lith's default.

Choosing

  • Memory is the constraint for most apps: an open tab holds its encoder for as long as it stays open. The stream budget decides the window per stream from the memory there is.
  • lith's default is zstd's fastest level at 2^19, 1.65 MiB a stream. Where bandwidth costs more than memory, ZstdLevel: zstd.SpeedDefault halves the bytes for 1.1 MiB more a stream. The budget's tiers are sized for the fastest level, though: at another level a stream holds more than the budget reserves for it, so leave room under the limit.
  • brotli serves the browsers without zstd, Safari before 26.3 among them. Its CPU per frame is four to six times zstd's.

Measuring again

shell
taskset -c 0-11 go test ./loadtest -run '^$' -bench BoardZstdLevels -count 3
go test ./sse -run TestHeapPerStream -v -count 3