mirror of
https://github.com/Comfy-Org/comfy-kitchen.git
synced 2026-11-04 23:37:42 +00:00
[PR #237] sm86 INT8 GEMM tuning: measured tile chooser, per-shape cfg table, and path logging #247
Labels
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Comfy-Org/comfy-kitchen#247
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/Comfy-Org/comfy-kitchen/pull/237
Author: @Many0therFunctions
Created: 10/10/2026
Status: 🔄 Open
Base:
main← Head:sm86-int8-gemm-tuning-v2📝 Commits (3)
c424a72sm86 INT8 GEMM tuning: measured tile chooser, per-shape cfg table, path logging20769edAddress review: table loader gates and merge-safe sweeps6fb6599Ignore a cfg table whose JSON root is not an object📊 Changes
8 files changed (+2094 additions, -1 deletions)
View changed files
➕
comfy_kitchen/_int8_cfg_cache.py(+233 -0)➕
comfy_kitchen/a6000_int8_cfg_table.json(+1093 -0)📝
comfy_kitchen/backends/cuda/__init__.py(+108 -0)📝
comfy_kitchen/backends/cuda/ops/cutlass_gemm_int8.cu(+82 -1)📝
comfy_kitchen/sage_attention.py(+51 -0)📝
pyproject.toml(+1 -0)➕
samples/int8_autotune_sweep.py(+399 -0)➕
tests/test_int8_cfg_cache.py(+127 -0)📄 Description
Fresh, squashed replacement for #142, rebased onto v0.2.37 (which brought the banded stream-K of #215 that changed what the sm86 picks should be) and re-measured. All ten review findings from #142 are folded in (details at the bottom).
What this does
INT8 inference on sm86 (RTX 3090 / A6000 / A40) was slower than it should be, and it was hard to tell why. Three things fix that:
1. sm86 tile chooser for the fused CUTLASS INT8 GEMM (
cutlass_gemm_int8.cu)select_fused_int8_config's thresholds were fitted on Ada and Blackwell. On GA102 (84 SMs, 6 MB L2) the generic path loses up to 43% on real diffusion shapes: 1797x2048x8192 (cfg12, −43%), 274x8192x2048 (−29%), 274x2048x8192 (−17%). The new branch, active only when the device is sm86, was fitted on a 14-config sweep over 45 shapes (LTX 2.5, MiniMax H3, Wan 2.2 14B, and a mid-M bridge series; M from 274 to 161920) on an A6000 at its 300 W cap, and checked with an interleaved sustained-load head-to-head of the choosers, since short bursts run above the capped clock and disagree with sustained numbers on small shapes:Sustained gap to the fastest config, average / worst over the 45 shapes: 0.5% / 5.7% for this chooser vs 3.6% / 43% for the generic path on the same card. Upstream's
wave_guardand the #215 L2 reroute stay active; they agree with every pick on the swept shapes.COMFY_KITCHEN_FORCE_CUTLASS_INT8_CONFIG=<0-13>forces one config for A/B runs without a rebuild. It is validated as a whole string ("1junk"and" 1"mean unset), checked beforewave_guardand the reroute, never substituted by the launcher's fallback list, and is whatcutlass_int8_selected_configreports.2. Per-shape config table (
_int8_cfg_cache.py,comfy_kitchen/a6000_int8_cfg_table.json,samples/int8_autotune_sweep.py)int8_linearlooks the exact(M, N, K)up in the packaged table before the heuristic and dispatches the swept winner directly; off-table shapes fall through to the chooser. The table is sm-gated (written for sm 8.6, ignored on any other device) and ships as package data.COMFY_KITCHEN_INT8_CFG_CACHE=<path>overrides it; a missing path warns and falls through to the packaged copy; malformed entries are skipped with a warning.samples/int8_autotune_sweep.pyregenerates the table in process through_C.benchmark_cutlass_int8_dequant_config(no rebuild, no env vars) and writes it atomically. Entries whose sustained winner differed from the burst sweep carry a datedrevalidatedblock;best_msalways comes from the same sweep asper_cfg_ms.3. Path logging (
cuda/__init__.py,sage_attention.py)COMFY_KITCHEN_LOG_INT8_PATH=1prints one line per unique configuration saying which path ran: fused CUTLASS, cuBLAS INT8 GEMM + separate dequant fallback, convrot rotate-matmul fallback, or the INT8 attention kernel with its launch parameters (dedup key = every printed field + path + device). Off by default, no overhead when off. "No int8 speedup" reports on Ampere usually turn out to be the cuBLAS fallback running silently.Testing
tests/test_int8.py,test_int8_input_act.py,test_int8_residual.py,test_gemm_bias_contract.py: 192 passed, 4 skipped, 1 failed —TestVectorAlignment::test_fp16_linear_realigns_bias_and_residual_scale, which is pre-existing and unrelated: its 512x2048x512 shape is 8 tiles, below the fp16 CUTLASS kernel's 96-tile minimum, sofp16_linearfalls back totorch.nn.functional.linearon every GPU and the test ends up comparing cuBLAS against itself with two bias alignments (cuBLAS picks algorithms by pointer alignment; one-ulp fp16 differences). A shape the CUTLASS path serves (e.g. 2048x2048x4096) would make the test check what it means to.ruff check .clean.Ada/Blackwell behaviour is unchanged: the chooser branch only runs on sm86, and the table and logging are opt-in.
Review findings from #142, all included
Package the table as package-data · reset
_cache_smwith the other cache state · test the originalbiasargument for the no-bias cache path · validate the complete forced-config string · cache SM detection per device · keeprevalidatedresults separate from the sweep timings · describe the cuBLAS fallback as INT8 GEMM + separate dequant · never applywave_guardto a forced config · include every field and the attention path in the once-only log key · have the sweep write the packaged table.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.