[perf] MiniMax H3: return uint8 frames from the decode worker - #1828
[perf] MiniMax H3: return uint8 frames from the decode worker#1828stayinalive181 wants to merge 1 commit into
Conversation
The H3 decode stage handed a CPU fp32 pixel buffer across the multiprocess-executor boundary (4.27 GB for a 345-frame 1344x768 clip), and the main process cast it to uint8 immediately afterwards. Casting inside the worker ships 1.07 GB instead. The clamp matches the post-decode path, so the pixels are unchanged. Post-decode now passes uint8 worker output through untouched, and the optional ``samples`` mirror stays on its [0, 1] float contract. This is one of the runtime patches published in hlander-ai/minimax-h3 (Apache-2.0); on 8xH200 that set trimmed the post-decode/encode/save stages from 3.09 s to 1.98 s per 345-frame clip. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
Welcome to FastVideo! Thanks for your first pull request.
How our CI works:
PRs run a three-tier CI system:
- Pre-commit — formatting (yapf), linting (ruff), type checking (mypy). Runs immediately on every PR.
- Fastcheck — six core GPU lanes run automatically via Buildkite (~10-15 min).
- Merge gate — a reviewer adds
ready; changed paths select only the relevant integration, training, golden, or SSIM coverage.
Before your PR is reviewed:
-
pre-commit run --all-filespasses locally - You've added or updated tests for your changes
- The PR description explains what and why
If pre-commit fails, a bot comment will explain how to fix it. Fastcheck and merge-gate results appear in the Checks section below.
Useful links:
Merge Protections🔴 1 of 1 protections blocking · waiting on 👀 reviews and 🤖 CI
🔴 PR merge requirementsWaiting for
This rule is failing.
|
SolitaryThinker
left a comment
There was a problem hiding this comment.
Two issues need addressing: training validation still assumes normalized float output, and existing decode-stage tests contradict the new uint8 contract.
Validation: reproduced uint8 overflow with a CPU PyTorch arithmetic check. Attempted the entrypoint and H3 streaming-stage pytest suites, but collection was blocked by a Triton dependency requiring an active GPU driver. No GPU inference or SSIM run was performed.
| # 4.27 GB as fp32 and 1.07 GB as uint8, and the main process | ||
| # cast it to uint8 immediately anyway; the clamp matches the | ||
| # post-decode handling of VAE output slightly outside [0, 1]. | ||
| output = output.mul_(255).clamp_(0, 255).to(torch.uint8) |
There was a problem hiding this comment.
[P1] Handle uint8 output in training validation before changing the stage contract
This also changes direct pipeline calls, which bypass the new VideoGenerator handling. The training validation callback reads output_batch.output directly and still applies (x * 255).numpy().astype(np.uint8) in fastvideo/train/callbacks/validation.py:1575. The shipped examples/train/configs/overfit_minimax_h3_t2va.yaml selects this H3 pipeline, so validation videos now suffer uint8 overflow: decoded values [0.25, 0.5, 0.75, 1.0] produce pixel bytes [63, 127, 191, 255], which validation transforms into [193, 129, 65, 1]. Please update this consumer to pass uint8 through and add regression coverage.
| # cast it to uint8 immediately anyway; the clamp matches the | ||
| # post-decode handling of VAE output slightly outside [0, 1]. | ||
| output = output.mul_(255).clamp_(0, 255).to(torch.uint8) | ||
| batch.output = output if is_output_rank else placeholder |
There was a problem hiding this comment.
[P2] Update existing stage tests for the new uint8 output contract
fastvideo/tests/stages/test_minimax_h3_vae_streaming.py still asserts that the serial result is the original float output buffer with values of 0.25 (lines 112–114), and that the parallel result contains 0.5 (line 192). This conversion instead returns a new uint8 tensor containing 63 or 127, respectively, so both tests contradict the new behavior. Please update them to verify quantization while preserving their CPU-buffer and rank-participation checks; the added entrypoint test does not exercise either stage path.
Purpose
The MiniMax-H3 decode stage returns a CPU fp32 pixel buffer that crosses the multiprocess-executor boundary (4.27 GB for a 345-frame 1344x768 clip), and the main process casts it to uint8 immediately afterwards. Casting inside the worker ships 1.07 GB instead. Lossless: the clamp matches the post-decode path, so the pixels are unchanged.
This is one of the runtime patches published in hlander-ai/minimax-h3 (Apache-2.0), where the post-decode/encode/save stages went from 3.09 s to 1.98 s per 345-frame clip on 8xH200 (together with a worker-side encode change that is not part of this PR). It applies to every hardware target, not only Hopper.
Changes
fastvideo/pipelines/basic/minimax_h3/stages/minimax_h3_decoding.py: on the output rank, quantize the decoded fp32 buffer to uint8 (mul_(255).clamp_(0, 255)) before handing it to the executor. The TAEH3 preview path andoutput_type="latent"are untouched.fastvideo/entrypoints/video_generator.py: post-decode passes uint8 worker output through untouched (float output keeps the on-device quantize path from [perf] Quantize frames to uint8 on-device before the post-decode D->H copy #1362); the optionalsamplesmirror stays on its[0, 1]float contract (copy_thendiv_(255), orfloat().div_(255)on the shape-mismatch path).fastvideo/tests/entrypoints/test_video_generator.py:test_generate_single_video_accepts_uint8_worker_outputchecks that frames come back byte-identical and thatsamplesis[0, 1]float.Test Plan
Test Results
Test output
The same worker-side cast has been running in a downstream 4xH100 deployment (Windflow streaming worker on FastVideo
b2db0c0, which consumes the uint8 frames throughreturn_frames=True), including a 30-minute continuous run of 51 clips.Checklist
pre-commititself is not installed on the authoring machine)For model/pipeline changes, also check:
*255, clamp and cast, moved across the process boundary) and the unit test asserts exact equality