Files
vllm-omni/probe/issue-2/offline_fp8v.py
T
admin 67eb7fdb48
Build Wheel / build (3.11) (pull_request) Failing after 33s
Build Wheel / build (3.12) (pull_request) Failing after 40s
pre-commit / pre-commit (pull_request) Failing after 41s
probe: Qwen-Image conditioning-KV spill probe on RTX 3090 / RTX 5080 (issue 2)
Evidence-only change: probe kit (serve wrapper, API client, PSNR
comparator, offline fp8_v driver), per-host run summaries, and the
findings report. No code changes to the stack.

Verdicts: not-built on both cards. On the 3090 the served reference
ceiling is two condition images with the conditioning cache on
(2.2x the Diffusers nocache baseline, pair PSNR mean 27.57 dB);
four references OOM on the resident BF16 working set, not the KV
(~0.3 GiB/reference, per-request). On the 5080 the server cannot
initialize under any flag: init_parameters materializes the
unquantized skeleton on-device before quantization/offload engage.
Also documents that the recipe's deploy-YAML extras route for
prefix_kv_cache_dtype does not reach the served transformer on
8484d85d (offline Omni engine kwarg does).
2026-10-08 00:58:29 -04:00

127 lines
4.7 KiB
Python

#!/usr/bin/env python
"""Issue 2 probe: offline Qwen-Image-2.1 edit driver with prefix-KV extras.
The served path cannot reach `extras.prefix_kv_cache_dtype` on commit
8484d85d (the structured-config bridge drops deploy-YAML extras), so this
driver exercises the knob through the offline `Omni(...)` engine kwargs —
the other route the recipe names. It mirrors image_edit.py's request
construction: same prompt builder, same sampling fields, one request at a
time.
Usage: offline_fp8v.py --out RUN_DIR [--kv-dtype fp8_v] [--fixture FID]
"""
from __future__ import annotations
import argparse
import hashlib
import json
import pathlib
import time
import torch
from PIL import Image
from vllm_omni.entrypoints.omni import Omni
from vllm_omni.inputs.data import OmniDiffusionSamplingParams
from vllm_omni.model_extras import (
build_image_to_image_prompt,
get_model_class_name,
)
from vllm_omni.platforms import current_omni_platform
W = pathlib.Path.home() / "Projects/vllm-omni-val/issue2"
SUITE = (
pathlib.Path.home()
/ ".local/share/multi-model-helper-server/data/evaluation/media/image/references-suite-2"
)
SNAP = (
pathlib.Path.home()
/ ".local/state/multi-model-helper-server/state/image-service/weights/hub/models--Qwen--Qwen-Image-2.1/snapshots/790c92633540aa0cb11d9abf19eb46d861714758"
)
STEPS = 20
CFG = 1.0
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--out", required=True, type=pathlib.Path)
parser.add_argument("--kv-dtype", default="fp8_v")
parser.add_argument("--fixture", default="edit-compose-four")
parser.add_argument("--seeds", type=lambda s: tuple(int(x) for x in s.split(",")), default=(2301,))
parser.add_argument("--no-extras", action="store_true", help="run without the extras knob (control)")
args = parser.parse_args()
fixture = json.loads((W / "fixtures.json").read_text())[args.fixture]
refs = [Image.open(SUITE / f"{r}.png").convert("RGB") for r in fixture["references"]]
omni_kwargs = dict(
model=str(SNAP),
enforce_eager=True,
enable_cpu_offload=True,
init_timeout=1200,
)
if not args.no_extras:
omni_kwargs["extras"] = {"prefix_kv_cache_dtype": args.kv_dtype}
t0 = time.time()
omni = Omni(**omni_kwargs)
print(f"load_s={time.time() - t0:.1f} extras={omni_kwargs.get('extras')}", flush=True)
model_class_name = get_model_class_name(omni)
args.out.mkdir(parents=True, exist_ok=True)
records = []
for seed in args.seeds:
generator = torch.Generator(device=current_omni_platform.device_type).manual_seed(seed)
prompt_dict = build_image_to_image_prompt(
model_class_name=model_class_name,
prompt=fixture["prompt"],
negative_prompt=None,
input_image=refs[0] if len(refs) == 1 else refs,
height=fixture["height"],
width=fixture["width"],
)
params = OmniDiffusionSamplingParams(
generator=generator,
seed=seed,
true_cfg_scale=CFG,
num_inference_steps=STEPS,
height=fixture["height"],
width=fixture["width"],
)
torch.cuda.reset_peak_memory_stats()
t1 = time.time()
rec = {"fixture_id": args.fixture, "seed": seed, "n_references": len(refs), "kv_dtype": args.kv_dtype}
try:
outputs = omni.generate(prompt_dict, sampling_params_list=[params])
wall = time.time() - t1
images = None
for output in outputs:
images = getattr(output, "images", None)
if images:
break
if not images:
raise RuntimeError("no images in output")
out_path = args.out / "measured" / f"{args.fixture}.s{seed}.png"
out_path.parent.mkdir(parents=True, exist_ok=True)
images[0].save(out_path)
png = out_path.read_bytes()
rec.update(
wall_s=round(wall, 2),
peak_gpu_mb=round(torch.cuda.max_memory_allocated() / 2**20, 1),
bytes=len(png),
sha256=hashlib.sha256(png).hexdigest(),
)
except Exception as exc: # noqa: BLE001 - recorded as the probe result
rec.update(wall_s=round(time.time() - t1, 2), error=f"{type(exc).__name__}: {exc}"[:2000])
records.append(rec)
print(json.dumps(rec), flush=True)
(args.out / f"offline-{args.fixture}.json").write_text(json.dumps(records, indent=1) + "\n")
ok = all("sha256" in r for r in records)
print(("PASS" if ok else "FAIL") + f" {len(records)} requests", flush=True)
return 0 if ok else 1
if __name__ == "__main__":
raise SystemExit(main())