Visitar URL original
perf(wasm): Create image decode workers on demand by ramezgerges · Pull Request #25132 · unoplatform/uno · GitHub
Skip to content

perf(wasm): Create image decode workers on demand - #25132

Merged
MartinZikmund merged 1 commit into
unoplatform:masterfrom
ramezgerges:dev/rara/wasm-lazy-image-workers
Oct 9, 2026
Merged

MartinZikmund merged 1 commit into
unoplatform:masterfrom
ramezgerges:dev/rara/wasm-lazy-image-workers

Conversation

@ramezgerges

Copy link
Copy Markdown
Contributor

GitHub Issue: closes #25125

PR Type:

🐞 Bugfix (performance)

What changed? 🚀

Uno.UI.Runtime.ImageLoader created one decode worker per logical processor when the script loaded, whether or not the app ever decoded an image. Each worker is a separate isolate with its own heap and an OffscreenCanvas 2D context.

Workers are now created on demand. A decode uses an idle worker if there is one. Otherwise it creates a worker while fewer than navigator.hardwareConcurrency exist, and otherwise it waits in the existing queue. The limit is unchanged, so burst decoding keeps the same parallelism.

Measurements 📊

Master's SamplesApp on WASM in headless Edge, on a 12-thread machine. navigator.hardwareConcurrency was set through the DevTools protocol (Emulation.setHardwareConcurrencyOverride) before load, which sets the pool size without changing code. Memory is Windows private bytes of the tab's renderer process; the GPU process doesn't change with worker count. Two runs per row.

Idle cost, before this PR: about 4 MB per worker.

hardwareConcurrency Before After
1 494–497 MB –
12 538–541 MB 493–495 MB
32 622–629 MB 497–500 MB

After the change, one worker exists at idle because the SamplesApp decodes an image at startup. An app that never decodes one creates none.

After a burst of 48 decodes of a 2048×2048 PNG, 15 s later:

  • Each worker that decoded holds up to about 11 MB more. On master with 32 workers that's +347 MB.
  • Its own GC releases this within about a minute: 971 MB at 15 s, 641 MB at 60 s.
  • This PR doesn't change that peak once the pool has grown; idle release would be a follow-up.

Decode time for the same burst is unchanged:

  • 12 workers: 794–844 ms on master, 802–817 ms with this PR.
  • 4 workers: about 1.1 s on both.
  • Throughput stops improving at about 6 workers on this machine.

Validation

  • Runtime:
    • WASM Given_BitmapSource, in headless Edge: 16/16 pass, 1 skipped (an existing [Ignore]). This includes When_Browser_Workers_Are_Reused, which sends one more concurrent decode than hardwareConcurrency and so exercises growing the pool to its limit and reusing a worker.
    • The measurements above, run on the published app.
  • Not added: a test for "no worker before the first decode". The pool is per process and earlier tests decode images, so the runtime-test app can't observe an untouched pool.
  • Not measured: GPU memory of the workers' canvases on a real GPU. The headless browser here draws canvases in software.

PR Checklist ✅

🤖 Generated with Claude Code

ImageLoader created one worker per logical processor at startup, each an
isolate with its own heap and canvas: about 4 MB apiece, so 130 MB on a
32-thread machine before any image was decoded. Workers are now created
when a decode finds none idle, up to the same limit, so burst decoding
is unchanged.

Fixes unoplatform#25125

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the platform/wasm 🌐 Categorizes an issue or PR as relevant to the WebAssembly platform label Oct 8, 2026
@mergify

mergify Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

This pull request does not currently match the merge queue conditions, so it cannot be queued from here. The box comes back if it matches again.

@MartinZikmund
MartinZikmund merged commit bc33bb8 into unoplatform:master Oct 9, 2026
46 checks passed
mikernet added a commit to Singulink/uno that referenced this pull request Oct 20, 2026
Port of unoplatform#25132 (e768182), for unoplatform#25125.

ImageLoader created one worker per logical processor at startup, each
an isolate with its own heap and canvas (about 4 MB apiece), whether
or not the app decoded an image. Workers are now created when a decode
finds none idle, up to the same limit, so burst decoding is unchanged.
@agneszitte

Copy link
Copy Markdown
Member

@Mergifyio backport servicing/6.8 release/stable/6.7

@mergify

mergify Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

backport servicing/6.8 release/stable/6.7

✅ Backports have been created

Details

agneszitte added a commit that referenced this pull request Nov 1, 2026
…6.7/pr-25132

perf(wasm): Create image decode workers on demand (backport #25132)
agneszitte added a commit that referenced this pull request Nov 1, 2026
…r-25132

perf(wasm): Create image decode workers on demand (backport #25132)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform/wasm 🌐 Categorizes an issue or PR as relevant to the WebAssembly platform

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[WASM] The image decode worker pool creates one worker per logical processor, a fixed memory cost that scales with core count

3 participants