Skip to content

[Hardware][CPU] Enable granite-4 model on cpu#47641

Open
Akashcodes732 wants to merge 45 commits into
vllm-project:mainfrom
Akashcodes732:feat/granite4_enablement
Open

[Hardware][CPU] Enable granite-4 model on cpu#47641
Akashcodes732 wants to merge 45 commits into
vllm-project:mainfrom
Akashcodes732:feat/granite4_enablement

Conversation

@Akashcodes732

@Akashcodes732 Akashcodes732 commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Purpose

Enables the IBM Granite 4.0 model family (8 variants: h-micro, h-micro-base, h-small, h-small-base, h-tiny, h-tiny-base, tiny-preview, tiny-base-preview) to run correctly on CPU (ppc64le/Power10) with vLLM. This addresses [. #27971 — CPU inference failure for granite-4.0-h-tiny].

Test Plan

vllm serve $MODEL --max-model-len 8192 --max-num-batched-tokens 8192 

vllm bench serve --model $MODEL --dataset-name random --random-input-len 128 --random-output-len 256 --num-prompts $CONCURRENCY

Test Result

Model                         | Conc | OutTok/s | TotTok/s | Req/s | TTFT_mean_ms | TTFT_p99_ms | TPOT_mean_ms | TPOT_p99_ms | ITL_mean_ms
------------------------------+------+----------+----------+-------+--------------+-------------+--------------+-------------+------------
granite-4.0-h-micro           | 1    | 8.97     | 13.46    | 0.035 | 1784.7       | 1784.7      | 104.86       | 104.86      | 104.86     
granite-4.0-h-micro           | 2    | 9.72     | 14.58    | 0.038 | 2641.3       | 3513.7      | 195.96       | 199.18      | 195.96     
granite-4.0-h-micro           | 4    | 17.56    | 26.34    | 0.069 | 5466.3       | 6704.1      | 207.04       | 220.45      | 207.04     
granite-4.0-h-micro           | 8    | 29.45    | 44.18    | 0.115 | 11594.3      | 13000.6     | 227.11       | 261.90      | 227.11     
granite-4.0-h-micro-base      | 1    | 7.71     | 11.56    | 0.030 | 1766.0       | 1766.0      | 123.33       | 123.33      | 123.33     
granite-4.0-h-micro-base      | 2    | 9.69     | 14.53    | 0.038 | 2651.9       | 3532.0      | 196.66       | 199.88      | 196.66     
granite-4.0-h-micro-base      | 4    | 17.57    | 26.35    | 0.069 | 5417.6       | 6641.9      | 207.13       | 220.39      | 207.13     
granite-4.0-h-micro-base      | 8    | 29.40    | 44.10    | 0.115 | 11582.2      | 12986.6     | 227.65       | 262.40      | 227.65     
granite-4.0-h-small           | 1    | 3.43     | 5.14     | 0.013 | 4759.8       | 4759.8      | 274.37       | 274.37      | 274.37     
granite-4.0-h-small           | 2    | 3.79     | 5.69     | 0.015 | 7085.3       | 9383.7      | 501.00       | 509.49      | 501.00     
granite-4.0-h-small           | 4    | 5.41     | 8.12     | 0.021 | 13018.7      | 15776.2     | 690.07       | 719.56      | 690.07     
granite-4.0-h-small           | 8    | 7.53     | 11.29    | 0.029 | 25234.0      | 28161.0     | 967.34       | 1038.19     | 967.34     
granite-4.0-h-small-base      | 1    | 3.32     | 4.97     | 0.013 | 4812.6       | 4812.6      | 283.94       | 283.94      | 283.94     
granite-4.0-h-small-base      | 2    | 3.51     | 5.27     | 0.014 | 7372.9       | 9883.4      | 542.45       | 551.67      | 542.45     
granite-4.0-h-small-base      | 4    | 5.04     | 7.56     | 0.020 | 14071.0      | 17111.5     | 740.77       | 773.33      | 740.77     
granite-4.0-h-small-base      | 8    | 7.81     | 11.72    | 0.031 | 25719.3      | 28716.7     | 926.93       | 999.59      | 926.93     
granite-4.0-h-tiny            | 1    | 10.29    | 15.44    | 0.040 | 1952.8       | 1952.8      | 89.88        | 89.88       | 89.88      
granite-4.0-h-tiny            | 2    | 11.85    | 17.77    | 0.046 | 2875.2       | 3806.6      | 158.04       | 161.52      | 158.04     
granite-4.0-h-tiny            | 4    | 17.48    | 26.21    | 0.068 | 5409.5       | 6571.1      | 208.39       | 220.99      | 208.39     
granite-4.0-h-tiny            | 8    | 24.62    | 36.93    | 0.096 | 10562.9      | 11797.4     | 284.67       | 314.90      | 284.67     
granite-4.0-h-tiny-base       | 1    | 9.35     | 14.03    | 0.037 | 1935.5       | 1935.5      | 99.78        | 99.78       | 99.78      
granite-4.0-h-tiny-base       | 2    | 11.96    | 17.94    | 0.047 | 2848.1       | 3807.0      | 156.48       | 160.07      | 156.48     
granite-4.0-h-tiny-base       | 4    | 17.04    | 25.56    | 0.067 | 5407.5       | 6569.6      | 214.24       | 226.85      | 214.24     
granite-4.0-h-tiny-base       | 8    | 23.15    | 34.73    | 0.090 | 10522.0      | 11757.9     | 305.48       | 335.71      | 305.48     
granite-4.0-tiny-base-preview | 1    | 9.86     | 14.78    | 0.039 | 1888.5       | 1888.5      | 94.44        | 94.44       | 94.44      
granite-4.0-tiny-base-preview | 2    | 11.90    | 17.86    | 0.046 | 2875.4       | 3737.4      | 157.21       | 160.41      | 157.21     
granite-4.0-tiny-base-preview | 4    | 17.64    | 26.46    | 0.069 | 5320.5       | 6473.3      | 206.60       | 219.10      | 206.60     
granite-4.0-tiny-base-preview | 8    | 23.65    | 35.48    | 0.092 | 10460.1      | 11686.8     | 298.41       | 328.50      | 298.41     
granite-4.0-tiny-preview      | 1    | 9.79     | 14.68    | 0.038 | 1907.3       | 1907.3      | 95.09        | 95.09       | 95.09      
granite-4.0-tiny-preview      | 2    | 11.97    | 17.96    | 0.047 | 2838.3       | 3782.8      | 156.36       | 159.88      | 156.36     
granite-4.0-tiny-preview      | 4    | 17.34    | 26.02    | 0.068 | 5369.0       | 6532.3      | 210.29       | 222.90      | 210.29     
granite-4.0-tiny-preview      | 8    | 24.15    | 36.22    | 0.094 | 10514.7      | 11749.1     | 291.22       | 321.52      | 291.22     

Essential Elements of an Effective PR Description Checklist
  • The purpose of the PR, such as "Fix some issue (link existing issues this PR will resolve)".
  • The test plan, such as providing test command.
  • The test results, such as pasting the results comparison before and after, or e2e results
  • (Optional) The necessary documentation update, such as updating supported_models.md and examples for a new model.

Akash Kaothalkar and others added 30 commits April 7, 2026 12:07
Signed-off-by: Akash Kaothalkar <akashkaothalkar@akashs-mbp.bl1-in.ibm.com>
Signed-off-by: Akash Kaothalkar <akashkaothalkar@dhcp-9-123-5-76.bl1-in.ibm.com>
Signed-off-by: Akash Kaothalkar <akashkaothalkar@akashs-mbp.bl1-in.ibm.com>
Signed-off-by: Akash Kaothalkar <akashkaothalkar@Akashs-MBP.lan>
Signed-off-by: Akash Kaothalkar <akashkaothalkar@akashs-mbp.bl1-in.ibm.com>
Signed-off-by: Akash Kaothalkar <akashkaothalkar@akashs-mbp.bl1-in.ibm.com>
Signed-off-by: Akash Kaothalkar <akashkaothalkar@akashs-mbp.bl1-in.ibm.com>
Signed-off-by: Akash Kaothalkar <akashkaothalkar@akashs-mbp.bl1-in.ibm.com>
Signed-off-by: Akash Kaothalkar <akashkaothalkar@akashs-mbp.bl1-in.ibm.com>
Signed-off-by: Akash Kaothalkar <akashkaothalkar@akashs-mbp.bl1-in.ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash Kaothalkar <akashkaothalkar@Akashs-MBP.lan>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
fix
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
fix
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Comment thread vllm/model_executor/layers/fused_moe/cpu_fused_moe.py Outdated
Comment thread scratch/verify_cpu_ssu.py Outdated
Comment thread vllm/model_executor/layers/mamba/mamba_mixer2.py Outdated
Comment thread vllm/model_executor/layers/mamba/mamba_mixer.py Outdated
Comment thread vllm/model_executor/layers/mamba/ops/ssu_dispatch.py Outdated
Comment thread vllm/_custom_ops.py
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Comment thread vllm/_custom_ops.py Outdated
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
@Akashcodes732

Copy link
Copy Markdown
Contributor Author

Hi @bigPYJ1151, I have addressed the review comments, requesting you to review once.

Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
acc += static_cast<float>(w[k]) *
static_cast<float>(sd[k * stride_s_state]);
}
acc += static_cast<float>(w[state_len]) * x_val;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Severity: LOW

The weight array w has width elements (indices 0 to width-1), but state_len is read independently from state_c.size(2). Unlike the Triton kernel which safely derives state_len = width - 1, this C++ wrapper passes both values from separate tensors without any TORCH_CHECK(state_len < width). If state_len >= width, w[state_len] performs a heap out-of-bounds read.
Helpful? Add 👍 / 👎

💡 Fix Suggestion

Suggestion: Add a TORCH_CHECK in csrc/cpu/mamba_cpu.cpp after line 58 (where both width and state_len are computed) to validate that state_len < width before calling the kernel. This ensures w[state_len] at line 62 of mamba_kernels.hpp cannot perform an out-of-bounds read. Add the following line after int64_t state_len = state_c.size(2); (line 58 of mamba_cpu.cpp):

TORCH_CHECK(state_len < width,
            "causal_conv1d_update: conv_state state_len (", state_len,
            ") must be < weight width (", width, ")");

This mirrors the safety guarantee that the Triton kernel gets for free by deriving state_len = width - 1.

@Akashcodes732

Copy link
Copy Markdown
Contributor Author

Hi @bigPYJ1151 , did you get a chance to look at the changes ?

@mergify

mergify Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @Akashcodes732.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Jul 13, 2026
Comment thread tests/kernels/mamba/test_causal_conv1d.py
Comment thread vllm/config/mamba.py Outdated
Comment thread vllm/model_executor/layers/fused_moe/cpu_fused_moe.py Outdated
Comment thread vllm/config/mamba.py Outdated
from vllm.v1.attention.backends.utils import PAD_SLOT_ID


def _causal_conv1d_fn_cpu(

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.

Kernels in this file should be moved to mamba/ops/cpu
And there is a torch fallback conv_1d kernel https://github.com/vllm-project/vllm/blob/main/vllm/model_executor/layers/mamba/ops/cpu/causal_conv1d.py , is it possible to reuse and extend?

Comment thread vllm/model_executor/layers/mamba/ops/ssu_dispatch.py Outdated
Comment on lines +282 to +284
# On CPU-only platforms (PowerPC, x86 without CUDA) Triton JIT is
# unstable or unavailable. Silently fall back to the pure-PyTorch CPU
# backend unless the user explicitly chose something other than "triton".

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.

Also need to update

Comment thread vllm/_custom_ops.py Outdated
Comment on lines +3434 to +3436
from vllm.platforms import CpuArchEnum

if current_platform.get_cpu_architecture() == CpuArchEnum.X86:

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.

The check is not required

Comment thread vllm/model_executor/layers/utils.py Outdated
Comment thread vllm/model_executor/layers/utils.py Outdated
Akash kaothalkar added 2 commits July 13, 2026 20:43
Comment thread csrc/cpu/mamba_cpu.cpp
auto get_int32_ptr =
[](const c10::optional<at::Tensor>& opt) -> const int32_t* {
return (opt.has_value() && opt.value().defined())
? opt.value().data_ptr<int32_t>()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Severity: MEDIUM

Unlike causal_conv1d_update_cpu_impl (line 73) and mamba_chunk_scan_fwd_cpu_impl (line 265) which safely convert with .to(at::kInt).contiguous(), this get_int32_ptr lambda calls data_ptr<int32_t>() without dtype validation. In release builds, PyTorch skips type-checking in data_ptr<T>(), so if any caller passes int64 tensors (e.g., cu_seqlens, state_batch_indices), memory is silently reinterpreted as int32, producing corrupted index values used for state pointer arithmetic—causing out-of-bounds writes.
Helpful? Add 👍 / 👎

💡 Fix Suggestion

Suggestion: Refactor the get_int32_ptr lambda to first convert the optional tensor to at::kInt (int32) and make it contiguous before calling data_ptr<int32_t>(), consistent with the safe pattern used in causal_conv1d_update_cpu_impl (line 73) and mamba_chunk_scan_fwd_cpu_impl (line 261). Because the converted tensor must outlive the pointer, change the lambda to return an at::Tensor instead, keep the converted tensors alive in local variables, and then extract raw pointers. Replace lines 180–189 with:

  auto safe_to_int32 =
      [](const c10::optional<at::Tensor>& opt) -> at::Tensor {
    if (opt.has_value() && opt.value().defined())
      return opt.value().to(at::kInt).contiguous();
    return {};
  };
  at::Tensor sbi_t = safe_to_int32(state_batch_indices);
  at::Tensor dsbi_t = safe_to_int32(dst_state_batch_indices);
  at::Tensor nat_t = safe_to_int32(num_accepted_tokens);
  at::Tensor csl_t = safe_to_int32(cu_seqlens);
  const int32_t* sbi_ptr = sbi_t.defined() ? sbi_t.data_ptr<int32_t>() : nullptr;
  const int32_t* dsbi_ptr = dsbi_t.defined() ? dsbi_t.data_ptr<int32_t>() : nullptr;
  const int32_t* nat_ptr = nat_t.defined() ? nat_t.data_ptr<int32_t>() : nullptr;
  const int32_t* csl_ptr = csl_t.defined() ? csl_t.data_ptr<int32_t>() : nullptr;

This ensures that even if callers pass int64 tensors (PyTorch's default integer dtype), they are safely converted to int32 before reinterpretation, preventing silent memory corruption and potential out-of-bounds access.

Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
@mergify mergify Bot removed the needs-rebase label Jul 14, 2026
for (; n <= dstate - VEC_ELEM_NUM; n += VEC_ELEM_NUM) {
vec_op::FP32Vec8 B_v((input_vec_t(B_g_base + n)));
vec_op::FP32Vec8 C_v((input_vec_t(C_g_base + n)));
vec_op::FP32Vec8 s_v((state_vec_t(s_hd + n)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Severity: MEDIUM

The selective_state_update_kernel accesses state elements as s_hd[n] (pointer+offset), implicitly assuming state.stride(3)==1 (dstate dimension is contiguous). However, selective_state_update_cpu_impl only captures strides for dimensions 0–2 and never validates dimension-3 contiguity. Unlike mamba_chunk_scan_fwd_cpu_impl (which has a TORCH_CHECK(final_states.is_contiguous())) and causal_conv1d_update_kernel (which explicitly passes and uses stride_s_state), this kernel has no such protection, leading to heap OOB reads/writes if state.stride(3) != 1.
Helpful? Add 👍 / 👎

💡 Fix Suggestion

Suggestion: Add a TORCH_CHECK in the selective_state_update_cpu_impl function in csrc/cpu/mamba_cpu.cpp, after capturing the state strides (around line 170), to validate that the dstate dimension is contiguous. For example, add:

TORCH_CHECK(state.stride(3) == 1,
            "selective_state_update_cpu: state must be contiguous in the "
            "dstate (last) dimension, got stride ", state.stride(3));

This mirrors the pattern used in mamba_chunk_scan_fwd_cpu_impl (line 269) which has TORCH_CHECK(final_states.is_contiguous(), ...). Alternatively, you could check full contiguity with state.is_contiguous() if the memmove on line 244 (which also assumes contiguity across heads) should be protected too.

Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
@Akashcodes732

Copy link
Copy Markdown
Contributor Author

Hi @bigPYJ1151 ,

I have addressed review comments, please take a look.

from vllm.v1.attention.backends.utils import PAD_SLOT_ID


def causal_conv1d_fn_cpu(

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.

Is it possible to combine this with causal_conv1d_torch?

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! We merged the fast F.conv1d logic into causal_conv1d_fn_cpu and removed the redundant function.

Comment on lines +92 to +179
"""CPU implementation for causal_conv1d_update."""
if isinstance(activation, bool):
activation = "silu" if activation else None

original_x_dtype = x.dtype
x = x.to(conv_state.dtype)
_, width = weight.shape
state_len = width - 1

if query_start_loc is None and x.dim() == 2:
x = x.unsqueeze(-1)
unsqueeze = True
else:
unsqueeze = False

if query_start_loc is None:
batch, dim, seqlen = x.shape

if conv_state_indices is not None:
cache_idxs = conv_state_indices.flatten()
valid_mask = cache_idxs != pad_slot_id
else:
cache_idxs = torch.arange(batch, device=x.device)
valid_mask = torch.ones(batch, dtype=torch.bool, device=x.device)

for t in range(seqlen):
x_t = x[:, :, t].clone()

states = conv_state[cache_idxs]

windows = torch.cat([states, x_t.unsqueeze(-1)], dim=-1)

val = (windows * weight.unsqueeze(0)).sum(dim=-1)
if bias is not None:
val = val + bias.unsqueeze(0)

if activation in ["silu", "swish"]:
val = val * torch.sigmoid(val)

val = val * valid_mask.unsqueeze(-1).to(val.dtype)
x[:, :, t] = val

new_state = torch.cat([states[:, :, 1:], x_t.unsqueeze(-1)], dim=-1)
conv_state[cache_idxs[valid_mask]] = new_state[valid_mask]

out = x
if unsqueeze:
out = out.squeeze(-1)
return out.to(original_x_dtype)

assert conv_state_indices is not None
assert query_start_loc is not None
batch = conv_state_indices.size(0)
out = x.clone()

for b in range(batch):
cache_idx = conv_state_indices[b].item()
if cache_idx == pad_slot_id:
continue

seq_start = query_start_loc[b].item()
seq_end = query_start_loc[b + 1].item()
seqlen_b = seq_end - seq_start

if seqlen_b == 0:
continue

local_state = conv_state[cache_idx].clone()

for t in range(seqlen_b):
x_t = x[seq_start + t, :]

window = torch.cat([local_state, x_t.unsqueeze(-1)], dim=-1)
val = (window * weight).sum(dim=-1)
if bias is not None:
val = val + bias
if activation in ["silu", "swish"]:
val = val * torch.sigmoid(val)

out[seq_start + t, :] = val

if state_len > 1:
local_state[:, :-1] = local_state[:, 1:].clone()
local_state[:, -1] = x_t

conv_state[cache_idx] = local_state

return out.to(original_x_dtype)

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.

Why not use causal_conv1d_update_cpu_vec?

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

Comment on lines 468 to +1276
@@ -1066,7 +1066,7 @@ def _causal_conv1d_update_kernel(
tl.store(o_ptrs, acc, mask=mask_1d)


def causal_conv1d_update(
def _causal_conv1d_update_cuda(
x: torch.Tensor,
conv_state: torch.Tensor,
weight: torch.Tensor,
@@ -1237,3 +1237,50 @@ def grid(META):
if unsqueeze:
out = out.squeeze(-1)
return out.to(original_x_dtype)


def causal_conv1d_fn(*args, **kwargs):
return _causal_conv1d_fn_cuda(*args, **kwargs)


def causal_conv1d_update(
x,
conv_state,
weight,
bias=None,
activation=None,
conv_state_indices=None,
num_accepted_tokens=None,
query_start_loc=None,
max_query_len=-1,
null_block_id=NULL_BLOCK_ID,
block_idx_last_scheduled_token=None,
initial_state_idx=None,
validate_data=False,
):
"""Dispatch causal_conv1d_update to CPU C++ kernel or CUDA Triton kernel."""

return _causal_conv1d_update_cuda(
x,
conv_state,
weight,
bias=bias,
activation=activation,
conv_state_indices=conv_state_indices,
num_accepted_tokens=num_accepted_tokens,
query_start_loc=query_start_loc,
max_query_len=max_query_len,
null_block_id=null_block_id,
block_idx_last_scheduled_token=block_idx_last_scheduled_token,
initial_state_idx=initial_state_idx,
validate_data=validate_data,

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.

These changes can be reverted.

Comment on lines 229 to +917
@@ -494,7 +495,7 @@ def _selective_scan_update_kernel(
tl.store(dst_state_ptrs, state, mask=mask)


def selective_state_update(
def _selective_state_update_cuda(
state,
x,
dt,
@@ -605,6 +606,26 @@ def selective_state_update(
if num_accepted_tokens is not None:
assert num_accepted_tokens.shape == (N,)

if not HAS_TRITON:
return ops.selective_state_update_cpu(
state,
x,
dt,
A,
B,
C,
D,
z,
dt_bias,
dt_softplus,
state_batch_indices,
dst_state_batch_indices,
null_block_id,
out,
num_accepted_tokens,
cu_seqlens,
)

grid = lambda META: (triton.cdiv(dim, META["BLOCK_SIZE_M"]), N, nheads)
z_strides = (z.stride(0), z.stride(1), z.stride(2)) if z is not None else (0, 0, 0)
state_batch_indices_strides = (
@@ -845,3 +866,58 @@ def selective_scan_fn(
return delta # output written inplace to delta
else:
return z # output written inplace to z


def selective_state_update(
state,
x,
dt,
A,
B,
C,
D=None,
z=None,
dt_bias=None,
dt_softplus=False,
state_batch_indices=None,
dst_state_batch_indices=None,
null_block_id=NULL_BLOCK_ID,
out=None,
num_accepted_tokens=None,
cu_seqlens=None,
is_blackwell=False,
enable_stochastic_rounding=False,
cache_philox_rounds=0,
):
"""Dispatch selective_state_update to CPU C++ kernel or CUDA Triton kernel."""
# Ensure out tensor exists
if out is None:
out = torch.empty_like(x if x.dim() == 2 else x)

return _selective_state_update_cuda(
state,
x,
dt,
A,
B,
C,
D=D,
z=z,
dt_bias=dt_bias,
dt_softplus=dt_softplus,
state_batch_indices=state_batch_indices,
dst_state_batch_indices=dst_state_batch_indices,
null_block_id=null_block_id,
out=out,
num_accepted_tokens=num_accepted_tokens,
cu_seqlens=cu_seqlens,
is_blackwell=is_blackwell,
enable_stochastic_rounding=enable_stochastic_rounding,
cache_philox_rounds=cache_philox_rounds,
)

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.

These changes can be reverted.

Comment on lines 27 to +186
@@ -154,6 +155,35 @@ def _mamba_chunk_scan_combined_fwd(
return states[last_chunk_indices]


@CustomOp.register("mamba_chunk_scan_combined_fwd")
class MambaChunkScanCombinedFwdOp(CustomOp):
def __init__(self):
# Bypass dispatch_forward() / get_current_vllm_config() by directly
# selecting the forward method based on the platform. This op may be
# instantiated during Mamba2 kernel warm-up, which happens before the
# vLLM compilation config is available.
super(CustomOp, self).__init__() # nn.Module.__init__ only
from vllm.platforms import current_platform

if current_platform.is_rocm():
self._forward_method = self.forward_hip
else:
self._forward_method = self.forward_cuda

def forward_cuda(self, *args, **kwargs):
return _mamba_chunk_scan_combined_fwd_cuda(*args, **kwargs)


_mamba_chunk_scan_combined_fwd_op: "MambaChunkScanCombinedFwdOp | None" = None


def _mamba_chunk_scan_combined_fwd(*args, **kwargs):
global _mamba_chunk_scan_combined_fwd_op
if _mamba_chunk_scan_combined_fwd_op is None:
_mamba_chunk_scan_combined_fwd_op = MambaChunkScanCombinedFwdOp()
return _mamba_chunk_scan_combined_fwd_op(*args, **kwargs)


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.

These changes can be reverted.

Comment thread mamba_cpu_updates.patch Outdated
Akash kaothalkar added 6 commits July 14, 2026 22:44
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Signed-off-by: Akash kaothalkar <akash.kaothalkar@ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/build cpu Related to CPU backends

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants