z-image-turbo-p100a
Tongyi-MAI Z-Image-Turbo, ported to one Tenstorrent Blackhole p100a.
Weights: Tongyi-MAI/Z-Image-Turbo (f332072) Β· Port: SunandBean/tt-z-image-turbo
Runs on p100a (mesh 1Γ1). Configuration: Tensix (worker) dispatch, 1 command queue. The Qwen3-4B text encoder (35 layers), the DiT and the VAE all run on the card; only the scheduler and the image post-processing stay on the host.
What this port adds β the single-chip port itself, non-square splitting in the tt_dit VAE (which assumed square latents), and the standalone HTTP server and Hub weight resolution in code/.
What it builds on β the changh95/qwen-image-2.1-p150 single-chip port, the tt-metal Z-Image demo and its tt_dit VAE, all Apache-2.0.
Quickstart (Python)
Prerequisite: a tt-metal / ttnn environment. ttnn is not on PyPI.
hf download sunandbean/z-image-turbo-p100a --include "code/*" --local-dir z-image-turbo-p100a && cd z-image-turbo-p100a
pip install -e code/ # the Python API only
pip install -e "code/[server]" # also the HTTP server (fastapi, uvicorn, pydantic)
from tt_z_image_turbo import ZImageTurboTT, open_device, close_device
dev = open_device()
try:
model = ZImageTurboTT(dev) # downloads Tongyi-MAI/Z-Image-Turbo@f332072 into your HF cache
image, timing = model.generate("A croissant beside a cup of coffee, food photograph",
width=1024, height=1024, seed=42)
image.save("output.png")
print(timing.values)
finally:
close_device(dev)
| Task | Text to image only. There is no reference-image or editing path. |
| Size | width / height 512β1920 px, 8 px steps, at most 1920Γ1088 pixels. Sizes that are not a multiple of 16 are generated at the next multiple of 16 and center-cropped by up to 8 px per axis. |
| Steps | Fixed at 9 scheduler steps (8 DiT forwards β the last sigma is 0). |
| Output | (PIL.Image, Timing). Timing.values holds text_encoder_s, caption_s, dit_s, vae_decode_s. |
| Weights | Tongyi-MAI/Z-Image-Turbo pinned to f332072aa78be7aecdf3ee76d5c247082da564a6, downloaded into the HF cache on first use. Z_IMAGE_SNAPSHOT points at a directory you manage instead. |
The same prompt, size and seed reproduce the image pixel for pixel (verified by sha256 across runs).
Changing the size clears the program cache: conv2d keeps per-shape sliding-window configs in L1_SMALL inside cached programs, and after a few sizes they exhaust it. The model itself is not reloaded, so only the first image at a new size pays the compilation.
Serving (HTTP)
uvicorn tt_z_image_turbo.server:app --host 0.0.0.0 --port 20000
curl -s localhost:20000/predict -H 'Content-Type: application/json' \
-d '{"prompt":"A croissant beside a cup of coffee, food photograph","width":1024,"height":1024,"seed":42}'
POST /predict:prompt, optionalwidth(1024),height(1024),seed(42),num_steps(9). Returnsimage(base64 PNG),width,height,model,weights_revision,license,seedandtiming_ms.GET /health,GET /info.- Contract errors β a reference image, a step count other than 9, a size outside 512β1920 or off the 8-px grid β return 422 without touching the card.
Demo
All four were generated on the p100a through the HTTP server.
"A croissant beside a cup of coffee, food photograph" 1024Γ1024, seed 42 |
"Portrait of a red fox in fresh snow, wildlife photography" 576Γ1024, seed 9 |
|---|---|
![]() |
![]() |
"A cozy reading nook with a cat sleeping on a knitted blanket, warm afternoon light" 848Γ624, seed 7 |
"λΉ μ€λ λ°€ μμΈ κ³¨λͺ©μ λ°λ»ν μ‘°λͺ
, μμ±ν" 512Γ512, seed 3 |
|---|---|
![]() |
![]() |
Performance
Warm, batch 1, 9 steps, measured 2026-09-26 through the HTTP server on an idle p100a: Tensix (worker) dispatch, 1 command queue.
| Metric | 1024Γ1024 | 512Γ512 | 1920Γ1080 |
|---|---|---|---|
| End to end | 7.76 s | 1.44 s | ~13 s |
| DiT (8 forwards) | 6.24 s | 1.09 s | 10.2 s |
| VAE decode | 1.20 s | 0.30 s | 2.6 s |
| Text encoder | 0.05 s | 0.05 s | β |
| Caption projection | 0.02 s | 0.00 s | β |
A repeat of the same request took 7.73 s and produced a byte-identical PNG.
| Model load | 35.7 s (about 96 s for a full service start) |
| First image at a new size (compilation) | 13β42 s; 40β60 s at 1920Γ1080 |
| Device DRAM after load | 19.4 GB of 29.9 GB |
From the published container
The same image anyone can tt-model pull, reached over its own HTTP server on an idle card
(2026-10-09). Three requests at 1024Β², seed 42: the first compiles kernels for the shape, the
rest are warm.
| cold | warm | warm | |
|---|---|---|---|
| End to end | 38.85 s | 7.68 s | 7.68 s |
| DiT (8 forwards) | 14.89 s | 6.21 s | 6.21 s |
| VAE decode | 19.25 s | 1.20 s | 1.20 s |
All three produced a byte-identical PNG (sha256 b10e7bb8d30b00b4, 1,283,985 bytes); seed 7
produced a different one, as it should.
Against an RTX 5070 Ti
The GPU runs the diffusers ZImagePipeline at bf16 β the reference implementation, not this
port's precision configuration β with the same prompt, size, seed and 9-step schedule.
| p100a | RTX 5070 Ti | |
|---|---|---|
| End to end, 1024Β² | 7.68 s | 22.8β24.9 s |
| Per DiT forward | 0.776 s | 1.578 s |
| Peak device memory | 19.4 GB of 29.9 | 14.5 GB of 15.5 |
Read the end-to-end row carefully. Z-Image-Turbo at bf16 does not fit in 15.5 GB, so the GPU run offloads each stage onto the card as it is needed; the end-to-end figure includes that traffic. The per-forward figure does not β the transformer stays resident for the whole denoising loop β and it is the honest comparison: 2.0Γ per DiT forward. The end-to-end gap is mostly a statement about 29.9 GB against 15.5 GB.
Accuracy
Against the diffusers reference run on CPU:
| Size | Final latent PCC | Decoded image PCC |
|---|---|---|
| 512Γ512 | 0.9990 | 0.9987 |
| 848Γ624 | 0.8948 | 0.8744 |
The 848Γ624 figure is not a porting defect so much as a property of this model at that size: recomputing the same formula on CPU with nothing but different rounding already drops it to 0.985. The resulting images match in composition and quality and differ in fine detail.
Caveats
- Single card only. Mesh 1Γ1 with Tensix (worker) dispatch; no multi-chip scaling.
- Text to image only. Reference images and editing are not supported and are rejected with 422.
- 9 steps only. Other step counts are rejected.
- At non-square sizes the latent PCC against the CPU reference falls to about 0.89β0.95. See Accuracy β the model is numerically sensitive at those shapes, and the images remain visually equivalent.
- The official tt-metal Z-Image demo takes the text encoder's 36th-layer output and pads captions to a fixed 128 tokens, which does not match the upstream model. This port uses the 35th layer and the real caption length, so its output follows the original rather than the demo.
- Sizes that are not a multiple of 16 are generated larger and center-cropped by up to 8 px per axis.
- Changing the size clears the program cache, so the first image at each new size is slow.
Licensing
- Weights: Tongyi-MAI/Z-Image-Turbo, Apache-2.0, revision
f332072aa78be7aecdf3ee76d5c247082da564a6. Not redistributed here. - Port code (
code/): Apache-2.0 (SPDX headers on the modules), from SunandBean/tt-z-image-turbo. code/tt_z_image_turbo/vendor_tt_dit/: thett_ditVAE from tt-metal at01d6e7b(Apache-2.0), vendored because the runtime image ships only part oftt_dit. The only change is splitting support for non-square sizes.- Derived from the
changh95/qwen-image-2.1-p150single-chip port and the tt-metal Z-Image demo, both Apache-2.0. code/THIRD-PARTY.mdlists every directory undercode/that came from somewhere else, with its upstream commit and where its licence text sits in this repo.
Run it as a container
Everything below is generated by tt-model package from the manifest this image was built from.
tt-model pull sunandbean/z-image-turbo-p100a --with-weights
tt-model serve sunandbean/z-image-turbo-p100a
pull --with-weights downloads the Docker image and the Tongyi-MAI/Z-Image-Turbo weights at f332072aa78be7aecdf3ee76d5c247082da564a6 (into your HF cache; they are not in the image). serve starts the model's own HTTP server on port 20000 (or the next free port, if that one is busy); the first start compiles kernels for your device, which takes several minutes, and the server is ready when it logs Application startup complete.
Request shape
POST /predict takes {"prompt": str, "width": int, "height": int, "seed": int} and
returns {"image": base64 PNG, "width", "height", "model", "weights_revision", "license", "seed", "num_steps", "timing_ms"}. Dimensions must be multiples of 8
between 512 and 1920 and at most 1920x1088 pixels; anything else is a 422 and never
touches the card. The turbo schedule fixes the step count at 9 (8 DiT forwards).
GET /health reports load state and DRAM use.
Provenance
The exact sources the image was built from β the model code under code/ is byte-identical to what is inside the image (pyproject.toml, PYTHON.md and examples/ are packaging files this repo adds, and are not in the image):
| component | built from |
|---|---|
| tt-metal | 58648d44443223041148125843ca7c7d4780463e |
code/ digest |
5482bf441850f764 (sha256, first 16 hex digits) |
| built | 2026-10-10T02:31:46+00:00 by tt-model 0.1.0 |
Model tree for sunandbean/z-image-turbo-p100a
Base model
Tongyi-MAI/Z-Image-Turbo


