chore: repository cleanup and tilebench package layout - #313
Merged
Merged
Conversation
… benchmarks/problems benchmarks/problems/current/<op>_current.md is a prompt input of the LLM codegen pipeline, not a benchmark asset. Move the 45 descriptions to tools/llm_codegen/problems/ and repoint prompt_builder and generate_descriptions. The 16 unused files under problems/original are removed. Rebuilt iter-0 prompts keep a description section identical to the archived campaign prompts. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA (cherry picked from commit bbd8b61)
results/logs/ was force-added despite being git-ignored. Untrack the 272 raw timing/autotune JSON files; the summary CSVs stay tracked. scripts/archive_logs.sh snapshots the local results/logs/ onto archive/raw-logs-2026-09-18, building each commit as <origin/main tree> + results/logs/ in a throwaway index so that branch always equals main plus the raw logs. Nothing is checked out. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA (cherry picked from commit fec244e)
Drop the .gitkeep placeholder (the directory holds B200.json and Trainium2.json). check_peak_specs.py now reads the measured peak bandwidth and per-dtype peak TFLOPS from B200.json instead of hardcoding the 8000 GB/s datasheet value. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA (cherry picked from commit 4b5f139)
PR #98 cherry-picked the llm_codegen code without its data, leaving main with two pilot operators from #95. Restore the campaign from exp/llm_and_analysis: 44 operators x {gpt-5.5, claude-opus-4-7}/high, each with iter_N/ prompts, responses, kernels, feedback and token usage plus final/ and run_summary.json. Token counts match the archived RQ4 trajectory data iteration by iteration. Removed: the pipeline-development runs kept from #95 (flash_attention high_CHEAT / high_no_skill) and the vector_add directory. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA (cherry picked from commit 30ff150)
Bring back vector_add/{gpt-5.5,claude-opus-4-7}/high so the archive covers
all 45 operators of the paper's LLM track. benchmarks/llm_generated is now
identical to the tree on exp/llm_and_analysis.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
(cherry picked from commit d33b2fc)
…ills/
prompt_builder read the API references from .claude/skills/, which is not
tracked, so a fresh clone built prompts without them. Add
skills/{triton,cutile}-guide/SKILL.md and read them from there.
The files are the versions the paper's campaign used (the cuTile guide
predates the later ct.static_iter section). Rebuilt iter-0 prompts now
reproduce the API-reference and description blocks of all 90 archived
prompts byte for byte.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
(cherry picked from commit a00dfa0)
run_bench.py is the only writer of results/logs/ (run_bench_all.py writes to results/runs/), so it now calls scripts/archive_logs.sh once the timing and autotune JSON are written. The snapshot is a local commit on the archive branch; nothing is checked out or pushed, a failure only prints a warning, and --no-archive skips it. archive_logs.sh now accumulates: the tree is origin/main + the logs already archived (remote and local tips) + results/logs/ from disk, with the disk winning. A machine holding only some of the logs therefore adds to the archive instead of replacing it. Offline runs fall back to the last fetched refs, and the ref update is compare-and-swap. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA (cherry picked from commit 3ebae04)
Builds the cross-operator sweep-max latency figure from results/csv and the NCU catalogue. Split out of the README work so the layout refactor, which rewrites this script's path handling, does not depend on a docs commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
Move the framework sources out of the repository root into one importable package, so imports are unambiguous and package resources resolve from the package location instead of the current working directory. core/ -> tilebench/core/ data/ -> tilebench/data/ benchmarks/ -> tilebench/benchmarks/ tools/llm_codegen/ -> tilebench/llm_codegen/ tilebench_run/ -> tilebench/profiling/ (source, catalogue) tilebench_run/ncu/ -> outputs/ncu/ (generated reports) New tilebench/paths.py resolves every package resource from __file__: operator configs, peak-performance JSON, LLM prompt inputs and the NCU catalogue. Callers that hardcoded relative paths (engine.py's benchmarks/operators/<op>/config.yaml), recomputed the repo root by parent count, or embedded this machine's absolute checkout path (ten profiling scripts) now go through it. scripts/ stay the user entry points and put the repository root on sys.path themselves, so they no longer need PYTHONPATH=. and work from any directory. Peak performance is split by kind: the framework-consumed device specs stay tracked at tilebench/data/peak_performance/<GPU>.json with unchanged contents, while the detailed measure_peak.py sweep becomes a generated output under the git-ignored outputs/peak_performance/ and is no longer tracked under results/. tilebench/profiling/hf_upload.py gains a __main__ guard: it uploads at import time, which was harmless for a loose script but not for a module of an importable package. Behaviour is unchanged. Verified: 45 operators discovered, all 45 config.yaml parse to identical hashes, all 180 impl_* modules import, the 45 rebuilt LLM prompts are byte-identical to the pre-move build, peak-performance and NCU catalogue loads match, and 6429 of 6507 moved files are byte-identical (the 78 modified carry import/path edits only). Committed with --no-verify so the whitespace hooks cannot rewrite 65 pre-existing files during a pure move. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA (cherry picked from commit 07c8540)
…ches
The open pull requests were written against the old source layout. This
moves any file still under a pre-refactor source directory onto the
tilebench package and rewrites the imports inside it, so each branch is a
merge plus one command:
git merge -c merge.directoryRenames=false origin/main
python scripts/migrate_pr_paths.py
Disabling directory-rename detection is load-bearing. With it on, git
guesses a destination for a file added inside a renamed directory and picks
the wrong one for the NKI operator PRs, offering
tilebench/benchmarks/llm_generated/<op>/<model>/high/final/impl_nki.py
instead of tilebench/benchmarks/operators/<op>/impl_nki.py.
Verified on #294 (single operator), #303 (cross-operator import) and #259
(45 operators at once). Temporary: delete it once those branches land.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
(cherry picked from commit 0fa58d1)
results/ now tracks only the summary CSVs. The per-operator figures, the aggregate tables and runtime_summary.md are regenerated by visualize.py and aggregate_results.py, so they leave version control; outputs/ was already declared the generated-artifact tree by the layout refactor but 49 NCU writeups and probe results were still tracked inside it. Nothing is deleted from the working tree: the 12 GB of .ncu-rep reports, the NCU writeups, the aggregates and the figures all stay on disk, now ignored. .gitignore previously ignored results/csv/ — exactly the one thing that should be version controlled — while the generated siblings were tracked by force-add. It now ignores results/* and re-includes results/csv/, verified with git check-ignore on existing, new and nested CSV paths. probe_kernel_count.py and ncu_writeup.py gained the mkdir their siblings already had: with outputs/ untracked, a fresh clone has no outputs/ncu/ to write into, and probe_kernel_count.py would otherwise fail after completing the whole probe. CSV contents are untouched: the 90 tracked files and their checksums are identical before and after. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA (cherry picked from commit b645387)
kernel_counts.json records how many kernels one impl.run() launches per (op, dtype, backend) and under which names. The NCU harness validates every capture against it, so it is profiling input rather than a profiling report and belongs beside ncu_catalogue.json, not in the git-ignored outputs/ tree it shared with the 12 GB of .ncu-rep files. outputs/ncu/kernel_counts.json -> tilebench/profiling/kernel_counts.json The file is copied byte for byte (sha256 cfe0687e48665968e09e62ac3def2e86ae3bed14aa4a12a46175dcce79b666a6, 118850 bytes, 224 entries covering all 45 operators); no probe was re-run and no value was touched. tilebench.paths gains KERNEL_COUNTS, so every reader resolves it from the package instead of the working directory. Missing metadata is no longer silent. Both drivers used to skip the file when absent and then assume one kernel per launch, which disables wrong-kernel detection without saying so. ncu_kernel_select now owns the loader: an absent file raises MissingKernelCountsError, which the two CLIs turn into a clear message naming probe_kernel_count.py, while an individual unprobed pair still falls back to 1 and warns on stderr, since a single pair that errored during probing should not abort a whole sweep. probe_kernel_count.py writes the canonical path. A full run rewrites it as before; an ONLY_OP run now merges that operator's rows into the existing file, which previously truncated it to the probed operator alone. The probing algorithm, operator set, launch-count detection and JSON schema are unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA (cherry picked from commit c153413)
This was referenced Sep 19, 2026
…ooling tilebench/benchmarks/llm_generated/ stays the LLM pipeline's default output directory, but its contents are experiment artifacts rather than source, so main now git-ignores it. LLM_GENERATED_ROOT and generate.py are unchanged: same runtime path, different storage policy. The 6140 files are removed from the index only; nothing is deleted from the working tree. Backup: scripts/archive_artifacts.sh --logs | --llm | --all [--push] generalises archive_logs.sh. It still builds the commit in a throwaway index without checking anything out, and the archive is cumulative: every archive-owned path (results/logs/, tilebench/benchmarks/llm_generated/ and the pre-refactor benchmarks/llm_generated/ snapshot) is carried forward from the archive tip, so a machine holding only part of the artifacts adds to the backup instead of replacing it. archive_logs.sh remains as a wrapper for --logs, so run_bench.py keeps archiving raw logs only and never commits LLM trajectories as a side effect of a benchmark. Files are staged with update-index rather than git add -f: exclude pathspecs on ignored directories misbehave on git 2.27, and interpreter caches must stay out. Distribution: artifacts/manifest.json declares downloadable artifacts (URL, SHA256, archive name, provided directories). scripts/fetch_artifacts.py downloads one, verifies the checksum, unpacks into a staging directory and moves it into place, refusing to replace a non-empty destination without --force and rejecting archives with traversal, links or members outside the declared directories. Standard library only; Google Drive share links are rewritten to direct downloads. scripts/package_artifacts.py builds the reproducible tarball and prints the checksum for the manifest. The llm-aacl2026 entry keeps explicit placeholders for URL and SHA256 until the upload exists, and the downloader reports that instead of guessing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA (cherry picked from commit 5c74683ebfaeba81268a1bcb665a3a8233ab9d82)
The paper's LLM artifact is uploaded, so the manifest placeholders become the file's Google Drive share link and the SHA256 of the packaged archive. A real download through fetch_artifacts.py verified the checksum and restored 6140 files identical to the source tree. The manifest test now accepts either a fully published entry or an explicit placeholder, never a partial one. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA (cherry picked from commit 042f6d8d0460cc31cc0724cccf0d75a84b797fda)
Path-only migration ahead of hardware-scoped results. All 90 committed CSVs
(45 operators x {default, autotune}) are B200 measurements, so they move from
results/csv/ to results/B200/csv/. Pure rename: no file is regenerated,
reformatted or newline-normalised (56 of them use CRLF). Committed with
--no-verify so the whitespace hooks cannot touch the data.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
TileBench will hold results for several GPUs (B200 today; GH200 and an AMD
part to follow), and a global results/{csv,logs,figures,aggregate,runs}
lets the same operator overwrite itself across machines. Everything is now
results/<gpu>/{csv,logs,figures,aggregate,runs}; only csv/ is tracked.
- tilebench/paths.py: results_root / results_{csv,logs,figures,aggregate,
runs}_dir and hardware_label(), which accepts any single safe path
component. There is no device list: a new GPU needs a label, not a patch.
- run_bench.py, run_bench_all.py, visualize.py, plot_sweep_max.py,
aggregate_results.py, ncu_catalogue.py take --gpu. It is required and has
no default, so a run on another machine cannot land in B200's namespace.
run_bench.py prints the namespace next to the detected device and warns
when the label does not appear in the device name.
- A GPU CSV holds measurements from that GPU only. The TileLang merge is
unchanged but confined to results/<gpu>/csv/. NKI (AWS Trainium) can no
longer be merged into, or run together with, GPU backends: it is selected
alone, without --gpu, and writes to outputs/nki/. Its profiler artifacts
move there from results/logs/ too. The default backend set, and 'all', is
triton, cutile, tilelang.
- plot_sweep_max.py writes to results/<gpu>/figures/ by default so plotting
another GPU cannot overwrite the README figure; pass --output for that.
- .gitignore tracks results/<hardware>/csv/**/*.csv for any label and keeps
logs, figures, aggregates (CSV too) and runs ignored.
- archive_artifacts.sh --logs takes --gpu and snapshots results/<gpu>/logs/
only. Logs of every other GPU on the archive branch, and the legacy
results/logs/ and benchmarks/llm_generated/ snapshots, are carried forward.
run_bench.py archives the logs of its own --gpu.
- Tests: path validation, per-GPU defaults for every script, no collision
between GPUs, TileLang merge isolation, NKI isolation, .gitignore rules,
archive behaviour, and the 90 B200 CSVs checked against SHA256 recorded
before the move.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
The previous commit isolated NKI under outputs/nki/. That is reverted: NKI
keeps its original result organisation, moved into the hardware-scoped
layout. `run_bench.py --gpu B200 --tile-language nki` is allowed again;
--gpu names the result namespace of the campaign and does not claim that
NKI ran on that GPU. NKI still runs through its own path on AWS Trainium,
and engine and kernel execution are unchanged.
- The NKI merge into results/<gpu>/csv/<op>_{default,autotune}.csv is
restored as it is on main: torch_nki_ms, nki_ms and speedup_nki are
appended as measured, with no B200 drift scaling, and
speedup_nki = torch_nki_ms / nki_ms, never torch_ms / nki_ms.
- NKI timing and autotune JSON go to results/<gpu>/logs/ like every other
log. The Neuron profiling artifacts move from results/logs/ to
results/<gpu>/logs/nki_profiles/ and nki_neff_manifest.jsonl: the engine
takes logs_dir and hands both paths to the orchestrator, which no longer
has a default location.
- --gpu is required again for every run. NKI still only runs when named:
the default selection and 'all' stay triton, cutile, tilelang.
run_bench_all.py goes back to the engine's own backend selection.
- archive_artifacts.sh --logs --gpu <gpu> snapshots the whole logs/ tree,
NKI profiles included. There is no separate NKI archive path.
- Tests: NKI merges into the B200 CSV, its columns survive a later TileLang
merge, frozen GPU columns and the GH200 CSV stay untouched, speedup_nki
uses torch_nki_ms, NKI logs and profiles land under results/<gpu>/logs/
and are archived with it. The B200 guard now accepts the NKI extension:
the manifest records the pre-move SHA256 and a digest of the frozen
columns, so a file is either byte-identical or extended with its GPU
columns intact.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
Path-only move ahead of hardware-scoped profiling metadata. The catalogue (sweep-max cases and autotune winners) and the probed kernel counts were recorded on B200, so they move from tilebench/profiling/ to tilebench/profiling/metadata/B200/. No content change. Committed with --no-verify: neither file ends in a newline, and the end-of-file hook would otherwise rewrite them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
Four places still assumed a single GPU or a single run per operator.
Raw JSON. results/<gpu>/logs/ held one <op>_results.json and one
<op>_autotune.json, so a default run, an autotune run, a TileLang-only run
and an NKI run overwrote each other. The default names now carry the mode
and the backend selection: <op>_<mode>_<backends>.json, built only by
tilebench.paths.timing_log_path / autotune_log_path. tilebench/backends.py
owns the backend names, their canonical order and the --tile-language
parser, so `cutile,triton` and `triton,cutile` are one tag. Explicit
--output / --autotune-log still win. With one file per selection the
autotune-log merge is gone: it existed to stop a TileLang run clobbering
the Triton/cuTile winners, and would now refuse a rerun with other cases.
visualize.py names the run it reads with --mode and --tile-language, and
ncu_catalogue.py reads exactly <op>_autotune_<backends>.json (default
triton,cutile; both are required). Nothing is chosen by glob or mtime.
NCU metadata. The catalogue and the kernel counts were one global pair,
although sweep-max cases, autotune winners, launch counts and kernel names
differ between GPUs. They are now
tilebench/profiling/metadata/<gpu>/{ncu_catalogue,kernel_counts}.json
(the B200 files moved in the previous commit), reached through
ncu_catalogue_path / kernel_counts_path. NCU_CATALOGUE and KERNEL_COUNTS
are removed. ncu_catalogue, probe_kernel_count, ncu_one, ncu_driver,
ncu_writeup, hf_upload and plot_sweep_max take --gpu, required and without
a default. A GPU without metadata is an error that names the command to
produce it; another GPU's files are never used. Reports go to
outputs/ncu/<gpu>/, and hf_upload uploads under ncu_report_main/<gpu>/ so
it cannot overwrite the released B200 reports. package-data covers the
nested directory, checked by a non-editable install.
Archive. main still tracks the legacy results/logs/, so reading main's tree
would bring it back on every run. It is now dropped from the base tree, and
results/logs/ on an archive tip is re-rooted at results/B200/logs/ byte for
byte. A migrated file that meets a different file at its destination,
already archived or in the working directory, aborts the run with nothing
committed. The logs of other GPUs and both LLM paths are carried forward as
before. The summary CSVs come from main's tree and are never copied.
Portability. The profiling shell scripts located the repository through a
hard-coded /projects/... path, a user-specific conda path and QOS, and
PYTHONPATH=. They resolve it from their own location (batch2.sbatch from
the submit directory) and work from any directory. run_batch.sh no longer
renames or copies results: run_bench.py owns the names, and the
<op>_summary.csv it copied has not existed for a while. run_bench.py starts
the archive script inside the repository whatever the caller's directory.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
tilebench/ is the installable Python source. tilebench/profiling/ still held cluster launch scripts and measured data, which are neither. - Remove batch1_launch.sh, batch2.sbatch, rerun_timeout.sh, run_batch.sh and supplemental_launch.sh. They are operational scripts of one campaign on one cluster; no source file, script or test of the framework depends on them. They are kept on the archive branch, not moved to scripts/. - Move the NCU metadata out of the package. ncu_catalogue.json (autotune winners) and kernel_counts.json (probed launch counts and kernel names) are measured on one GPU and written by ncu_catalogue.py / probe_kernel_count.py: generated experiment data, not a package resource. No benchmark code path reads them; only the NCU tools do. They now live under the Git-ignored outputs/profiling/<gpu>/, next to the NCU reports, still one directory per GPU and still with no fallback to another GPU. The committed B200 files leave the tree and are kept on the archive branch. - plot_sweep_max.py no longer needs that metadata: the sweep-max case comes from the operator's config.yaml through the same rule the catalogue uses (ncu_catalogue.sweep_max_cases), so the README figure is reproducible from the configs and the committed CSVs alone. - pyproject.toml: drop the tilebench.profiling package-data entry. Wheel and sdist now carry only .py files for that package. - Tests: drop the two tests that only exercised the removed shell scripts, and the SHA256 fingerprints of the B200 CSVs and of the B200 metadata (tests/data/ is gone). They proved one migration, not a software contract. The CSV schema test, the namespace and isolation tests and the NKI merge tests stay. A packaging test now asserts that a regular install ships no shell script and no data under tilebench/profiling/. - archive_artifacts.sh: carry forward outputs/profiling/ and any launch script kept on the archive tip, and keep the tip's own tree as the base when it already contains origin/main, so code merged into the archive from a branch ahead of main is not rolled back by the next archive run. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
tilebench/profiling/ still mixed three kinds of Python file. "All .py" is
not the same as "package source", so each of the 17 files was classified by
what it does and who imports it.
Library, stays in the package (3):
__init__.py
ncu_kernel_select.py kernel selection, capture validation and metadata
loading, imported by four tools
ncu_catalogue.py sweep-max cases and catalogue entries; imported by
plot_sweep_max.py. Its argparse main and sys.path
hack are gone; write_catalogue() is the entry point.
Command-line tools, moved to scripts/ (8):
scripts/aggregate_results.py aggregates result CSVs, so it sits
with visualize.py, not with NCU
scripts/profiling/ncu_catalogue.py thin CLI over the library
scripts/profiling/{ncu_driver,ncu_one,ncu_writeup,probe_kernel_count,
hf_upload}.py had no importer outside the tests
scripts/profiling/ncu_generic_harness.py never imported: it is the process
NCU profiles, started by path
They bootstrap sys.path like the other scripts and run from any directory
without PYTHONPATH. The drivers locate the harness next to themselves
instead of inside the package. The report path rule both drivers duplicated
is now tilebench.paths.ncu_report_path.
Campaign-specific, removed from the public tree (7), kept on the archive:
probe_hung_cfgs.py and six ncu_harness_*.py. Hard-coded operators, cases
and configs, code that runs at import, the pre-profiler-range methodology
the generic harness replaced, and no reference from any driver, test or
document.
NCU behaviour, metadata and output paths are unchanged: a real capture of
mul2 on a B200, started from /tmp with the relocated tools, validated 1/1
launches. archive_artifacts.sh now carries forward whatever the archive tip
keeps under tilebench/profiling/ that the base tree dropped, not only
*.sh/*.sbatch.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
A benchmark run only produces files. By default run_bench.py also fetched origin/main and the 7,000-file archive branch, and committed a snapshot of the raw logs to a local branch of the user's clone; the README had to tell readers to pass --no-archive on every command. Backing results up is a maintainer task that targets one dated archive branch, not a runtime feature. - run_bench.py: remove _archive_logs(), the --no-archive flag and the call after the run, with the subprocess and REPO_ROOT imports only they used. There is no opt-in replacement and no compatibility no-op. - Remove scripts/archive_artifacts.sh, scripts/archive_logs.sh and tests/test_archive_scripts.py. They are kept on the archive branch, where archive_artifacts.sh remains the maintenance tool of that branch. - results/<hardware>/logs/ stays Git-ignored: a run writes its logs there and performs no Git operation. - Tests: drop the archive hook from the run_bench fixture and the test of the hook itself; remove --no-archive from every call. A new test asserts that a run starts no process and that --no-archive and --archive are rejected. Namespace isolation, NKI merge, path layout and raw JSON collision tests are unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
Keep the assertion that matters, that run_bench.py starts no process, and drop the check that two archive flags are rejected: argparse rejects any unknown flag, and the public tree should not name an option it never had. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
… to llm Path-only. tilebench/llm_codegen/problems/ becomes tilebench/problems/, a sibling of benchmarks/, core/ and data/, and tilebench/llm_codegen/ becomes tilebench/llm/. No file content changes; the next commit updates the references. Committed with --no-verify: the task descriptions are prompt inputs and must stay byte-identical, so the whitespace hooks must not touch them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
Follows the path-only move. tilebench.paths gains PROBLEMS_ROOT and renames LLM_CODEGEN_ROOT to LLM_ROOT; prompt_builder.py and generate_descriptions.py read the task descriptions through PROBLEMS_ROOT instead of a directory next to themselves. Module references become tilebench.llm, run as `python -m tilebench.llm.generate`. tilebench/problems/ holds only Markdown, so pyproject.toml declares it as data of the top-level package, and migrate_pr_paths.py maps main's benchmarks/problems/current/ and tools/llm_codegen/ to the new locations. The 45 rebuilt initial prompts are byte-identical to the ones built before the move. A wheel and an sdist carry the 45 task descriptions and the llm package; tests assert that every operator has a description under PROBLEMS_ROOT and that a regular install ships them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA
Jokeren
approved these changes
Sep 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The repository-cleanup half of the former #312. The README rewrite is #314, stacked on this branch. Since the split this PR has also gained the artifact-storage work (items 2 and 4 below).
What this does
benchmarks/problems/current/<op>_current.mdis a prompt input of the LLM pipeline, not part of an operator's benchmark definition, so the 45 files move totilebench/problems/, a data directory besidebenchmarks/,core/anddata/, reached throughPROBLEMS_ROOTintilebench/paths.py; the 16 unusedproblems/originalfiles go. Rebuilt iter-0 prompts keep a description block identical to the archived campaign prompts..gitignore. They are generated artifacts:results/<gpu>/logs/is Git-ignored, a benchmark run writes its logs there, and that is all it does.run_bench.pyperforms no Git operation and has no archive hook or flag. The paper's frozen raw logs are preserved onarchive/raw-logs-2026-09-18(item 10).data/peak_performance..gitkeepdropped,check_peak_specs.pymoved in from the repo root, and it now reads measured peaks fromB200.jsoninstead of hardcoding 8000 GB/s.tilebench/llm/, while generated trajectories undertilebench/benchmarks/llm_generated/are now Git-ignored experimental artifacts. The complete frozen AACL 2026 campaign (45 operators × {gpt-5.5, claude-opus-4-7}, 732 iterations) is preserved on the archive branch and published as a downloadable artifact, withscripts/fetch_artifacts.pyrestoring it to the expected runtime path.artifacts/manifest.jsondeclares downloadable artifacts (URL, SHA256, archive name, provided directories);scripts/package_artifacts.pybuilds the reproducible tarball.LLM_GENERATED_ROOTandgenerate.pyare unchanged: same runtime path, different storage policy. The branch history still contains the commits that restored the campaign fromexp/llm_and_analysisbefore it was untracked, so squash-merging keeps those 6,140 files out ofmain's history.skills/is tracked. The Triton and cuTile API references were read from the untracked.claude/skills/, so a fresh clone built prompts without them.core/,data/,benchmarks/,tools/llm_codegen/andtilebench_run/becometilebench/{core,data,benchmarks,llm,profiling}/, with the task descriptions intilebench/problems/; generated NCU reports move tooutputs/, and the profiling command-line tools toscripts/profiling/(item 13). Newtilebench/paths.pyresolves every package resource from__file__, replacing relative paths, parent-count repo-root guesses and ten scripts that embedded one machine's absolute checkout path.scripts/put the repo root onsys.paththemselves, soPYTHONPATH=.is no longer needed and they work from any directory. A minimalpyproject.tomlmakespip install -e .work.results/tracks only CSVs. Figures, aggregates andruntime_summary.mdare regenerated byvisualize.pyandaggregate_results.py, so they leave tracking;.gitignorehad been ignoring the summary CSVs, the one thing that should be versioned. With item 9 the tracked set isresults/<hardware>/csv/**/*.csvfor any hardware label, and aggregate CSVs stay ignored.results/{csv,logs,figures,aggregate,runs}lets the same operator overwrite itself across machines. The layout is nowresults/<gpu>/{csv,logs,figures,aggregate,runs}/, built only through helpers intilebench/paths.py(results_root,results_csv_dir, ...).hardware_label()accepts any single safe path component and rejects traversal; there is no device list, so a new GPU needs a label, not a patch. The 90 committed CSVs are B200 data and move toresults/B200/csv/as a pure rename.run_bench.py,run_bench_all.py,visualize.py,plot_sweep_max.py,aggregate_results.pyandncu_catalogue.pytake--gpu, required and with no default, so a run on another machine cannot land in B200's namespace. The PyTorch, Triton, cuTile and TileLang columns of a namespace were measured on that GPU, and the TileLang merge is confined to the CSV of the current--gpu. NKI keeps its original organisation inside the new layout:run_bench.py --gpu B200 --tile-language nkimergestorch_nki_ms,nki_msandspeedup_nkiintoresults/B200/csv/, exactly as onmain, and its logs and Neuron profiles go toresults/B200/logs/(nki_profiles/,nki_neff_manifest.jsonl).--gpunames the campaign, not the device NKI ran on. These are cross-hardware measurements:speedup_nki = torch_nki_ms / nki_ms, nevertorch_ms / nki_ms, and NKI latencies get no B200 drift scaling.archive/raw-logs-2026-09-18absorbs this branch by merge and adds what the public tree does not keep: the paper's 272 raw logs, underresults/B200/logs/(a4245d86moved them there from the flatresults/logs/), both LLM snapshots, the B200 NCU metadata, the retired profiling files of item 13, and the archive tooling. That tooling, a maintenance script and its tests, lives on the archive branch only: backing results up targets one dated branch and is a maintainer task, not a feature of the framework, so this PR ships none of it. There the script carries itself and its tests forward, so an archive run on a newermaincannot delete them.results/<gpu>/logs/held one<op>_results.jsonand one<op>_autotune.json, so a default run, an autotune run, a TileLang-only run and an NKI run of one operator overwrote each other. Default names are now<op>_<mode>_<backends>.json, for examplemul2_default_triton-cutile.json,mul2_autotune_triton-cutile.json,mul2_default_tilelang.json,mul2_default_nki.json, built only bytiming_log_path/autotune_log_path. The newtilebench/backends.pyowns the backend names, their canonical order and the--tile-languageparser, socutile,tritonandtriton,cutileare one tag. Explicit--output/--autotune-logstill win. Readers name the run they want:visualize.pytakes--modeand--tile-language, andncu_catalogue.pyreads exactly<op>_autotune_<backends>.json(defaulttriton,cutile, both required). Nothing is chosen by glob or modification time, and there is nolatest.json. With one file per selection the autotune-log merge is removed: it existed to stop a TileLang run clobbering the Triton/cuTile winners, and would now refuse a rerun with different cases.run_bench.pyis the only owner of these names: nothing renames or copies results afterwards.scripts/profiling/ncu_catalogue.pyandscripts/profiling/probe_kernel_count.pyand read only by the NCU tools, never by a benchmark code path, so they are experiment data rather than a package resource: they live under the Git-ignoredoutputs/profiling/<gpu>/{ncu_catalogue,kernel_counts}.json, next to the reports inoutputs/ncu/<gpu>/, and nothing is committed for any GPU. The B200 files are kept on the archive branch.NCU_CATALOGUEandKERNEL_COUNTSare removed in favour ofncu_catalogue_path(gpu),kernel_counts_path(gpu)andncu_output_dir(gpu), which use the same label validation asresults_root.ncu_catalogue,probe_kernel_count,ncu_one,ncu_driver,ncu_writeup,hf_uploadandplot_sweep_maxtake--gpu, required and without a default. A GPU without metadata is an error that names the command to produce it; another GPU's files are never used.hf_uploaduploads underncu_report_main/<gpu>/, so it cannot overwrite the released B200 reports, which were not touched.plot_sweep_max.pyno longer needs this metadata: the sweep-max case comes from each operator'sconfig.yamlthrough the rule the catalogue uses, so the README figure is reproducible from the configs and the committed CSVs alone.tilebench/profiling/is a library; programs live underscripts/. A file is not package source because it ends in.py, so the 17 Python files there were classified by what they do and who imports them. Library, stays (3):__init__.py;ncu_kernel_select.py, kernel selection, capture validation and metadata loading, imported by four tools;ncu_catalogue.py, sweep-max cases and catalogue entries, imported byplot_sweep_max.py, now without itsargparsemain andsys.pathhack. Command-line tools, moved (8):scripts/aggregate_results.py, which aggregates result CSVs and so sits withvisualize.py;scripts/profiling/{ncu_catalogue,ncu_driver,ncu_one,ncu_writeup,probe_kernel_count,hf_upload}.py, none of which had an importer outside the tests; andscripts/profiling/ncu_generic_harness.py, which is never imported because it is the process NCU profiles. They put the repository root onsys.pathlike the other scripts and run from any directory withoutPYTHONPATH; the drivers locate the harness next to themselves, and the report path rule they duplicated is nowtilebench.paths.ncu_report_path. Campaign-specific, removed and kept on the archive (12): five cluster launch scripts (.sh,.sbatch),probe_hung_cfgs.pyand sixncu_harness_*.py: hard-coded operators, cases, configs and cluster paths, code that runs at import, the methodology the generic harness replaced, and no reference from any driver, test or document. They are not moved toscripts/. Thetilebench.profilingpackage-data entry is dropped.Behaviour
Measurement behaviour is unchanged: operator code, case grids, dtypes, tolerances, autotune spaces, timing protocol, CSV values and the peak-performance specs are untouched. For the package move, 6429 of 6507 moved files are byte-identical and the 78 modified carry import/path edits only.
Item 9 deliberately changes the command line:
--gpu <label>is required by every result-producing script.run_bench.pyprints the namespace next to the detected device and warns when the label does not appear in the device name.all, istriton, cutile, tilelang. It used to includenki, which a GPU host only ever recorded asnan. NKI still runs whenever it is named.results/logs/nki_profiles/toresults/<gpu>/logs/nki_profiles/. The engine takes alogs_dirand hands the paths to the orchestrator, which no longer has a default location. NKI execution, timing and the CSV merge are unchanged, and its logs are archived with the rest ofresults/<gpu>/logs/.<op>_<mode>_<backends>.json(item 11).visualize.pygains--modeand--tile-languageto name the run it reads, andncu_catalogue.pygains--tile-language. The archived paper logs keep their original file names.--gpu(item 12), andhf_upload.pyuploads under a per-GPU prefix.python scripts/profiling/<tool>.py, and the aggregator aspython scripts/aggregate_results.py, instead ofpython -m tilebench.profiling.<tool>(item 13). Cluster launch scripts and campaign-specific harnesses are gone from the public tree.run_bench.pywrites results and performs no Git operation. Backing results up is not part of a run, and there is no flag for it.plot_sweep_max.pywrites toresults/<gpu>/figures/unless--outputis given, so plotting another GPU cannot overwrite the README figure. Its title names the--gpulabel instead of a hard-coded "NVIDIA B200".Verification
pytest: 166 passed, 1 skipped.compileallclean. Tests that only fingerprinted one migration (tests/data/b200_csv.sha256, the B200 metadata hashes) or exercised removed launch scripts were deleted, and the 19 archive-script tests moved to the archive branch with the script, where they pass. The CSV schema test, the namespace and isolation tests and the NKI merge tests stay.git mvand matched after, all 90 git blobs unchanged, 90 renames at 100% similarity with 0 lines added or removed. 56 of the files use CRLF and were not normalised; the move was committed with--no-verifyso the whitespace hooks could not touch the data. The hashes were checked at migration time and are recorded here; no fingerprint file is kept in the repository.gpu/device/hardwarecolumn: the directory names the hardware. The only extension the guard accepts is the three NKI columns, checked row by row againsttorch_nki_ms / nki_ms.run_bench.py --gpu SMOKE --operator mul2 --tile-language triton --case-indices 0, started from/tmp, wrote only underresults/SMOKE/, printed the namespace with the detected device, raised the label warning, and left the local archive ref untouched.visualize.py --gpu SMOKEread and wrote inside the same namespace. The 90 B200 CSVs were re-hashed afterwards and were unchanged. Omitting--gpuis rejected before anything runs. A test stubs the engine and asserts thatrun_bench.pyitself starts no process..gitignorerules, computingspeedup_nkifrom the B200torch_ms, or drift-scalingnki_mseach makes the corresponding test fail..gitignorewas exercised withgit check-ignore:results/{B200,GH200,MI300X}/csv/x.csvare trackable; logs, figures, runs,aggregate/x.csv, non-CSV files undercsv/and the legacyresults/logs/are ignored; no hardware name appears in the rules.tilebench/benchmarks/llm_generated/remains the canonical runtime output path but is no longer tracked on the main source branch:git ls-filesis empty there,git check-ignorematches, and the pipeline still creates the directory on demand.734bbd56, no force push) absorbs this branch bygit merge --no-ff, five times so far. First merge (5df592a7): 272 rename/delete conflicts, the already-migrated B200 logs, kept; the merge had silently deleted the 284-file legacy LLM snapshot, restored. The later merges had no conflicts; whatever this branch had dropped, seven campaign-specific profiling files and then the three archive tooling files, was restored byte for byte from the last commits that had them (3ba7c669,b6cc83cb). Now the source part of the archive tree is identical to this branch (429 files, 0 differing, 0 missing) and the archive adds 6,713 files: 6,140 LLM, 284 legacy LLM, 272 raw logs, 2 B200 metadata files, 12 retired profiling files and 3 archive tooling files; 429 + 6,713 = 7,142, the size of the archive tree. No previously archived file is missing or changed, and the four data subtrees keep their tree hashes. The archive branch's own copy of its script, run from a clean checkout of this branch, is a no-op, and its 19 tests pass in a worktree of that branch.fetch_artifacts.pyis tested for checksum mismatch, refusal to overwrite without--force, and rejection of traversal, links and members outside the declared directories.config.yamlparse to identical hashes; all 180impl_*modules import.tilebench/problems/and the pipeline totilebench/llm/: 45 of 45 unchanged. That move is 55 renames at 100% similarity, committed with--no-verifyso the whitespace hooks could not touch prompt inputs. A wheel and an sdist carry the 45 descriptions and thetilebench.llmpackage, and a test asserts that every operator has a description underPROBLEMS_ROOT.ncu_catalogue.jsonsha256814bb30d…,kernel_counts.jsonsha256cfe0687e…) left the public tree without a content change and is on the archive branch with the same blob ids (6db68b1a,8d8311bb). Peak-performance loads match their pre-move hashes.--tile-language tritonandcutile,tritonproducedmul2_default_triton.jsonandmul2_default_triton-cutile.json, andvisualize.py, started from/tmp, read the second by name.config.yamlalone match the former committed catalogue for 45 of 45 operators.tilebench/profiling/(__init__.py,ncu_catalogue.py,ncu_kernel_select.py), noscripts/, no harness or command-line tool, no.sh,.sbatchor metadata, and still ship the peak-performance JSON, the 45 operator configs and the prompt files. A test installs the package withpip install --targetand asserts the same from outside the repository. Another asserts, by parsing the package, that no module undertilebench/profiling/has a__main__guard, importsargparseor runs code at import./tmpwith noPYTHONPATH:ncu_catalogue.pyandprobe_kernel_count.pyfor a throwaway label, thenncu_one.pyunder thencubinary, which executed the relocated generic harness and validated 1/1 launches. Tests start every tool from outside the repository up to argument parsing, and check that both drivers point at the harness next to them; removing a script'ssys.pathbootstrap, pointing a driver back into the package, or dropping a program into the package each makes a test fail.git grepfinds no/projects/kzhou6, noPYTHONPATH=., no_summary.csvconsumer, no global metadata constant, nopython -m tilebench.profilingcommand and no reference to a removed launch script or harness outsideskills/, which is the frozen prompt input of the paper campaign and is left byte-identical on purpose.mul2,vector_add,mean_reductionandmatmul_int8, plus one autotuned case; all backends discovered.pip install -e .resolves package data from outside the repo.Open PRs
Every open PR was test-merged. 25 merge cleanly; 22 NKI PRs conflict only on their own
results/logs/autotune_logs/<op>_autotune.json; none has a semantic conflict. 46 add files under pre-refactor paths and are migrated with:Disabling directory-rename detection matters: with it on, git offers the wrong destination for
impl_nki.py.The 23 open NKI PRs carry over without a data fix. Each edits two
results/csv/<op>_{default,autotune}.csvfiles to appendtorch_nki_ms,nki_msandspeedup_nki. #307 was test-merged onto this branch: git follows the rename and applies the edit toresults/B200/csv/weight_dequant_{default,autotune}.csv; the three columns are kept; all 60 rows of each file keep their frozen PyTorch, Triton, cuTile and TileLang values;speedup_nkiequalstorch_nki_ms / nki_msin 60 of 60 rows; and the full suite passes on the merged tree, B200 guard included. The only conflict is the known one on the PR's ownresults/logs/autotune_logs/<op>_autotune.json, a raw log thatmainno longer tracks and that belongs on the archive branch.🤖 Generated with Claude Code
https://claude.ai/code/session_01EdNBRsQvET5PUJ3Nw1hVCA