Render benchmark · LVGL Coffee Dispenser HMI

Did LVGL 9.6 get faster?

On this workload: yes, by about 7% of render time per fully redrawn 800×480 frame — measured as eight back-to-back pairs, because on this machine the absolute numbers drift further than the effect. The memory bill moved the other way: +16% peak RSS, while LVGL's own heap grew only 3%.

Render per frame −7.1% paired median of 8 runs
Flush per frame +1.9% SDL upload · unchanged
LVGL heap peak +3.1% 40752 → 42008 bytes
Process peak RSS +16% 11.7 → 13.5 MB

Eight pairs, not two averages

Each pair ran back to back on the same pinned core, 9.3.0 first: same clock state, same background load, so the difference inside a pair is the library and not the machine. The absolute level wanders by more than 10% across the session — that is the reason for pairing, and the reason a single before/after run is worthless here.

LVGL 9.3.0 LVGL 9.6.0 │ one pair per row
Render time per frame, eight pairs. Every pair points the same way; the size of the gap does not.

Where the frame time goes

Rasterization plus the handover of the finished buffer to SDL. Only the first half is LVGL's own work, and only the first half moved.

Ten-run means from the two committed reports, measured in the same session.

The memory bill

Measured in-process: lv_mem_monitor() for the pool LVGL manages itself — the number an embedded integrator sizes — and ru_maxrss for the whole process, which also carries the SDL window, the frame buffers and the C library.

Metric9.3.09.6.0ΔHow measured
LVGL heap peak40 752 B42 008 B+1 256 B (+3.1%)lv_mem_monitor(), 10 runs, identical every run
LVGL heap in use10%10%—of the same 256 KB pool
Process peak RSS11 707 kB13 464 kB+1 757 kB (+15%)ru_maxrss, 10 runs
Stripped binary1.73 MB1.97 MB+0.24 MB (+14%)release build, same flags

The split is the interesting part: LVGL's managed pool barely grew, so the extra 1.8 MB is not allocations LVGL tracks. It sits in what the library allocates outside that pool and in the binary itself — more code, more static data, larger draw and cache structures. On a target where the LVGL pool is the constrained resource, 9.6 costs 1.3 KB more; where total RAM is the constraint, it costs about 1.8 MB.

The numbers

Pair9.3.0 render9.6.0 renderΔ
median42623917−7.1%

All values in microseconds. Two further interleaved measurements on the same machine returned −5.1% and −8.0%, and the two ten-run reports −7.3%, so the honest range is 5 to 8%.

Is 7% a good improvement?

For what it costs the integrator, yes. It arrives by bumping a submodule: no configuration change, no visual difference, and on our side only a handful of deprecated calls to migrate. Those ~350 µs per frame are CPU time handed back to the application, and they compound with every redraw.

It is well under the “up to 1.33×” the release notes advertise — expected, not suspicious. That figure is a best case on the boards LVGL measured: ARM cores, usually RGB565, with the blend paths that got the most attention. This run is x86-64, XRGB8888, one draw unit, and a UI of large opaque fills, rounded panels and text. The honest reading: a real single-digit gain on hardware LVGL did not optimise for, paid for with about 1.8 MB more resident memory. If RAM is the binding constraint on your target, that trade needs checking before the upgrade, and the LVGL pool figure above says the growth is not in the part you size yourself.

Did we measure the right thing?

Partly, and one earlier version of this page did not. It reported 1.23× from a single before/after round; repeating it as pairs put the same comparison at 7%. The first number was machine drift, and it is corrected here rather than quietly dropped.

Method

Harnessscripts/run_render_benchmark.sh --runs 8 --duration-ms 4000 --pin-cpu 0 --compare <baseline build>
AppLVGL Coffee Dispenser 0.10.5, release build, identical sources both sides
Display800×480, XRGB8888, SDL dummy driver
RefreshLV_DEF_REFR_PERIOD 33 ms, so ~30 fps is the ceiling
CPUIntel Core i9-12900H, pinned to CPU 0
CompilerGCC 16.2.1, -O3, identical flags for both
Configidentical, except LV_COLOR_DEPTH 32 vs LV_COLOR_FORMAT_DEFAULT XRGB8888
Memorylv_mem_monitor() and ru_maxrss, reported by the application itself