Skip to content

perf(studio-server): copy alpha cut-outs several pieces at a time - #6342

Open
miguel-heygen wants to merge 1 commit into
mainfrom
checklong/alpha-parallel-pieces
Open

miguel-heygen wants to merge 1 commit into
mainfrom
checklong/alpha-parallel-pieces

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

What

Copying a long alpha cut-out to a browser-playable VP8 file now encodes several 15 s pieces at the same time, sharing the cores between them. How many: at most 4 pieces across ALL copies running together (one shared pool, not one per copy), one core always left free for the editor, and no more than one piece per 3 GB of RAM (each 4K piece holds about 1.2 GB). On a 4-core 30 GB box that is 3; an 8 GB laptop gets 2. The progress line counts the finished pieces plus every piece in flight. If one piece fails, the others are ended at once and that failure is the one reported. H.264 copies are unchanged: one piece at a time.

Why

libvpx scales badly across threads, so one 4-thread encode leaves cores idle and burns CPU on thread overhead. One 15 s piece of the 4K alpha clip: 4 threads used 106 s of CPU, 1 thread used 94 s. Four 1-thread encodes of 60 s of the clip took 95 s on both runs, against 186 s and 196 s for one piece at a time with 4 threads.

End to end

Generated 120 s 3840x2160 VP9-alpha clip, check --prepare-media, 4-core Linux box shared with other jobs, cache cleared each run, sequential and this branch alternating (with the cap, 3 pieces at once):

run sequential this branch
1 127 s 107 s
2 131 s 117 s
3 156 s 120 s

Faster in all three pairs, and the branch ran at the higher machine load each time. An earlier uncapped run (4 at once) gave 110/108/124 s against 160/130/130 s. The gain is smaller than the encode-only test because every piece also decodes its slice of the source.

Why the file is smaller (33 MB vs 41 MB)

Only -threads changed per piece, and that alone changes the bytes: the same 5 s of the clip is 5.30 MB at -threads 4 and 3.09 MB at -threads 1 with identical settings (crf 23). Quality does not drop with it (below).

Quality against the serial path

First 40 s (1200 frames, covering the seams at 15 s and 30 s), both copies against the source scaled to the copy's size (2166x1218), PSNR over Y, U, V and alpha:

serial this branch
mean PSNR, all planes 36.61 dB 36.76 dB
worst frame 35.44 dB (frame 249) 35.44 dB (frame 249)
alpha plane, mean / worst 56.05 / 55.80 dB 56.05 / 55.80 dB

The two copies against each other: mean 49.8 dB, worst 36.9 dB, many frames bit-identical. Seams: the six frames around 15 s and around 30 s sit at 36.2 to 37.6 dB, the same as the rest of the clip, so there is no jump or dip at a piece boundary.

Memory and CPU

Measured per ffmpeg process on the 4K alpha clip: peak RSS 1.15 to 1.18 GB for a 1-thread piece and 1.17 GB for a 4-thread piece, so memory scales with the number of pieces at once (4 would be about 4.6 GB); that is why the cap divides total RAM by 3 GB. CPU per piece is lower with 1 thread (94 s vs 106 s).

Tests

Three new tests in proxyTranscoder.test.ts: two long alpha copies started together never run more pieces than the cap (fails when each copy gets its own pool); pieces start side by side with the cores shared out and progress counts them together (fails with the pool forced to one worker), and a failing piece ends the pieces still running and reports its own error. All 57 in the file pass; lint and format clean.

@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 271 (base branch 271), smooth 149 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

Unstable (1)

  • seqplainkeys-none-pct-r0-root-z100: tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo false, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true

@miguel-heygen
miguel-heygen force-pushed the checklong/alpha-parallel-pieces branch 2 times, most recently from dab2a59 to f16eabf Compare October 25, 2026 21:01

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES · 1 blocker · 0 important · 0 nits · head f16eabf9

The piece cap applies to each copy, but two copies can run at once, so neither the third-of-RAM limit nor the free core holds. ↓ inline

Scope

Read proxyTranscoder.ts and the two new tests in full. Checked the abort path: a worker that starts a piece after stop.abort() hits signal.throwIfAborted() in spawnFfmpeg, and Promise.all waits for every worker, so no ffmpeg process is left behind. Checked that progress only goes up and that swapping variant === "vp8" for keepsAlpha changes nothing. I couldn't run the test file locally because the workspace packages weren't built. CI was still running at review time. No earlier reviews.

— Claude (Genesis)

Comment thread packages/studio-server/src/helpers/proxyTranscoder.ts Outdated
@miguel-heygen
miguel-heygen force-pushed the checklong/alpha-parallel-pieces branch from f16eabf to d5e785d Compare October 25, 2026 21:14

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — 1 resolved, 0 open, 0 new · f16eabf → d5e785d

Non-blocking: an alpha copy short enough to go in one pass (≤ 22.5 s) skips ALPHA_PIECE_GATE and still uses CORES threads. One such copy running next to a long one is one encode over the cap: on an 8 GB laptop, 3 × ~1.15 GB is about 43% of RAM. Taking a slot around the single-pass alpha encode would close this.

— Claude (Genesis)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant