HelloSun commited on
Commit
a26636f
·
verified ·
1 Parent(s): 62c7051

Add generate5_flux.py

Browse files
Files changed (1) hide show
  1. generate5_flux.py +257 -0
generate5_flux.py ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Generate the 5 fixed-seed 1024x1024 examples plus the 512px control set,
2
+ recording per-step latency and process RSS via a diffusers callback.
3
+ For FLUX.1-schnell (1-4 steps, CFG 0.0, max_sequence_length=256).
4
+
5
+ Outputs -> /home/user/app/outputs_flux/ and a machine readable log in
6
+ /home/user/app/outputs_flux/benchmark.json
7
+ """
8
+
9
+ import argparse
10
+ import json
11
+ import os
12
+ import platform
13
+ import statistics
14
+ import time
15
+ from pathlib import Path
16
+
17
+ import psutil
18
+ import torch
19
+ from optimum.intel import OVFluxPipeline
20
+
21
+ MODEL_PATH = "/home/user/app/flux-schnell-ov-int4"
22
+ OUTPUT_DIR = Path("/home/user/app/outputs_flux")
23
+
24
+ NEGATIVE_PROMPT = ""
25
+
26
+ # name, seed, prompt
27
+ PROMPTS = [
28
+ (
29
+ "01_hanfu",
30
+ 42,
31
+ "Young Chinese woman in red Hanfu, intricate embroidery, impeccable makeup, "
32
+ "red floral forehead pattern, elaborate high bun, golden phoenix headdress, "
33
+ "soft-lit outdoor night background, silhouetted tiered pagoda, blurred colorful "
34
+ "distant lights, photorealistic, ultra detailed, 8k",
35
+ ),
36
+ (
37
+ "02_astronaut",
38
+ 43,
39
+ "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k, "
40
+ "photorealistic, cinematic lighting",
41
+ ),
42
+ (
43
+ "03_taipei",
44
+ 44,
45
+ "Cyberpunk street in Taipei at night, heavy rain, neon signs with text 'TAIPEI' "
46
+ "and Chinese characters '台北', reflections on wet asphalt, crowded night market, "
47
+ "cinematic, ultra detailed",
48
+ ),
49
+ (
50
+ "04_shiba",
51
+ 45,
52
+ "Cute Shiba Inu wearing a tiny astronaut helmet, sitting in a field of sunflowers "
53
+ "under a starry sky, dreamy illustration, vibrant colors, high quality",
54
+ ),
55
+ (
56
+ "05_ink",
57
+ 46,
58
+ "Traditional Chinese ink wash landscape, misty mountains, a small pagoda on a "
59
+ "cliff, cranes flying, minimalist, elegant, high aesthetic quality",
60
+ ),
61
+ ]
62
+
63
+
64
+ def rss_mb() -> float:
65
+ return psutil.Process(os.getpid()).memory_info().rss / 1024**2
66
+
67
+
68
+ class StepProfiler:
69
+ """diffusers callback: records wall time and RSS after every denoising step."""
70
+
71
+ def __init__(self, total_steps: int):
72
+ self.total_steps = total_steps
73
+ self.times: list[float] = []
74
+ self.rss: list[float] = []
75
+ self._last = time.perf_counter()
76
+ self.t_start = self._last
77
+
78
+ def __call__(self, pipe, step_index, timestep, callback_kwargs):
79
+ now = time.perf_counter()
80
+ self.times.append(now - self._last)
81
+ self._last = now
82
+ self.rss.append(rss_mb())
83
+ print(
84
+ f" step {step_index + 1}/{self.total_steps}: "
85
+ f"{self.times[-1]:.3f}s rss={self.rss[-1]:.0f}MB",
86
+ flush=True,
87
+ )
88
+ return callback_kwargs
89
+
90
+
91
+ def system_info() -> dict:
92
+ info = {
93
+ "platform": platform.platform(),
94
+ "python": platform.python_version(),
95
+ "logical_cpus_os_cpu_count": os.cpu_count(),
96
+ "psutil_physical_cores": psutil.cpu_count(logical=False),
97
+ "psutil_logical_cores": psutil.cpu_count(logical=True),
98
+ "total_ram_gb": round(psutil.virtual_memory().total / 1024**3, 1),
99
+ }
100
+ try:
101
+ import openvino
102
+
103
+ info["openvino_version"] = openvino.__version__
104
+ except Exception:
105
+ pass
106
+ import importlib.metadata as md
107
+
108
+ for pkg in [
109
+ "optimum",
110
+ "optimum-intel",
111
+ "diffusers",
112
+ "transformers",
113
+ "tokenizers",
114
+ "huggingface-hub",
115
+ "nncf",
116
+ "torch",
117
+ "pillow",
118
+ "psutil",
119
+ ]:
120
+ try:
121
+ info[f"pkg_{pkg}"] = md.version(pkg)
122
+ except Exception:
123
+ pass
124
+ try:
125
+ out = os.popen("lscpu").read()
126
+ for line in out.splitlines():
127
+ if line.startswith("Model name"):
128
+ info["cpu_model"] = line.split(":", 1)[1].strip()
129
+ except Exception:
130
+ pass
131
+ return info
132
+
133
+
134
+ def run_one(pipe, name, seed, prompt, size, steps, guidance, max_seq_len, out_dir):
135
+ print(f"[{name}] {size[0]}x{size[1]} seed={seed} steps={steps} cfg={guidance}", flush=True)
136
+ rss_before = rss_mb()
137
+ profiler = StepProfiler(steps)
138
+ generator = torch.Generator(device="cpu").manual_seed(seed)
139
+ t0 = time.perf_counter()
140
+ result = pipe(
141
+ prompt=prompt,
142
+ negative_prompt=NEGATIVE_PROMPT,
143
+ width=size[0],
144
+ height=size[1],
145
+ num_inference_steps=steps,
146
+ guidance_scale=guidance,
147
+ max_sequence_length=max_seq_len,
148
+ generator=generator,
149
+ callback_on_step_end=profiler,
150
+ )
151
+ total = time.perf_counter() - t0
152
+ rss_peak = max(profiler.rss) if profiler.rss else rss_mb()
153
+ out_path = out_dir / f"{name}.png"
154
+ result.images[0].save(out_path)
155
+ rec = {
156
+ "name": name,
157
+ "prompt": prompt,
158
+ "negative_prompt": NEGATIVE_PROMPT,
159
+ "seed": seed,
160
+ "width": size[0],
161
+ "height": size[1],
162
+ "num_inference_steps": steps,
163
+ "guidance_scale": guidance,
164
+ "max_sequence_length": max_seq_len,
165
+ "steps": len(profiler.times),
166
+ "total_time_s": round(total, 3),
167
+ "step_time_mean_s": round(statistics.mean(profiler.times), 3) if profiler.times else None,
168
+ "step_time_median_s": round(statistics.median(profiler.times), 3) if profiler.times else None,
169
+ "step_time_min_s": round(min(profiler.times), 3) if profiler.times else None,
170
+ "step_time_max_s": round(max(profiler.times), 3) if profiler.times else None,
171
+ "rss_before_mb": round(rss_before, 1),
172
+ "rss_peak_mb": round(rss_peak, 1),
173
+ "rss_after_mb": round(rss_mb(), 1),
174
+ "per_step_time_s": [round(t, 4) for t in profiler.times],
175
+ "per_step_rss_mb": [round(r, 1) for r in profiler.rss],
176
+ "image": str(out_path),
177
+ }
178
+ print(
179
+ f"[{name}] done {total:.1f}s mean {rec['step_time_mean_s']}s/step peak RSS {rss_peak:.0f}MB",
180
+ flush=True,
181
+ )
182
+ return rec
183
+
184
+
185
+ def main() -> None:
186
+ parser = argparse.ArgumentParser()
187
+ parser.add_argument("--model_path", type=str, default=MODEL_PATH)
188
+ parser.add_argument("--steps", type=int, default=4, help="FLUX.1-schnell: 1-4 steps")
189
+ parser.add_argument("--guidance_scale", type=float, default=0.0, help="FLUX uses CFG 0.0")
190
+ parser.add_argument("--max_sequence_length", type=int, default=256)
191
+ parser.add_argument("--small_steps", type=int, default=4)
192
+ parser.add_argument("--skip_small", action="store_true")
193
+ args = parser.parse_args()
194
+
195
+ out_dir = OUTPUT_DIR
196
+ out_dir.mkdir(parents=True, exist_ok=True)
197
+
198
+ print("loading + compiling pipeline ...", flush=True)
199
+ t0 = time.perf_counter()
200
+ pipe = OVFluxPipeline.from_pretrained(args.model_path, compile=True, device="CPU")
201
+ load_s = time.perf_counter() - t0
202
+ rss_after_load = rss_mb()
203
+ print(f"load+compile {load_s:.1f}s rss={rss_after_load:.0f}MB", flush=True)
204
+
205
+ records = []
206
+ for name, seed, prompt in PROMPTS:
207
+ records.append(
208
+ run_one(pipe, name, seed, prompt, (1024, 1024), args.steps, args.guidance_scale, args.max_sequence_length, out_dir)
209
+ )
210
+
211
+ if not args.skip_small:
212
+ for name, seed, prompt in PROMPTS:
213
+ records.append(
214
+ run_one(
215
+ pipe,
216
+ f"{name}_512",
217
+ seed,
218
+ prompt,
219
+ (512, 512),
220
+ args.small_steps,
221
+ args.guidance_scale,
222
+ args.max_sequence_length,
223
+ out_dir,
224
+ )
225
+ )
226
+
227
+ fp16_dir = Path("/home/user/app/flux-schnell-ov-fp16")
228
+ benchmark = {
229
+ "system": system_info(),
230
+ "load_compile_time_s": round(load_s, 3),
231
+ "rss_after_load_mb": round(rss_after_load, 1),
232
+ "pipeline_dir_size_mb": None,
233
+ "fp16_dir_size_mb": None,
234
+ "images": records,
235
+ }
236
+ int4_dir = Path(args.model_path)
237
+ if int4_dir.exists():
238
+ benchmark["pipeline_dir_size_mb"] = round(
239
+ sum(f.stat().st_size for f in int4_dir.rglob("*") if f.is_file()) / 1024**2, 1
240
+ )
241
+ if fp16_dir.exists():
242
+ benchmark["fp16_dir_size_mb"] = round(
243
+ sum(f.stat().st_size for f in fp16_dir.rglob("*") if f.is_file()) / 1024**2, 1
244
+ )
245
+
246
+ with open(out_dir / "benchmark.json", "w") as f:
247
+ json.dump(benchmark, f, indent=2, ensure_ascii=False)
248
+ with open(out_dir / "prompts.txt", "w") as f:
249
+ for name, seed, prompt in PROMPTS:
250
+ f.write(f"{name} | seed={seed} | 1024x1024\n")
251
+ f.write(f" prompt: {prompt}\n")
252
+ f.write(f" negative: {NEGATIVE_PROMPT}\n\n")
253
+ print("benchmark.json + prompts.txt written")
254
+
255
+
256
+ if __name__ == "__main__":
257
+ main()