On an 8 GB Mac mini M1, a headless openscreen export spent 4.2 s between emitting started and composing its first frame — 18 % of a 60 s export's measured interval, and 71 % of a 5 s one. Then it stopped doing so, on the same binary, with nothing changed.
What was measured
Decomposed from the CLI's own JSON events plus timestamps inside the renderer:
|
while it reproduced |
now |
started → first progress |
4208–4502 ms |
481 ms |
renderer domInteractive |
3887 ms |
15–355 ms |
the whole of runExport |
24 ms |
24 ms |
Same shipped 1.10.0 application, untouched, hours apart. Everything after the module graph was always fast: loading the project is 4 ms, the two <video> metadata probes are 13 ms and 6 ms, and Compositor::new is 2.4 ms including runtime MSL compilation of shaders.metal.
What it is not
Three hypotheses were measured and all three were wrong:
- Runtime Metal shader compilation — 2.4 ms for the whole compositor init.
<video> probes scaling with media size — a 1.7 MB / 5 s source gives the same 4197 ms as a 50 MB / 60 s one.
- CPU work — neither process burns any. Both sit in
kevent64 / __workq_kernreturn; 14 CPU-seconds for a 23 s export.
It is also not the CLI window loading the editor's entry point. That was tried (#590) and measured at −0.1 % against a proper baseline.
What it most likely is
Memory pressure. While it reproduced the machine was at 387M unused / 2613M compressor with ~6.5 M swapouts; it is now 564M unused / 1837M compressor, 57 % free, and the cost is gone. Faulting ~1.8 MB of module chunks out of a 274 MB app.asar while the compressor is thrashing looks precisely like what was observed: several seconds of wall clock, no CPU, independent of the work being asked for.
That is a hypothesis. Nobody has recreated the pressure deliberately and watched the cost come back.
Why it is worth chasing anyway
- 8 GB is the entry-level Apple Silicon machine, and the export is exactly the moment the app is competing with a browser and an editor for RAM. If this is the cause, real users hit it and it is invisible in any profile — it does not show up as CPU.
- It lands inside the measured interval. The CLI emits
started as soon as the window is created, so any startup latency counts against the export in benchmarks and in the user's perception alike.
- The
app.asar is 274 MB, of which the renderer needs about 1.8 MB. Whether that size costs anything under pressure is testable.
How to settle it
Reproduce the pressure deliberately — pin memory until the compressor is near where it was, then measure started → first progress on the shipped app — and if it returns, try the same with a smaller asar (or with asar: false) to see whether archive size is the lever. Until someone does that, the cause is unproven and no fix should be attempted on the strength of it.
On an 8 GB Mac mini M1, a headless
openscreen exportspent 4.2 s between emittingstartedand composing its first frame — 18 % of a 60 s export's measured interval, and 71 % of a 5 s one. Then it stopped doing so, on the same binary, with nothing changed.What was measured
Decomposed from the CLI's own JSON events plus timestamps inside the renderer:
started→ firstprogressdomInteractiverunExportSame shipped 1.10.0 application, untouched, hours apart. Everything after the module graph was always fast: loading the project is 4 ms, the two
<video>metadata probes are 13 ms and 6 ms, andCompositor::newis 2.4 ms including runtime MSL compilation ofshaders.metal.What it is not
Three hypotheses were measured and all three were wrong:
<video>probes scaling with media size — a 1.7 MB / 5 s source gives the same 4197 ms as a 50 MB / 60 s one.kevent64/__workq_kernreturn; 14 CPU-seconds for a 23 s export.It is also not the CLI window loading the editor's entry point. That was tried (#590) and measured at −0.1 % against a proper baseline.
What it most likely is
Memory pressure. While it reproduced the machine was at
387M unused / 2613M compressorwith ~6.5 M swapouts; it is now564M unused / 1837M compressor, 57 % free, and the cost is gone. Faulting ~1.8 MB of module chunks out of a 274 MBapp.asarwhile the compressor is thrashing looks precisely like what was observed: several seconds of wall clock, no CPU, independent of the work being asked for.That is a hypothesis. Nobody has recreated the pressure deliberately and watched the cost come back.
Why it is worth chasing anyway
startedas soon as the window is created, so any startup latency counts against the export in benchmarks and in the user's perception alike.app.asaris 274 MB, of which the renderer needs about 1.8 MB. Whether that size costs anything under pressure is testable.How to settle it
Reproduce the pressure deliberately — pin memory until the compressor is near where it was, then measure
started→ firstprogresson the shipped app — and if it returns, try the same with a smaller asar (or withasar: false) to see whether archive size is the lever. Until someone does that, the cause is unproven and no fix should be attempted on the strength of it.