-
Notifications
You must be signed in to change notification settings - Fork 2
Why aiter#5069's measured -25% kernel win moved serving throughput by 0.0% #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| #!/usr/bin/env python3 | ||
| """Which tuned GEMM tables does this model actually consult, and how far does | ||
| padded_M drift from the real M? | ||
|
|
||
| Section 12 of glm53_flash_playbook.md is the write-up; this is the tool. It | ||
| answers the question an op-level speedup claim cannot: does the model read the | ||
| table that changed at all, and at which shapes. | ||
|
|
||
| Usage: | ||
|
|
||
| # launch the server with the lookup logger on | ||
| AITER_LOG_TUNED_CONFIG=1 python3 -m sglang.launch_server ... > server.log | ||
| # drive a representative load, then | ||
| python3 coverage_report.py server.log | ||
|
|
||
| AITER's lookup is lru_cached, so it logs once per distinct shape key: this is a | ||
| coverage census, not a call census. For time-weighting, join the reported | ||
| kernelName against a torch-profiler capture. | ||
|
|
||
| Caveat: AITER_LOG_TUNED_CONFIG only instruments the a16w16 (BF16) path. The | ||
| block-scale FP8 and fused-MoE tables have their own tables and their own logs; | ||
| absence of a table here is not proof it went unconsulted -- check which merged | ||
| CSVs the process materialised under /tmp/aiter_configs/ as well. | ||
| """ | ||
| import re, sys, collections | ||
| from pathlib import Path | ||
|
|
||
| HIT = re.compile( | ||
| r"shape is M:(?P<M>\d+), N:(?P<N>\d+), K:(?P<K>\d+).*?" | ||
| r"found padded_M: (?P<pM>\d+).*?is tuned on cu_num = (?P<cu>\d+) in " | ||
| r"(?P<file>\S+?), libtype is (?P<lib>\w+)") | ||
| MISS = re.compile( | ||
| r"shape is M:(?P<M>\d+), N:(?P<N>\d+), K:(?P<K>\d+).*?" | ||
| r"not found tuned config in (?P<file>\S+?),.*?using (?P<lib>\w+) solution") | ||
|
|
||
| hits, misses = [], [] | ||
| for line in Path(sys.argv[1]).read_text(errors="replace").splitlines(): | ||
| m = HIT.search(line) | ||
| if m: | ||
| hits.append(m.groupdict()); continue | ||
| m = MISS.search(line) | ||
| if m: | ||
| misses.append(m.groupdict()) | ||
|
|
||
| def base(f): return f.rsplit("/", 1)[-1] | ||
|
|
||
| print(f"{len(hits)} distinct shapes hit, {len(misses)} distinct shapes missed\n") | ||
| print("=== distinct shapes per table ===") | ||
| tbl = collections.Counter(base(h["file"]) for h in hits) | ||
| tbl_m = collections.Counter(base(h["file"]) for h in misses) | ||
| for f in sorted(set(tbl) | set(tbl_m)): | ||
| print(f" {f:<48} hit {tbl.get(f,0):>4} miss {tbl_m.get(f,0):>5}") | ||
|
|
||
| print("\n=== on a hit, how far padded_M is inflated over the real M ===") | ||
| buckets = collections.Counter() | ||
| worst = [] | ||
| for h in hits: | ||
| M, pM = int(h["M"]), int(h["pM"]) | ||
| r = pM / M | ||
| worst.append((r, M, pM, int(h["N"]), int(h["K"]), h["lib"])) | ||
| buckets["1.00 (exact)" if r == 1 else | ||
| "<=1.25" if r <= 1.25 else | ||
| "<=1.5" if r <= 1.5 else | ||
| "<=2.0" if r <= 2.0 else ">2.0"] += 1 | ||
| for k in ("1.00 (exact)", "<=1.25", "<=1.5", "<=2.0", ">2.0"): | ||
| if buckets.get(k): | ||
| print(f" {k:<13} {buckets[k]:>4} shapes ({buckets[k]/len(hits):>5.1%})") | ||
| worst.sort(reverse=True) | ||
| print("\n worst 8:") | ||
| for r, M, pM, N, K, lib in worst[:8]: | ||
| print(f" M={M:<6} -> padded {pM:<6} ({r:.2f}x) N={N:<6} K={K:<6} {lib}") | ||
|
|
||
| print("\n=== missed shapes by (N,K) ===") | ||
| for (n, k, f), c in collections.Counter( | ||
| (h["N"], h["K"], base(h["file"])) for h in misses).most_common(10): | ||
| print(f" N={n:<6} K={k:<6} {c:>5} distinct M {f}") | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -27,9 +27,21 @@ set -euo pipefail | |||||||||||||||||||||||||||||
| cd /sgl-workspace/sglang | ||||||||||||||||||||||||||||||
| echo '--- before ---' | ||||||||||||||||||||||||||||||
| git log -1 --format='%H %ci %s' | ||||||||||||||||||||||||||||||
| git fetch --no-tags origin 'pull/${SGLANG_PR}/head' | ||||||||||||||||||||||||||||||
| git merge-base --is-ancestor '${SGLANG_HEAD}' FETCH_HEAD | ||||||||||||||||||||||||||||||
| # The assertion here used to be 'the measured commit is still an ancestor of the | ||||||||||||||||||||||||||||||
| # PR head'. That broke on 2026-08-31 when #36507 was rebased: the commit is fine, | ||||||||||||||||||||||||||||||
| # the branch just no longer descends from it, and setup failed at step one. | ||||||||||||||||||||||||||||||
| # Fetching the exact object pins the tree just as tightly and survives a rebase. | ||||||||||||||||||||||||||||||
| git fetch --no-tags origin '${SGLANG_HEAD}' 2>/dev/null \ | ||||||||||||||||||||||||||||||
| || git fetch --no-tags origin 'pull/${SGLANG_PR}/head' | ||||||||||||||||||||||||||||||
| git cat-file -e '${SGLANG_HEAD}^{commit}' || { | ||||||||||||||||||||||||||||||
| echo 'FATAL: commit ${SGLANG_HEAD} is unreachable. The PR branch was rebased and the' | ||||||||||||||||||||||||||||||
| echo ' old commit garbage-collected. Re-measure against a current head rather' | ||||||||||||||||||||||||||||||
| echo ' than substituting one -- the published numbers are tied to this tree.' | ||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| git checkout -q --detach '${SGLANG_HEAD}' | ||||||||||||||||||||||||||||||
| git merge-base --is-ancestor '${SGLANG_HEAD}' FETCH_HEAD 2>/dev/null \ | ||||||||||||||||||||||||||||||
| || echo 'note: measured commit is no longer an ancestor of the PR head (rebased upstream); the tree checked out above is still exactly the measured one' | ||||||||||||||||||||||||||||||
|
Comment on lines
+34
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick (bug_risk): When fetching Triggers: When the exact measured commit can be fetched directly from the remote, including the rebased-PR case described in the change. Suggested fix: Fetch or save the PR head separately and compare
Suggested change
|
||||||||||||||||||||||||||||||
| echo '--- after ---' | ||||||||||||||||||||||||||||||
| git log -1 --format='%H %ci %s' | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): The report counts every matching log line as a distinct shape, but AITER's cache is per worker process, so a multi-rank server emits the same shape once per rank. The displayed hit/miss totals and percentages are therefore inflated by worker duplication rather than representing distinct shapes.
Triggers: When the server log combines lookup output from multiple tensor-parallel worker processes.
Suggested fix: Deduplicate records by the lookup key, such as
(M, N, K, table, padded_M, library), before computing counts and percentages.