Docs Performance

Hibernation

An hour of 200 tabs, simulated second by second: what an idle zstd stream saves when it gives its encoder back, what its next frame costs, and what browsers make of it.

Most open tabs sit still. With live.Options.Hibernate set, a zstd stream that sent no frame for that long ends its zstd frame and gives its encoder back to a pool, and its window back to the budget; its next frame takes an encoder from the pool and starts without the history of the frames before. Heartbeats go out without waking it. It is off unless an app sets it.

An hour of 200 tabs

TestHibernation (hibernate.csv) simulates an hour of 200 tabs, second by second, with the page workload's frames: each person changes something every three minutes on average, a modal and then, a second later, the list it changed, and the hub sends a heartbeat after 20 s of silence. The heap per tab is the mean of samples every five minutes after the first ten, each after a collection, which also empties the pool of encoders.

Heap per tab, by how long a stream waits before it hibernatesbench/budget/hibernate.csv
0.00.51.01.52.0MiB per tabhibernate afterzstd 2^17 low memory, hibernate after never: 0.640 MiB per tabzstd 2^19, hibernate after never: 1.647 MiB per tabneverzstd 2^17 low memory, hibernate after 120 s: 0.308 MiB per tabzstd 2^19, hibernate after 120 s: 0.787 MiB per tab120 szstd 2^17 low memory, hibernate after 30 s: 0.106 MiB per tabzstd 2^19, hibernate after 30 s: 0.268 MiB per tab30 s0.6401.6470.3080.7870.1060.268
  • zstd 2^17 low memory
  • zstd 2^19
The numbers
hibernate afterzstd 2^17 low memoryzstd 2^19
never0.6401.647
120 s0.3080.787
30 s0.1060.268
Setting Hibernate after Heap per tab Tabs awake Bytes per frame Heartbeats per tab and hour
zstd 2^17, low memory never 0.640 MiB 100% 433 1,010 B
30 s 0.106 MiB 16% 1,533 2,412 B
120 s 0.308 MiB 48% 1,103 1,798 B
zstd 2^19 never 1.647 MiB 100% 277 1,010 B
30 s 0.268 MiB 16% 1,532 2,412 B
120 s 0.787 MiB 48% 1,093 1,798 B

What the next frame costs

After 30 s, a sixth of the encoders stay, and a frame takes about 1.1 KB more: 41% of the frames start a zstd frame without history, 1,947 bytes on average, and the list after a modal finds only the modal in its frame. That is about 45 KB more per tab and hour. A heartbeat of a hibernating stream goes out in a zstd frame of its own, 15 bytes instead of 6.

Waking takes 36 to 62 µs for the page's 37 KB list with an encoder from the pool, and 101 to 117 µs with a new one; 124 µs and 303 to 467 µs for the board's frame (TestWakeCost). The list then takes 3,790 bytes on the wire, the board 10,930.

What browsers decode

Headless Chromium 154 (TestChromiumDecodesFrames in sse) decodes a zstd body of several frames event by event, each as soon as it is sent: frames that hibernation ended, raw frames while a budget had no room, frames from pooled encoders. A brotli body cannot do the same: given one that holds a second brotli stream after the first, Chromium decodes the first and drops the second without an error. So only zstd streams hibernate.

Choosing a time

A hibernating tab trades heap for bytes: the 30 s setting cuts the heap to a sixth and adds 45 KB an hour per tab. Where tabs stay open for hours and change rarely, a public site, a dashboard on a wall, that trade is cheap; this site hibernates after a minute. Where every open tab changes every few seconds, hibernation rarely starts, and costs nothing either.

Measuring again

shell
LITH_BUDGET=1 taskset -c 0-11 go test ./bench/budget -run 'Hibernation|WakeCost' -v -timeout 1h
go test ./sse -run TestChromiumDecodesFrames -v

The Chromium test finds Chromium on the PATH, or in CHROME; on a machine without a sandbox for it, set LITH_CHROME_NO_SANDBOX=1.