-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Selective State Update kernel (mamba) #2301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 6 commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
bcbd2aa
I seem that I can call my cuda wrapper from python
44d2b41
check all the kernel inputs. ready to develop dispatch code.
3d00fdf
simple implementation of selective_state_update is working
ebe9070
ported the hopper version + runtime dispatch check
8e80802
Passed pre-commit checks
ishovkun 8568d67
Update flashinfer/mamba/selective_state_update.py
ishovkun 7bb48cc
remove unreachable code
5812938
Improve docstring for input state shape
ishovkun 9883eab
Simple kernel also uses fast_exp
8aed468
Remove error check of the kernel launch (it was a debugging leftover).
5c65e53
no need for the fast_exp function
681a5ba
Remove unnecessary None check for D before unsqueeze
5e8536e
Hoist dA computation outside the innermost loop.
acab6bd
support for non-none z
84bdc07
stage forgotten z handling in the test
edb5269
test: handle z
3e97354
make sure that batch size does not exceed the state cache size
7ba913d
do use matrixA_dtype in the test
83c5ecb
Add selective state update module to JIT specs
c0f96aa
Fix dt_bias stride check to handle None value
03a8570
handle a few different DIM and DSTATE
ce2e868
selective state: test various dims and state sizes
d0cd9da
Fix
62eeef8
Merge branch 'flashinfer-ai:main' into main
ishovkun 0f5f4b8
formatting
ishovkun 728e0c3
Merge branch 'main' of github.com:ishovkun/flashinfer-dev
ishovkun a35c408
Add SM90 Mamba selective state update JIT module
ishovkun 81163a2
ifdef guards for the hopper+ implementation for aot
ishovkun d2a147a
export both selective_state_update and selective_state_update_sm90 moβ¦
ishovkun 040734e
Add alignment checks for Mamba state update
ishovkun 6d9aa1b
Fix `toFloat` usage in Mamba selective state update kernel
ishovkun d22eec4
avoid an ambiguous variable name
ishovkun 59c9f85
Support SM90+ for Mamba selective state update
ishovkun c443c54
Improve FLASHINFER_CHECK error message in selective_state_update
ishovkun 95fa14d
Refactor SM90 module to use CompilationContext for nvcc flags
ishovkun dfc0a38
Exclude an unnecessary compiler flag as it is part of the default flags.
ishovkun baa55a1
comment about the use of TMA to justify the neccesity of sm90 module
ishovkun 5afc7f6
a comment about the choice of A tensor in the unit test
ishovkun 0d9c71b
Use torch.testing.assert_allclose instead of torch.allclose as the
ishovkun 5cda288
Merge branch 'flashinfer-ai:main' into main
ishovkun d99923c
fix obsoleted bf16 ifdefs
ishovkun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| /* | ||
| * Copyright (c) 2025 by FlashInfer team. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| #include "tvm_ffi_utils.h" | ||
|
|
||
| // Declare the function(s) implemented in selective_state_update.cu | ||
| using tvm::ffi::Optional; | ||
|
|
||
| namespace flashinfer::mamba { | ||
|
|
||
| void selective_state_update(TensorView state, TensorView x, TensorView dt, TensorView output, | ||
| TensorView A, TensorView B, TensorView C, TensorView D, | ||
| Optional<TensorView> z, Optional<TensorView> dt_bias, bool dt_softplus, | ||
| Optional<TensorView> state_batch_indices, int64_t pad_slot_id); | ||
|
|
||
| } // namespace flashinfer::mamba | ||
|
|
||
| // Export the function(s) via TVM-FFI | ||
| // This enables cross-language bindings (not just PyTorch) | ||
| TVM_FFI_DLL_EXPORT_TYPED_FUNC(selective_state_update, flashinfer::mamba::selective_state_update); | ||
|
|
||
| // Add more mamba operations here as they are implemented |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| /* | ||
| * Copyright (c) 2025 by FlashInfer team. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| #include <flashinfer/mamba/selective_state_update.cuh> | ||
|
|
||
| #include "tvm_ffi_utils.h" | ||
|
|
||
| using namespace flashinfer; | ||
| using tvm::ffi::Optional; | ||
|
|
||
| namespace flashinfer::mamba { | ||
|
|
||
| // TODO: Implement the launcher function that: | ||
| // 1. Validates tensor dimensions and devices | ||
| // 2. Extracts raw pointers from TensorView | ||
| // 3. Calls the framework-agnostic kernel from include/flashinfer/mamba/selective_state_update.cuh | ||
| // | ||
| void selective_state_update(TensorView state, TensorView x, TensorView dt, TensorView output, | ||
| TensorView A, TensorView B, TensorView C, TensorView D, | ||
| Optional<TensorView> z, Optional<TensorView> dt_bias, bool dt_softplus, | ||
| Optional<TensorView> state_batch_indices, int64_t pad_slot_id) { | ||
| auto const batch = x.size(0); | ||
| auto const state_cache_size = state.size(0); | ||
| auto const nheads = state.size(1); | ||
| auto const dim = state.size(2); | ||
| auto const dstate = state.size(3); | ||
| auto const ngroups = B.size(1); | ||
|
|
||
| FLASHINFER_CHECK(nheads % ngroups == 0, "nheads must be divisible by ngroups"); | ||
|
|
||
| // Check x shape and strides | ||
| CHECK_DIM(3, x); | ||
| FLASHINFER_CHECK(x.size(1) == nheads, "x.size(1) must equal nheads"); | ||
| FLASHINFER_CHECK(x.size(2) == dim, "x.size(2) must equal dim"); | ||
| CHECK_LAST_DIM_CONTIGUOUS_INPUT(x); | ||
| FLASHINFER_CHECK(x.stride(1) == dim, "x.stride(1) must equal dim, got ", x.stride(1), | ||
| " expected ", x.size(2)); | ||
|
|
||
| // Check output shape and strides | ||
| CHECK_DIM(3, output); | ||
| CHECK_LAST_DIM_CONTIGUOUS(output); | ||
| FLASHINFER_CHECK(output.size(1) == nheads, "output.size(1) must equal nheads"); | ||
| FLASHINFER_CHECK(output.size(2) == dim, "output.size(2) must equal dim"); | ||
| FLASHINFER_CHECK(output.stride(1) == dim, "output.stride(1) must equal dim"); | ||
|
|
||
| // Check dt shape and strides | ||
| CHECK_CUDA(dt); | ||
| CHECK_DIM(3, dt); // dt: {batch, nheads, dim} | ||
| FLASHINFER_CHECK(dt.size(1) == nheads, "dt.size(1) must equal nheads"); | ||
| FLASHINFER_CHECK(dt.size(2) == dim, "dt.size(2) must equal dim"); | ||
| FLASHINFER_CHECK(dt.stride(1) == 1, "dt.stride(1) must be 1, got ", dt.stride(1)); | ||
| FLASHINFER_CHECK(dt.stride(2) == 0, "dt.stride(2) must be 0 (broadcasted), got ", dt.stride(2)); | ||
|
|
||
| // Check state - fully contiguous | ||
| CHECK_INPUT(state); // CUDA + fully contiguous (uses TVM FFI) | ||
| CHECK_DIM(4, state); // state: {state_cache_size, nheads, dim, dstate} | ||
|
|
||
| // Check B shape and strides | ||
| CHECK_CUDA(B); | ||
| CHECK_DIM(3, B); // B: {batch, B.size(1), dstate} | ||
| FLASHINFER_CHECK(B.size(0) == batch, "B.size(0) must equal batch"); | ||
| FLASHINFER_CHECK(B.size(1) == ngroups, "B.size(1) must equal ngroups"); | ||
| FLASHINFER_CHECK(B.size(2) == dstate, "B.size(2) must equal dstate"); | ||
| CHECK_LAST_DIM_CONTIGUOUS(B); // stride(2) == 1 | ||
| FLASHINFER_CHECK(B.stride(1) == B.size(2), "B.stride(1) must equal dstate, got ", B.stride(1), | ||
| " expected ", B.size(2)); | ||
|
|
||
| // Check C shape and strides | ||
| CHECK_CUDA(C); | ||
| CHECK_LAST_DIM_CONTIGUOUS(C); // stride(2) == 1 | ||
| CHECK_DIM(3, C); // C: {batch, C.size(1), dstate} | ||
| FLASHINFER_CHECK(C.stride(1) == C.size(2), "C.stride(1) must equal dstate, got ", C.stride(1), | ||
| " expected ", C.size(2)); | ||
| FLASHINFER_CHECK(C.size(0) == batch, "C.size(0) must equal batch"); | ||
| FLASHINFER_CHECK(C.size(1) == ngroups, "C.size(1) must equal ngroups"); | ||
| FLASHINFER_CHECK(C.size(2) == dstate, "C.size(2) must equal dstate"); | ||
|
|
||
| // Check D - specific stride patterns indicating broadcasting | ||
| CHECK_CUDA(D); | ||
| CHECK_DIM(2, D); // D: {nheads, dim} | ||
| FLASHINFER_CHECK(D.size(0) == nheads, "D.size(0) must equal nheads"); | ||
| FLASHINFER_CHECK(D.size(1) == dim, "D.size(1) must equal dim"); | ||
| FLASHINFER_CHECK(D.stride(0) == 1, "D.stride(0) must be 1, got ", D.stride(0)); | ||
| FLASHINFER_CHECK(D.stride(1) == 0, "D.stride(1) must be 0 (broadcasted), got ", D.stride(1)); | ||
|
|
||
| // Check A - specific stride patterns indicating broadcasting | ||
| CHECK_CUDA(A); | ||
| CHECK_DIM(3, A); // A: {nheads, dim, dstate} | ||
| FLASHINFER_CHECK(A.size(0) == nheads, "A.size(0) must equal nheads"); | ||
| FLASHINFER_CHECK(A.size(1) == dim, "A.size(1) must equal dim"); | ||
| FLASHINFER_CHECK(A.size(2) == dstate, "A.size(2) must equal dstate"); | ||
| FLASHINFER_CHECK(A.stride(1) == 0, "A.stride(1) must be 0 (broadcasted), got ", A.stride(1)); | ||
| FLASHINFER_CHECK(A.stride(2) == 0, "A.stride(2) must be 0 (broadcasted), got ", A.stride(2)); | ||
|
|
||
| // Optional dt_bias check | ||
| if (dt_bias.has_value()) { | ||
| auto& bias = dt_bias.value(); | ||
| CHECK_CUDA(bias); | ||
| CHECK_DIM(2, bias); // dt_bias: {nheads, dim} | ||
| FLASHINFER_CHECK(bias.size(0) == nheads, "dt_bias.size(0) must equal nheads"); | ||
| FLASHINFER_CHECK(bias.size(1) == dim, "dt_bias.size(1) must equal dim"); | ||
| FLASHINFER_CHECK(bias.stride(0) == 1, "dt_bias.stride(0) must be 1, got ", bias.stride(0)); | ||
| FLASHINFER_CHECK(bias.stride(1) == 0, "dt_bias.stride(1) must be 0 (broadcasted), got ", | ||
| bias.stride(1)); | ||
| } | ||
|
|
||
| // if(z.has_value()) | ||
| FLASHINFER_CHECK(!z.has_value() && "z is not supported yet"); | ||
|
|
||
| if (state_batch_indices) { | ||
| CHECK_DIM(1, (*state_batch_indices)); | ||
| FLASHINFER_CHECK(state_batch_indices.value().size(0) == batch, | ||
| "state_batch_indices.shape must be (", batch, ")"); | ||
| } | ||
|
|
||
| SelectiveStateUpdateParams p; | ||
|
|
||
| // copy dimensions | ||
| p.batch = batch; | ||
| p.nheads = nheads; | ||
| p.dim = dim; | ||
| p.dstate = dstate; | ||
| p.ngroups = ngroups; | ||
| p.state_cache_size = state_cache_size; | ||
| p.dt_softplus = dt_softplus; | ||
| p.pad_slot_id = pad_slot_id; | ||
|
|
||
| // Copy strides | ||
| p.x_stride_batch = x.stride(0); | ||
| p.dt_stride_batch = dt.stride(0); | ||
| p.B_stride_batch = B.stride(0); | ||
| p.C_stride_batch = C.stride(0); | ||
| p.out_stride_batch = output.stride(0); | ||
| if (state_batch_indices) p.state_batch_indices = state_batch_indices.value().data_ptr(); | ||
|
|
||
| // Copy pointers | ||
| p.state = state.data_ptr(); | ||
| p.x = x.data_ptr(); | ||
| p.dt = dt.data_ptr(); | ||
| p.output = output.data_ptr(); | ||
| if (dt_bias) { | ||
| p.dt_bias = dt_bias.value().data_ptr(); | ||
| } | ||
| if (z) { | ||
| p.z = z.value().data_ptr(); | ||
| } | ||
| p.A = A.data_ptr(); | ||
| p.B = B.data_ptr(); | ||
| p.C = C.data_ptr(); | ||
| p.D = D.data_ptr(); | ||
|
|
||
| // Set device and get stream | ||
| ffi::CUDADeviceGuard device_guard(state.device().device_id); | ||
| const cudaStream_t stream = get_stream(state.device()); | ||
|
|
||
| // Dispatch based on dtype combination | ||
| DLDataType state_dtype = state.dtype(); | ||
| DLDataType input_dtype = x.dtype(); | ||
| DLDataType weight_dtype = dt.dtype(); | ||
| DLDataType matrixA_dtype = A.dtype(); | ||
|
|
||
| int64_t state_dtype_code = encode_dlpack_dtype(state_dtype); | ||
| int64_t input_dtype_code = encode_dlpack_dtype(input_dtype); | ||
| int64_t weight_dtype_code = encode_dlpack_dtype(weight_dtype); | ||
| int64_t matrixA_dtype_code = encode_dlpack_dtype(matrixA_dtype); | ||
|
|
||
| // Pack all dtype codes into a single value for switching | ||
| auto dtype_key = | ||
| std::make_tuple(state_dtype_code, input_dtype_code, weight_dtype_code, matrixA_dtype_code); | ||
|
|
||
| // Currently only support: input_t = weight_t = state_t = bfloat16, matrixA_t = float | ||
| if (dtype_key == std::make_tuple(bfloat16_code, bfloat16_code, bfloat16_code, float32_code)) { | ||
| using state_t = nv_bfloat16; | ||
| using input_t = nv_bfloat16; | ||
| using weight_t = nv_bfloat16; | ||
| using matrixA_t = float; | ||
|
|
||
| invokeSelectiveStateUpdate<input_t, weight_t, matrixA_t, state_t>(p, stream); | ||
| } else { | ||
| // Default case: unsupported dtype combination | ||
| TVM_FFI_ICHECK(false) << "Unsupported dtype combination for selective_state_update: " | ||
| << "state_dtype=" << state_dtype.code << ":" << state_dtype.bits << ", " | ||
| << "input_dtype=" << input_dtype.code << ":" << input_dtype.bits << ", " | ||
| << "weight_dtype=" << weight_dtype.code << ":" << weight_dtype.bits | ||
| << ", " | ||
| << "matrixA_dtype=" << matrixA_dtype.code << ":" << matrixA_dtype.bits | ||
| << ". Currently only support: " | ||
| << "state=bfloat16, input=bfloat16, weight=bfloat16, matrixA=float32"; | ||
|
|
||
| throw std::runtime_error("selective_state_update is not implemented yet."); | ||
| } | ||
| } | ||
|
|
||
| } // namespace flashinfer::mamba | ||
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| """ | ||
| Copyright (c) 2025 by FlashInfer team. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| """ | ||
|
|
||
| from .selective_state_update import gen_selective_state_update_module | ||
|
|
||
| __all__ = ["gen_selective_state_update_module"] |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| """ | ||
| Copyright (c) 2025 by FlashInfer team. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| """ | ||
|
|
||
| from .. import env as jit_env | ||
| from ..core import JitSpec, gen_jit_spec | ||
|
|
||
|
|
||
| def gen_selective_state_update_module() -> JitSpec: | ||
| nvcc_flags = [ | ||
| "-DENABLE_BF16", | ||
|
ishovkun marked this conversation as resolved.
Outdated
|
||
| "-DENABLE_FP8", | ||
| ] | ||
|
|
||
| return gen_jit_spec( | ||
| "mamba_selective_state_update", | ||
| [ | ||
| jit_env.FLASHINFER_CSRC_DIR / "selective_state_update.cu", | ||
| jit_env.FLASHINFER_CSRC_DIR / "flashinfer_mamba_binding.cu", | ||
| ], | ||
| extra_cuda_cflags=nvcc_flags, | ||
| ) | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| """ | ||
| Copyright (c) 2025 by FlashInfer team. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| """ | ||
|
|
||
| from .selective_state_update import selective_state_update | ||
|
|
||
| __all__ = ["selective_state_update"] |
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.