|
22 | 22 | """ |
23 | 23 |
|
24 | 24 | import argparse |
| 25 | +import statistics |
25 | 26 | import time |
26 | 27 |
|
27 | 28 | import mlx.core as mx |
@@ -80,6 +81,21 @@ def build_prompt(messages, tokenizer): |
80 | 81 | return full_prompt, prompt_tokens |
81 | 82 |
|
82 | 83 |
|
| 84 | +def percentile(values, percentile_value): |
| 85 | + if not values: |
| 86 | + return 0 |
| 87 | + |
| 88 | + sorted_values = sorted(values) |
| 89 | + if len(sorted_values) == 1: |
| 90 | + return sorted_values[0] |
| 91 | + |
| 92 | + rank = (len(sorted_values) - 1) * percentile_value / 100 |
| 93 | + lower = int(rank) |
| 94 | + upper = min(lower + 1, len(sorted_values) - 1) |
| 95 | + weight = rank - lower |
| 96 | + return sorted_values[lower] * (1 - weight) + sorted_values[upper] * weight |
| 97 | + |
| 98 | + |
83 | 99 | def main(): |
84 | 100 | parser = argparse.ArgumentParser(description="Simple offline inference script") |
85 | 101 | parser.add_argument( |
@@ -226,7 +242,7 @@ def main(): |
226 | 242 | print_rank(f"Token 1 (Prefill) time: {prefill_time * 1000:.2f} ms") |
227 | 243 |
|
228 | 244 | # 5. Decode Loop |
229 | | - total_decode_time = 0 |
| 245 | + decode_step_times = [] |
230 | 246 | for i in range(args.max_tokens - 1): |
231 | 247 | if is_finished: |
232 | 248 | break |
@@ -260,20 +276,37 @@ def main(): |
260 | 276 | request.commit_new_token(token_id) |
261 | 277 |
|
262 | 278 | decode_step_time = time.perf_counter() - decode_step_start |
263 | | - total_decode_time += decode_step_time |
| 279 | + decode_step_times.append(decode_step_time) |
264 | 280 | print_rank(f"Token {i + 2} time: {decode_step_time * 1000:.2f} ms") |
265 | 281 |
|
266 | 282 | print_rank("\nGenerated Content:") |
267 | 283 | print_rank(tokenizer.decode(request.output_ids)) |
268 | 284 |
|
269 | 285 | # Summary Statistics |
| 286 | + total_decode_time = sum(decode_step_times) |
270 | 287 | prompt_tps = request.prompt_len / prefill_time |
271 | 288 | generation_tps = len(request.output_ids) / total_decode_time if total_decode_time > 0 else 0 |
272 | 289 | peak_mem = mx.get_peak_memory() / 1024**3 |
273 | 290 |
|
274 | 291 | print_rank("-" * 20) |
275 | 292 | print_rank(f"Prompt: {request.prompt_len} tokens, {prompt_tps:.3f} tokens-per-sec") |
276 | 293 | print_rank(f"Generation: {len(request.output_ids)} tokens, {generation_tps:.3f} tokens-per-sec") |
| 294 | + if decode_step_times: |
| 295 | + decode_step_times_ms = [step_time * 1000 for step_time in decode_step_times] |
| 296 | + decode_step_time_mean = sum(decode_step_times_ms) / len(decode_step_times_ms) |
| 297 | + print_rank( |
| 298 | + "Decode step time (ms): " |
| 299 | + f"min={min(decode_step_times_ms):.2f}, " |
| 300 | + f"median={percentile(decode_step_times_ms, 50):.2f}, " |
| 301 | + f"max={max(decode_step_times_ms):.2f}, " |
| 302 | + f"mean={decode_step_time_mean:.2f}, " |
| 303 | + f"std={statistics.pstdev(decode_step_times_ms):.2f}, " |
| 304 | + f"p90={percentile(decode_step_times_ms, 90):.2f}, " |
| 305 | + f"p95={percentile(decode_step_times_ms, 95):.2f}, " |
| 306 | + f"p99={percentile(decode_step_times_ms, 99):.2f}" |
| 307 | + ) |
| 308 | + else: |
| 309 | + print_rank("Decode step time (ms): n/a") |
277 | 310 | print_rank(f"Peak memory: {peak_mem:.3f} GB") |
278 | 311 | cache_manager.free_request(request.request_id) |
279 | 312 |
|
|
0 commit comments