WolOS
Reference contents

Reference · Apps and the UI kit

Wolkit benchmarks

How the UI kit is measured, the budgets it must stay under, and every number measured so far with its machine.

On this page

What it is

How Wolkit and its engine are measured: the harnesses, the method, the budgets, and the results so far.

Why

A UI kit that is fast today gets slow one change at a time. Written budgets and repeatable harnesses make every slowdown visible.

How

Each number names the machine it was measured on and the command that repeats it.

libWolkit: the engine · Performance budgets

Reference

How the UI kit is measured and what has been measured so far. The engine is crates/wolkit (docs/modules/libwolkit.md), the Crystal shard is wolos-crystal/shards/wolkit (docs/modules/wolkit.md). Every number below names its machine; numbers are only comparable on the same machine at the same commit.

Harnesses

WhatWhereHow to runGate
Engine microbenchmarks: text, batching, style, layout, GPUcrates/wolkit/benches/{text,batch,style,layout,gpu}.rs (plain programs, no benchmark crate)cargo bench -p wolkit --bench <name> -- --checkcrates/wolkit/benches/budgets.toml: a median over its limit fails --check. Limits are ceilings with CI noise in mind; lower them by hand after a forge run, never raise one without a recorded reason.
Shard side: cold start, text change to patch applied, bytes per interaction, a full collection at 16 MB and 64 MB, a 100-op patch encodetools/bench/wolkit/ (bench.cr, run.sh, README.md)sh tools/bench/wolkit/run.sh (libraries from WOLKIT_BUILD, else make -C wolos-crystal/shards/wolkit all)one JSON line, median and p95 over 30 runs (WOLKIT_BENCH_RUNS)
Allocation per frame (Rust)crates/wolkit/tests/alloc_free.rs, paint_alloc.rs, style_alloc.rscargo test -p wolkita counting allocator: 0 allocations in a warm frame
Allocation per interaction (Crystal)wolos-crystal/shards/wolkit/spec/budget_spec.crmake -C wolos-crystal/shards/wolkit specGC.stats deltas
GPU against the CPU referencecrates/wolkit/tests/gpu_golden.rscargo test -p wolkit --test gpu_goldenno channel off by more than 2, at most 0.1% of pixels off by more than 1
Frame times, damage, draws of a running appwolkit_engine_stats_json (the HUD shows it), the compositor's wolstatsin a sessionnone (observation)

Method

The comparison the kit has to win is written down before it is run, so the result cannot pick its own rules.

  • Three sample apps. 1: to-dos (a list, a text field, 1,000 rows: typing latency, small updates, accessibility). 2: monitor (4 live charts of 1,000 points at 60 to 120 Hz: canvas, continuous animation, damage). 3: table (100,000 rows, search, column sort: virtualisation, scroll, large text).
  • Five stacks. S1 Wolkit on the Kit backend; S2 Wolkit native with the CPU rasteriser; S3 Wolkit native with Vulkan; S4 today's Kit path through wol.h (to-dos only: apps 2 and 3 do not fit its 200-part, no-canvas limits, which is itself a result); S5 a declarative script library in the image's own browser engine, in a kiosk window.
  • Metrics. Cold start to the first frame; RSS idle and under load; input to photon with the frame-hash probes of design/tools/perf (dmg, vk, vp, latency.py); the frame-time histogram (p50, p95, p99, missed frames per 1,000 at 120 Hz) from the engine's frame ring and wolstats; CPU time per frame from CLOCK_THREAD_CPUTIME_ID; energy over a 60 s script from the RAPL sampler (wol-realtime gpu sample); GC statistics and pauses; damaged bytes per frame; binary size; lines of app code and the number of concepts per stack.
  • Rules. Same machine, same scripted scenario, 30 runs, median and p95. A report states the commit, the machine and the display refresh, and says which numbers were taken at 60 Hz headless and which at 120 Hz on hardware. A nightly forge run fails on a regression over 10% against budgets.toml.

Budgets

Targets are for a mid-range integrated GPU at 120 Hz (an 8.33 ms frame). "Measured" is the best evidence so far; the forge is a shared 128-core build machine with no GPU, the CTO box an Ubuntu workstation with no GPU, so every GPU number there comes from a software Vulkan driver.

#BudgetTargetMeasured
1Input to frame submitted (engine)p50 <= 3 ms, p95 <= 5 msGPU path including the list rebuild with text: CTO box p50 0.80 / p95 1.08 ms, forge p50 2.03 / p95 2.87 ms
2Main-thread CPU per interaction frame<= 2.5 ms p95CTO box 0.12 ms, forge 0.35 ms
3Handler and effect flush (Crystal)<= 0.5 ms for <= 20 effectsnot yet in a bench
4Patch apply<= 0.2 ms per 100 opsnot yet in a bench ([patch] is reserved in budgets.toml)
5Style resolve<= 0.2 ms per 100 dirty nodesforge, release: 100 state flips 72 to 74 us, 100 inline 113 us, restyle of 1,000 nodes 130 us, a clean frame 7 ns
6Layout<= 0.5 ms per 1k-node boundary; full 10k relayout <= 4 msforge: boundary 277 to 390 us, an edit inside it 11 to 13 us, full 10k 2.9 to 4.6 ms (through the scene 3.4 to 4.0 ms), resize 0.7 to 0.8 ms; CTO box boundary 225 us, full 10k 2.4 ms
7Scroll at 120 Hz<= 1 ms CPU per frame, no dropped frames over 100k rows9 px steps / a fling across all 100,000 rows, p95: CTO box 0.045 / 0.049 ms, forge 0.107 / 0.115 ms; 0 of 6,000 frames over 8.33 ms
8Textrun-cache hit <= 2 us; cold 40-character Latin line <= 50 us; atlas hit rate 100% steadyhit 64 ns (CTO box) / 99 ns (forge); cold 3.7 / 5.7 us; atlas lookup 19 / 31 ns; hit rate 1.000 over 50 mixed Latin, CJK and Hebrew frames; a warm 4-line paragraph 11 us
9Batching<= 10 draw calls for a typical window1 to 2 for the typical window (sidebar, toolbar, 40 text rows, a menu: about 600 to 900 instances); 4 for the layered golden scene
10Damagea caret blink or hover damages only its bounds; idle 0caret 16 px, hover two rows (26,880 px), idle 0 px and 0 draws
11Allocation (Rust)0 per steady-state frame0 over 100 patch applies and 100 frames (tests/alloc_free.rs)
12Allocation (Crystal)0 idle; <= 8 KB per interaction0 idle; 285 bytes for a signal write plus its patch, about 750 bytes through the engine
13GC pausep99 <= 3 ms for a heap <= 64 MBfull collection, median / p95: 16 MB 1.5 / 1.6 ms, 64 MB 4.9 / 6.0 ms with parallel marking; one marker 2.9 / 3.0 ms and 13.4 / 13.7 ms. Collecting at idle (after 1 MB allocated) keeps every frame under 0.4 ms, so the shard collects only at idle and keeps its heap small
14Startfirst frame <= 150 ms native (CPU <= 100 ms), <= 250 ms kitnot yet measured on hardware
15Memoryempty window <= 25 MB RSS; <= 350 bytes per nodenot yet in a bench
16Idle<= 0.1 ms/s CPU, 0 wakeupsno frame callbacks or timers at idle by construction; not yet measured with design/tools/perf/idle.py

The CPU reference rasteriser draws the typical window at 640 by 480 in 8.2 ms (CTO box) and 13.3 ms (forge): it is the golden reference and the fallback, not the fast path.

Developer loop

Shard build times for a 3,081-line app of 70 screens on the forge: crystal build --no-codegen (the wol app check path) 1.8 s, a debug build 3.8 s, a rebuild after a one-line change 2.5 s, a release build 17.7 s. The hot-reload target is at most 5 s for a 2,000-line app.

Not measured yet

The five-stack comparison itself (sample apps 2 and 3 need the canvas and virtual-list paths on the native backend), input to photon on hardware, energy, RSS, start time and the idle probe. When they are run, their results go in a new section here with the commit, the machine and the refresh rate.

From the developer documentation, wonderlandos/wolos docs/modules/wolkit-bench.md.