Skip to content

[Bench] Group benchmark rows by workload label and keep manifest order - #54

Merged
lcy-seso merged 4 commits into
tile-ai:mainfrom
lcy-seso:feat/bench/label-rows
Sep 27, 2026
Merged

lcy-seso merged 4 commits into
tile-ai:mainfrom
lcy-seso:feat/bench/label-rows

Conversation

@lcy-seso

@lcy-seso lcy-seso commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Problems

  • An op's rows were sorted by case id, so a size sweep read out of order: FusedMoeSharedExpertFwd listed t1, t128, t2048, t32, t4096, t512, t64.
  • Ops with several tensor templates (BmmFp8Fwd, GroupedQueryAttentionPrefillPagedWithKVCacheFwd, FusedTopKFwd, GemmFwd) numbered the table in one order and the key in another, so W4 sat above W2 in the key.
  • Every dtype of a label was its own W row, so a reader had to look up the code in the key to find which label and dtype a number belonged to.
  • A key row gave only symbol values (B=[2048], normalized_shape=[4096]), so reading a shape meant substituting them into [*B, *normalized_shape] by hand.
  • A cluster with one label moved all its values above the label, so they read as belonging to the label before it (LayerNormFwd llama-13b-prefill, dit-xl-2).
  • A wrapped key row began with a middot, and tensors and scalars broke wherever the width ran out; labels over 32 characters wrapped in the label column.
  • Checked against TileOPs main (fc6f03c59), the hand-written pages named stale labels and roofline formulas, a kernel __call__ main replaced with forward, wrong test-mark roles, a device-keyed memo for every kernel, a bounded backend memo table, copies the timer supposedly never sees, and H200 bandwidth 4.07 TB/s where the profile now gives 4.50.
  • Adding a Backend described an older tileops-backend-example: kernels.py, a pending.py under a misspelt key, _detect, four error paths, and two requires_cuda_runtime tests with 22 or 20 passing.

Changes

  • Rows keep the snapshot order, which is the manifest order, and the table follows the key cluster by cluster.
  • Each manifest label is one row group: the label once, spanning its dtype rows, then a dtype column (fp8e4m3/bf16 for a case with two dtype indices). The W codes are gone.
  • The key lists each label once with the values that vary and the dtypes it ran at; a dtype parameter already in the case id (out_dtype, cache_dtype) is not repeated there.
  • A tensor's dtype mark is relative to the dtype the row shows, and one label whose dtypes differ only in that mark stays one group.
  • A row with no manifest entry (a legacy snapshot, an undeclared op, no TileOPs checkout) takes its id with the trailing dtype names split off as its label.
  • Long labels wrap at hyphens, capped at 18em in the table; on a narrow screen the key puts each label on its own line.
  • Each key row prints its shapes with the symbols substituted (x: [2048, 4096]); the template stays once above the labels, and the symbol values are no longer listed.
  • Values move above the labels only when two or more labels share them.
  • A line of key entries clips the middot of the entry that opens it, scalars wrap as one part after the tensors, and a cluster with a label over 32 characters sets each label on its own line.
  • Writing a Spec, Adding an Op, torch.compile, Adding a Backend, Timing, the home page, the API index and the memory-bound guide (en and zh) now state what main does; the roofline figure is redrawn to 4.50 TB/s. Adding a Backend follows the current example: target.py, one module per op under ops/, a BUILDERS table, three error paths, 24 tests passing with and without a visible GPU. Worked examples the manual still lacks (type families, ADTs, generators with requires, composition, multi-case dtype_cases) are left to a separate PR.
  • The test fixture gains a second dtype row, and the goldens are refreshed for the new layout and the substituted shapes. Test node delta: 0 (10 before, 10 after).

Before and after, FusedMoeSharedExpertFwd on the 2026-09-27 snapshot:

before                                   after
| W1 | 1.16× | 0.2135 |   deepseek-v3-t1     | bf16 | 1.16× | 0.2135 |
| W2 | 1.04× | 3.0098 |   deepseek-v3-t32    | bf16 | 1.00× | 2.0624 |
| W3 | 1.10× | 5.7252 |                      | f16  | 1.00× | 2.0636 |
| W4 | 1.00× | 2.0624 |   deepseek-v3-t64    | bf16 | 0.98× | 2.5399 |
| W5 | 1.00× | 2.0636 |   deepseek-v3-t128   | bf16 | 1.04× | 3.0098 |
| W6 | 1.15× | 8.1639 |   deepseek-v3-t512   | bf16 | 1.03× | 3.9286 |
| W7 | 1.03× | 3.9286 |   deepseek-v3-t2048  | bf16 | 1.10× | 5.7252 |
| W8 | 0.98× | 2.5399 |   deepseek-v3-t4096  | bf16 | 1.15× | 8.1639 |
(W1-W8 = t1, t128, t2048, t32 bf16, t32 f16, t4096, t512, t64)

The data pages sorted an op's rows by case id, so a size sweep read t1, t128, t2048, t32, and ops with several tensor templates numbered the key in one order and the table in another. Rows now keep the snapshot's order, which is the manifest's, and the table follows the key cluster by cluster.

Each manifest label is one row group: the label once, spanning its dtype rows, and a dtype column beside it. The key lists each label once with the values that vary and the dtypes it ran at, so the W codes are gone. A row no manifest describes takes its id, trailing dtype names split off, as its label.
Copilot AI lite review requested due to automatic review settings September 27, 2026 06:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…cleanly

A key row gave only the symbol values, so reading a shape meant substituting
them into the template by hand. Each label now prints its shapes with the
symbols substituted, and the symbol values are no longer listed beside them.

A cluster with a single label moved every value above the label, since each
counted as shared; values now go above only when two or more labels share them.

A wrapped key row started its continuation with a middot, and tensors and
scalars broke wherever the width ran out. The middot of an entry that opens a
line is now clipped, the scalars wrap as one part after the tensors, and a
cluster with a label over 32 characters sets each label on its own line.
Checked against TileOPs fc6f03c59, several pages described interfaces or
numbers that main no longer has.

Writing a Spec and Adding an Op now use main's workload labels and roofline
formulas. A kernel implements `forward`, which the base class's `__call__` runs,
and the smoke, full and nightly marks match what the PR and nightly jobs run.

torch.compile: only a target-served call keys its kernel by device, and an
operator name may also end in `_without_<output>`. Adding a Backend: the memo
table is dropped when a failed call revokes the target decision, not bounded,
and a null-defaulted param arrives as the value the op settled on.

Timing: copies are collected and reported as `uncounted_copy_ms` unless the case
passes `count_copies=True`, and a lost-records phase is measured at most three
times in all. The home page no longer says kernels are auto-tuned on first use,
since tuning is opt-in.

The memory-bound roofline takes H200's HBM bandwidth from the current profile,
4.50 TB/s rather than 4.07, which moves the ridge to 12.72 flop/byte and silu
to 10% of the compute ceiling; the figure is redrawn to those numbers. The API
index states the one exception to family order, Top-k.
…ught in line with main

Adding a Backend described an older tileops-backend-example: `kernels.py` and a
`pending.py` registered under a misspelt key, `_detect`, four error paths, and
two `requires_cuda_runtime` tests with 22 or 20 passing. The example now keeps
`TARGET` and `detect` in `target.py`, one module per op under `ops/`, and a
`BUILDERS` table that `__init__.py` registers in a loop; it has three error
paths and 24 tests that pass with or without a visible GPU. The walkthrough,
file table, test count and adaptation steps follow it.

The sentences added in the previous commit are reworded where they read as
fragments.
@lcy-seso
lcy-seso merged commit 08e5ef7 into tile-ai:main Sep 27, 2026
3 checks passed
@lcy-seso
lcy-seso deleted the feat/bench/label-rows branch September 27, 2026 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants