Dv/cpu image bringup report - #20
Conversation
|
oh nice, how fast was it? |
There was a problem hiding this comment.
Pull request overview
Adds an experimental CPU image-generation script plus accompanying bring-up/report docs to document and reproduce coherent 128x128 CPU outputs (notably on the unpacked transformer path) for bonsai-image.
Changes:
- Added a standalone
scripts/generate_cpu_experimental.pyscript to run Flux2 CPU diffusion with logging and optional step image dumps. - Added two markdown reports capturing validated CPU bring-up results and suggested practical guidance.
- Documented an example command shape intended to reproduce the
128x128,4-stepCPU results.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| scripts/generate_cpu_experimental.py | New experimental CPU generation entrypoint (prompt encode → diffusion → VAE decode) with model loading helpers and detailed logging. |
| docs/upstream_issue_cpu_bringup.md | Focused bring-up/status note describing the strongest validated CPU results and reproduction shape. |
| docs/upstream_cpu_image_report_draft.md | Longer-form draft report consolidating the same CPU bring-up evidence and guidance. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| REPO_ROOT = Path(__file__).resolve().parents[1] | ||
| sys.path.insert(0, str((REPO_ROOT / "vendor" / "image-studio").resolve())) | ||
|
|
||
| from backend_gpu.diffusion_klein import _mflux_empirical_mu # noqa: E402 |
| def mem_gib() -> float: | ||
| with open("/proc/self/status") as fh: | ||
| for line in fh: | ||
| if line.startswith("VmRSS:"): | ||
| return int(line.split()[1]) / 1024 / 1024 | ||
| return 0.0 |
| parser.add_argument("--output", required=True) | ||
| parser.add_argument("--height", type=int, default=256) | ||
| parser.add_argument("--width", type=int, default=256) | ||
| parser.add_argument("--steps", type=int, default=1) |
| if args.height % 32 != 0 or args.width % 32 != 0: | ||
| raise SystemExit("height and width must be multiples of 32") | ||
|
|
| python scripts/generate_cpu_experimental.py \ | ||
| --prompt 'ostrich' \ | ||
| --height 128 \ | ||
| --width 128 \ | ||
| --steps 4 \ | ||
| --seed 7 \ | ||
| --transformer-dir models/bonsai-image-4B-ternary-unpacked/transformer |
| python scripts/generate_cpu_experimental.py \ | ||
| --prompt 'ostrich' \ | ||
| --height 128 \ | ||
| --width 128 \ | ||
| --steps 4 \ | ||
| --seed 7 \ | ||
| --transformer-dir models/bonsai-image-4B-ternary-unpacked/transformer |
Per codex: fastest passing config here was 96x96, 2-step, fp32, 4 threads: 11.4s warm render, about 49.6s total including 38.2s setup. 128x128 at 2 steps was 13.2s warm / 54.7s total, and 128x128 at 4 steps was 33.0s warm / 80.2s total. Old 128x128 4-step baseline here was about 20m42s, so the search materially improved it. This is with 4 neoverse arm vcpus and 24 gigs of ram on a free tier oracle cloud server. Will push latest changes to this branch. |
| @@ -0,0 +1 @@ | |||
| scripts: add warm CPU server benchmark helpers | |||
There was a problem hiding this comment.
can delete this file probably
| @@ -0,0 +1,38 @@ | |||
| ## Summary | |||
There was a problem hiding this comment.
do we need to commit this file?
should the in the PR itself
|
Nice that's faster than I imagined for CPU only, is this running in fp16 or using the 1-bit or ternary packing? Kinda saw mix of both when skimming through the code. |
|
Codex is on it, will post a reply shortly
Sincerely,
Denis Valeev
…On Sun, Jun 7, 2026 at 2:13 PM Pasha Khosravi ***@***.***> wrote:
*khosravipasha* left a comment (PrismML-Eng/Bonsai-Image-Demo#20)
<#20 (comment)>
Nice that's faster than I imagined for CPU only, is this running in fp16
or using the 1-bit or ternary packing? Kinda saw mix of both when skimming
through the code.
Happy to merge it after some clean up and making it blend better with rest
of demo, e.g. have a script that starts a server, etc.
—
Reply to this email directly, view it on GitHub
<#20?email_source=notifications&email_token=AAGX7TWKWSRAATI5PIDAFRD46WWEXA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTINRUGM2TIMZXHE32M4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#issuecomment-4643543797>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAGX7TUILCX6Q3HBNLSWAAL46WWEXAVCNFSM6AAAAACZYBFU56VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DMNBTGU2DGNZZG4>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
|
Pushed a cleanup pass to this branch. What changed:
On the runtime question: the path here is not fp16 on this ARM CPU box.
Heads-up on the warm timings: the faster numbers from the later server work are for cached prompt requests on a resident server. A brand-new uncached prompt still pays a large cold prompt-encode cost first on this CPU path, so warm cached latency and first-hit latency are very different. The warm-server helper now makes that split explicit. If you want, I can do one more follow-up pass after this and fold the CPU server start path more tightly into the existing demo flow. |
|
For the above example run this: |
|
A lone samurai warrior in ornate black lacquer armor standing in mist, katana held low at his side, crimson silk cords and weathered metal plates, rain droplets glistening on the armor, stern shadowed face under a kabuto helmet, dramatic rim lighting, foggy bamboo forest background, shallow depth of field, cinematic composition, high detail, realistic historical texture, moody atmosphere, ultra-detailed photography style. size: 1024x1024 |
| "huggingface-hub", | ||
| # Experimental CPU bring-up dequantizes the HQQ text encoder on both macOS | ||
| # and Linux, so keep hqq available outside the Linux CUDA path too. | ||
| "hqq", |
There was a problem hiding this comment.
hqq runs ok on cpu? is it mostly just used as loading the checpoint?
|
|
||
| [[package]] | ||
| name = "accelerate" | ||
| version = "1.13.0" |
There was a problem hiding this comment.
for the uv lock what's the main required changes?
I might just generate the uv lock myself after I merge this, too many changes to review.
Is the pyproject.toml enough to generate this? if yes could you remove this file from the diff. I can generate myself and push after.
| in_channels_latents = transformer.config.in_channels // 4 | ||
| h_lat = width // 8 | ||
| w_lat = height // 8 | ||
| noise_shape = (1, in_channels_latents * 4, h_lat // 2, w_lat // 2) |
| output_path = Path(req.output) | ||
| output_path.parent.mkdir(parents=True, exist_ok=True) | ||
|
|
| class GenerateRequest(BaseModel): | ||
| prompt: str | ||
| output: str | ||
| width: int | ||
| height: int | ||
| steps: int | ||
| seed: int | ||
| guidance: float = 1.0 | ||
| max_seq: int = 64 | ||
|
|
|
I tried to repro my own work here in this branch on a mac with m1; failed
miserably; tasked Codex to fix it all. It purportedly did in the latest
commits.
Per codex:
Yep. HQQ here is mostly a loading/dequantization path for the text encoder
and for getting the CPU path into a usable dense form; the validated CPU
inference itself is not doing packed HQQ/GemLite kernel execution on CPU.
On this host the working path ended up being float32 CPU inference with
unpacked dense transformer weights.
For `uv.lock`, the main required change is just reflecting the
`pyproject.toml` dependency updates, especially keeping `hqq` available
outside the Linux CUDA-only path because the experimental CPU bring-up
dequantizes the HQQ text encoder on both macOS and Linux. If you prefer,
`pyproject.toml` is enough for this PR and I can drop `uv.lock` from the
diff so you can regenerate it after merge.
Sincerely,
Denis Valeev
…On Wed, Jun 10, 2026 at 1:35 PM Pasha Khosravi ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In uv.lock
<#20 (comment)>
:
> @@ -19,13 +19,13 @@ name = "accelerate"
version = "1.13.0"
for the uv lock what's the main required changes?
I might just generate the uv lock myself after I merge this, too many
changes to review.
Is the pyproject.toml enough to generate this? if yes could you remove
this file from the diff. I can generate myself and push after.
—
Reply to this email directly, view it on GitHub
<#20?email_source=notifications&email_token=AAGX7TWBZZEFPZHFYQPKP2T47GL7JA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTINBXGAYTMNZQGAY2M4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#pullrequestreview-4470167001>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAGX7TXB3UOSVB2EEIMUW6347GL7JAVCNFSNUABGKJSXA33TNF2G64TZHMYTEMZQGI4DCMRRGQ5US43TOVSTWNBVG43DINBUG43DJILWAI>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
|
Codex:
merged the overlapping CPU bring-up docs into docs/cpu-experimental.md,
deleted the two redundant upstream draft docs, and pushed b15cea2 to
dv/cpu-image-bringup-report
Sincerely,
Denis Valeev
…On Wed, Jun 10, 2026 at 1:37 PM Pasha Khosravi ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In docs/cpu-experimental.md
<#20 (comment)>
:
> @@ -0,0 +1,95 @@
+# Experimental CPU Bring-Up
Thanks for the details doc :D
now there is 3 similar ones, can we merge all in one and summarize them a
bit.
—
Reply to this email directly, view it on GitHub
<#20?email_source=notifications&email_token=AAGX7TQKRSUOBXAFMUOXPML47GMHJA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTINBXGAYTQMZTGQY2M4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#pullrequestreview-4470183341>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAGX7TTGDDPYPIJHOJ6MSGT47GMHJAVCNFSNUABGKJSXA33TNF2G64TZHMYTEMZQGI4DCMRRGQ5US43TOVSTWNBVG43DINBUG43DJILWAI>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
|
CPU bring-up update from this Mac:
Validation on this Mac: ./scripts/generate_cpu_low_memory.sh \
--prompt 'ostrich' \
--output outputs/cpu-ostrich-128.png \
--height 128 \
--width 128 \
--steps 4 \
--seed 7Result: coherent Pushed fix commit: |
| payload = { | ||
| "prompt": prompt, | ||
| "output": str(output_path), | ||
| "width": width, | ||
| "height": height, | ||
| "steps": steps, | ||
| "seed": seed, | ||
| "guidance": guidance, | ||
| "max_seq": max_seq, | ||
| } |
| if [ -s "$UNPACKED_ROOT/transformer/diffusion_pytorch_model.safetensors" ] || \ | ||
| [ -s "$UNPACKED_ROOT/transformer/diffusion_pytorch_model.safetensors.index.json" ] || \ | ||
| find "$UNPACKED_ROOT/transformer" -maxdepth 1 -type f -name 'diffusion_pytorch_model-*.safetensors' -size +0c | grep -q .; then |
| parser.add_argument("--dtype", default="float32") | ||
| parser.add_argument("--text-encoder-dtype", default="auto") | ||
| parser.add_argument("--threads", type=int, default=4) | ||
| parser.add_argument("--interop-threads", type=int, default=4) |





Summary
Adds a focused CPU image generation bring-up report for
bonsai-image.The report centers the strongest demonstrated result: the unpacked transformer CPU path can produce coherent
128x128outputs end to end, including:ostrichWhy this is useful
This makes the current CPU status easier to understand and reproduce.
It documents that:
128x128is a practical validation target for CPU image generationGuidance captured in the report
128x128+for structure/composition validationNotes