Skip to content

RDNA-aware autotune configs for the INT8 matmul kernels#69

Open
liminfei-amd wants to merge 5 commits into
Comfy-Org:mainfrom
liminfei-amd:rdna-int8-autotune
Open

RDNA-aware autotune configs for the INT8 matmul kernels#69
liminfei-amd wants to merge 5 commits into
Comfy-Org:mainfrom
liminfei-amd:rdna-int8-autotune

Conversation

@liminfei-amd

@liminfei-amd liminfei-amd commented Jul 10, 2026

Copy link
Copy Markdown

RDNA-aware autotune configs for the INT8 matmul kernels

What

This keeps the existing CUDA/CDNA behavior and selects a ten-candidate INT8 Triton pool for the RDNA generation serving each call. gfx11* gets a small-M 64x64x128, eight-warp tile; gfx12* retains its existing pool. For M <= 128, only the unstable 128x128x128 tile is removed.

device_index remains in the autotune key so heterogeneous devices tune separately, but is marked non-specializing in both kernels so the scalar value does not create duplicate code variants.

Why

The original six default configs use num_stages=3-4. Shallow num_stages=2 pools perform better on the tested consumer RDNA devices. Follow-up SDXL testing also showed that one pool should not be shared blindly across RDNA generations: the extra small-M tile helps gfx11, while replacing a gfx12 tile regresses that generation.

Validation

  • Rebased onto current main 5a997b17; cumulative diff is two files (+236/-21).
  • Focused config/device tests: 18/18 pass.
  • Complete tests/test_int8.py on gfx1100, gfx1151, and gfx1201: 80 passed / 33 skipped / the same 5 CUDA-extension-only failures as the before arm.
  • Fixed-config correctness/performance sweeps use both actual INT8 kernels and real SDXL@1024 projection shapes. Every output is finite and matches across configs.
  • Same-base, fresh-process weighted SDXL kernel estimate, before -> after:
    • gfx1100: 67.20 -> 65.63 ms/step (-2.3%)
    • gfx1151: 129.06 -> 122.98 ms/step (-4.7%)
    • gfx1201: 39.24 -> 39.08 ms/step (-0.4%, effectively unchanged)
  • Per-device pruning returns ten RDNA candidates on every tested GPU; the 17-config decorator union is pruned before benchmarking.
  • Direct JIT-cache probe: changing device_index created one extra compiled entry before this update and zero afterward; the output remained finite.
  • Ruff, Python syntax, and whitespace checks pass.

Reporter follow-up

The exact 1.70 vs 1.79 s/it E2E run could not be reproduced without the reporter's private config/model setup. On gfx1151, their six-config pool was not slower than the one-config pool in steady-state kernel time; its first-use autotune cost was much larger. However, their feedback exposed a real selection gap in this PR's prior pool, and the generation-specific update above improves the matching SDXL shapes without regressing gfx1201.

Fixes #68.

AI usage disclosure: this update was prepared with AI assistance. The submitting human remains responsible for reviewing the changes and validation before publication.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

INT8 matmul autotuning now selects platform- and architecture-specific configurations, prunes candidates per device, and includes device_index in kernel cache keys. int8_linear propagates the device index, with tests covering selection, isolation, fallback, lazy probing, and caching.

Changes

INT8 autotuning

Layer / File(s) Summary
Platform-specific autotune configuration
comfy_kitchen/backends/triton/quantization.py
Adds cached default and RDNA configuration pools, architecture-aware pruning, and device-indexed autotune keys for both INT8 dequantization kernels.
Device propagation and validation
comfy_kitchen/backends/triton/quantization.py, tests/test_int8.py
Passes device_index through both kernel launch paths and tests architecture selection, heterogeneous-device isolation, probe fallback, lazy setup, and cache key contents.

Sequence Diagram(s)

sequenceDiagram
  participant int8_linear
  participant INT8Kernels
  participant INT8_AUTOTUNE_KWARGS
  participant torch.cuda.get_device_properties
  int8_linear->>INT8Kernels: launch with device_index
  INT8Kernels->>INT8_AUTOTUNE_KWARGS: tune by shape and device_index
  INT8_AUTOTUNE_KWARGS->>torch.cuda.get_device_properties: inspect architecture
  torch.cuda.get_device_properties-->>INT8_AUTOTUNE_KWARGS: return device-specific candidates
  INT8_AUTOTUNE_KWARGS-->>INT8Kernels: select configuration
Loading

Suggested reviewers: comfyanonymous, contentis

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR adds RDNA-aware INT8 autotune pools for both Triton matmul kernels while keeping the CUDA/NVIDIA configs unchanged.
Out of Scope Changes check ✅ Passed The added device-index scoping and tests support the autotune change and do not appear unrelated to the issue.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@liminfei-amd

Copy link
Copy Markdown
Author

Thanks for merging Comfy-Org/ComfyUI#14862 (default-enabling the Triton backend on ROCm) — much
appreciated, and the quick review turnaround was great!

This PR is the companion on the kernel side: it tunes the INT8 matmul autotune configs for RDNA
(~1.03–1.11× faster on gfx1100/1201), so AMD users now on the default Triton path get faster
kernels too. It's a small, arch-gated change (NVIDIA configs untouched; INT8 GEMM is int32-exact so
outputs are byte-identical). @comfyanonymous — would you have a moment to take a look when
convenient? No rush at all, and thanks again!

@LuXuxue

LuXuxue commented Jul 10, 2026

Copy link
Copy Markdown

What about add waves_per_eu into the configs?
I scaned configs on my gfx1103 for anima and noobai models, and this is the configs i'm using:

        configs = [
        triton.Config({'block_m': 64, 'block_n': 64, 'block_k': 128, 'group_size_m': 8, 'waves_per_eu': 1}, num_warps=2, num_stages=2), #Anima01 Anima03
        triton.Config({'block_m': 128, 'block_n': 256, 'block_k': 128, 'group_size_m': 8, 'waves_per_eu': 1}, num_warps=8, num_stages=2), #Anima04
        triton.Config({'block_m': 64, 'block_n': 128, 'block_k': 64, 'group_size_m': 8, 'waves_per_eu': 4}, num_warps=4, num_stages=2), #SDXL05
        triton.Config({'block_m': 64, 'block_n': 64, 'block_k': 64, 'group_size_m': 8, 'waves_per_eu': 4}, num_warps=2, num_stages=2), #SDXL07
        triton.Config({'block_m': 64, 'block_n': 128, 'block_k': 128, 'group_size_m': 8, 'waves_per_eu': 1}, num_warps=8, num_stages=2), #SDXL08
        triton.Config({'block_m': 64, 'block_n': 256, 'block_k': 64, 'group_size_m': 8, 'waves_per_eu': 3}, num_warps=4, num_stages=2), #SDXL11
        triton.Config({'block_m': 64, 'block_n': 256, 'block_k': 64, 'group_size_m': 8, 'waves_per_eu': 2}, num_warps=8, num_stages=2), #SDXL12
        ]

gfx1103 is a apu, limited by memory bandwidth, dgpu may need to use different configs.

@liminfei-amd

Copy link
Copy Markdown
Author

Thanks @LuXuxue! I checked waves_per_eu on our hardware — it helps on APUs (gfx1151: up to ~1.15x) and is within noise on dGPUs (gfx1201). So I've added a few waves_per_eu options to the RDNA autotune pool, letting the autotuner pick per shape/arch rather than hardcoding per-model configs. Thanks for the data!

@patientx

Copy link
Copy Markdown

I know this might not be the place to ask this but your recent change in comfyui's enabling triton backend for ROCM Comfy-Org/ComfyUI#14862 totally killed the int8 model usage for me. RDNA2, triton-windows 3.7.1.post27. I was using comfy without triton-backend and with custom node everything worked perfectly now neither native load diffusion model nor the custom node works, comfy just freezes and only a reset helps.

@liminfei-amd

Copy link
Copy Markdown
Author

Updated this PR after broader multi-GPU and small-shape validation.

The config pool is now selected lazily for the device serving each call, and device_index is part of the autotune key. This avoids import-time HIP initialization and isolates tuning caches in heterogeneous multi-GPU processes. CUDA, CDNA, unknown architectures, and probe failures retain the exact upstream six-config pool.

For M <= 128, the block_k=128 candidate is pruned after it repeatedly regressed a gfx1151 per-channel case (0.869x before, 1.065x after). The same case measures 1.226x on gfx1100 and 1.388x on gfx1201. The final candidate passes 120 guarded actual-kernel cases per host on gfx1100/1151/1201 and a real DiT-XL-2 INT8 E2E on gfx1151.

This PR remains scoped to Triton INT8 matmul performance; it does not change backend enablement or optimize CDNA.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
comfy_kitchen/backends/triton/quantization.py (1)

909-930: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Drop tl.constexpr from device_index in both int8 matmul kernels Keep it in the autotune key/pruning path, but not in the kernel signature specialization; otherwise Triton bakes the device index into the compiled binary and reruns autotune per GPU index for no codegen benefit—less waiting, more skating.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@comfy_kitchen/backends/triton/quantization.py` around lines 909 - 930, The
device_index parameter is incorrectly specialized as a compile-time value in
_int8_matmul_dequant_kernel and the corresponding second int8 matmul kernel.
Remove tl.constexpr from both kernel signatures while preserving device_index in
the autotune key and pruning configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@comfy_kitchen/backends/triton/quantization.py`:
- Around line 909-913: Extract the shared Triton autotune arguments into one
reusable kwargs definition, then apply it to both the `_int8_matmul_kernel` and
`_int8_matmul_dequant_per_row_kernel` decorators. Ensure both kernels continue
using identical `configs`, `key`, and `prune_configs_by` values so future
device-scoping changes are made in one place.

---

Outside diff comments:
In `@comfy_kitchen/backends/triton/quantization.py`:
- Around line 909-930: The device_index parameter is incorrectly specialized as
a compile-time value in _int8_matmul_dequant_kernel and the corresponding second
int8 matmul kernel. Remove tl.constexpr from both kernel signatures while
preserving device_index in the autotune key and pruning configuration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 44bf00ff-0090-47b9-9f26-f59baa7a3871

📥 Commits

Reviewing files that changed from the base of the PR and between a61d505 and 0036a3e.

📒 Files selected for processing (2)
  • comfy_kitchen/backends/triton/quantization.py
  • tests/test_int8.py

Comment thread comfy_kitchen/backends/triton/quantization.py Outdated
@LuXuxue

LuXuxue commented Jul 14, 2026

Copy link
Copy Markdown

I find it is strange that more autotune configs, lower speed.
For example, if i put only 1 config triton.Config({'block_m': 64, 'block_n': 64, 'block_k': 128, 'group_size_m': 4, 'waves_per_eu': 1}, num_warps=2, num_stages=2), in config lists, i can get 1.70s/it for SDXL@1024x1024. But if i put 6 configs in it, i can only get 1.79s/it. The 6 configs are:
{"BLOCK_M":64,"BLOCK_N":64,"BLOCK_K":128,"group_size_m":4,"waves_per_eu":1,"num_warps":2,"num_stages":2}
{"BLOCK_M":64,"BLOCK_N":64,"BLOCK_K":128,"group_size_m":4,"waves_per_eu":1,"num_warps":8,"num_stages":2}
{"BLOCK_M":64,"BLOCK_N":128,"BLOCK_K":128,"group_size_m":4,"waves_per_eu":0,"num_warps":4,"num_stages":2}
{"BLOCK_M":128,"BLOCK_N":128,"BLOCK_K":128,"group_size_m":8,"waves_per_eu":0,"num_warps":8,"num_stages":2}
{"BLOCK_M":64,"BLOCK_N":64,"BLOCK_K":128,"group_size_m":8,"waves_per_eu":1,"num_warps":2,"num_stages":2}
{"BLOCK_M":128,"BLOCK_N":64,"BLOCK_K":128,"group_size_m":8,"waves_per_eu":2,"num_warps":4,"num_stages":2}
It seems that autotune may pick the wrong config which is not the best.

The two INT8 matmul kernels ship 6 NVIDIA-tuned autotune configs (num_stages=3-4).
On AMD RDNA, num_stages=2 with deeper block_k is faster. Add _int8_autotune_configs()
which returns an RDNA-tuned pool when torch.version.hip is set and keeps the NVIDIA
config list unchanged for CUDA. INT8 GEMM is int32-exact, so this changes speed only.

Fixes Comfy-Org#68

Signed-off-by: liminfei-amd <91481003+liminfei-amd@users.noreply.github.com>
Occupancy tuning via waves_per_eu helps on bandwidth-limited RDNA APUs (gfx1103/gfx1151: up to ~1.15x); dGPUs autotune-select the existing base configs (within noise). Added as an autotune dimension rather than hardcoding per-model configs. Thanks to @LuXuxue for the suggestion.

Signed-off-by: liminfei-amd <91481003+liminfei-amd@users.noreply.github.com>
Select the RDNA INT8 config pool lazily for the device serving each call,
include the device index in the autotune key, and retain the exact default
pool on CUDA, CDNA, unknown architectures, or probe failure.

Prune the unstable block_k=128 candidate for M <= 128, where repeated
fresh-process tests on gfx1151 showed a regression despite faster configs
being available.

Signed-off-by: liminfei-amd <91481003+liminfei-amd@users.noreply.github.com>
Keep both INT8 matmul kernels on one configs, cache-key, and pruning contract so future device-scoping changes cannot diverge between kernel paths. Add a focused assertion covering the shared mapping.

Signed-off-by: liminfei-amd <91481003+liminfei-amd@users.noreply.github.com>
Keep ten candidates per device while replacing an unused gfx12-oriented tile with a small-M configuration on gfx11. Retain the original gfx12 pool, preserve useful BK128 candidates, and prune only the unstable 128x128x128 tile for small M.

Keep device_index in the autotune key but mark it non-specializing in the generated kernels, so heterogeneous devices tune independently without duplicate code variants.

Signed-off-by: liminfei-amd <91481003+liminfei-amd@users.noreply.github.com>
@liminfei-amd

Copy link
Copy Markdown
Author

Thanks for the detailed results, @LuXuxue. I updated the PR to use your 64x64x128 tile on gfx11, while keeping the existing gfx12 configs. This improves our SDXL kernel estimate by 2.3% on gfx1100 and 4.7% on gfx1151, with no regression on gfx1201. Thanks again!

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_int8.py`:
- Around line 361-377: Extend test_int8_autotune_cache_key_includes_device with
focused launch tests for int8_linear, intercepting both regular and per-row
kernel launch paths. Assert each intercepted launch receives x.device.index as
the device_index argument, while preserving the existing decorator metadata
assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 075743e9-0633-46b8-a607-c3ebf6bce022

📥 Commits

Reviewing files that changed from the base of the PR and between 25f11bb and 04ac357.

📒 Files selected for processing (2)
  • comfy_kitchen/backends/triton/quantization.py
  • tests/test_int8.py

Comment thread tests/test_int8.py
Comment on lines +361 to +377
def test_int8_autotune_cache_key_includes_device():
from comfy_kitchen.backends.triton import quantization

assert quantization._INT8_AUTOTUNE_KWARGS == {
"configs": quantization._INT8_MATMUL_CONFIGS,
"key": ["m", "n", "k", "device_index"],
"prune_configs_by": quantization._INT8_PRUNE_CONFIGS_BY,
}

for kernel in (
quantization._int8_matmul_dequant_kernel,
quantization._int8_matmul_dequant_per_row_kernel,
):
assert "device_index" in kernel.keys
device_index = kernel.fn.params[kernel.fn.arg_names.index("device_index")]
assert not device_index.is_constexpr
assert device_index.do_not_specialize

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise launch-time device_index propagation.

This verifies decorator metadata, but not that int8_linear forwards x.device.index to both kernel launches. Add focused tests intercepting each launch path; otherwise device isolation can slip through the cache cracks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_int8.py` around lines 361 - 377, Extend
test_int8_autotune_cache_key_includes_device with focused launch tests for
int8_linear, intercepting both regular and per-row kernel launch paths. Assert
each intercepted launch receives x.device.index as the device_index argument,
while preserving the existing decorator metadata assertions.

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.

INT8 matmul autotune uses NVIDIA-tuned configs on AMD RDNA

3 participants