Skip to main content

impl-jit-kernel

Guide for implementing CUDA or CPU JIT kernels in mllm-kernel. Use when the user asks to create, add, or implement a new kernel in mllm-kernel.

Quellinformationen

Repository
UbiquitousLearning/mllm
Letzte Quellaktivität
2. März 2026 um 06:45
Erkannte Sprache von SKILL.md
Englisch
Sterne
1.628
Forks
219

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
impl-jit-kernel
description
Guide for implementing CUDA or CPU JIT kernels in mllm-kernel. Use when the user asks to create, add, or implement a new kernel in mllm-kernel.
# Implementing a JIT Kernel in mllm-kernel ## Overview mllm-kernel uses a JIT (Just-In-Time) compilation system built on `tvm_ffi`. Kernels are written in C++20 (`.cuh` for CUDA, `.cpp` for CPU), validated at runtime via `TensorMatcher`, and exposed to Python through a `@jit` decorator. No pre-compilation is needed -- kernels compile on first call and are cached at `~/.cache/mllm_kernel/`. ## File Layout For a kernel named `my_kernel`: ``` mllm-kernel/ mllm_kernel/ cuda/ csrc/my_kernel.cuh # CUDA kernel implementation jit/my_kernel.py # Python JIT wrapper jit/__init__.py # Add export here cpu/ csrc/my_kernel.cpp # CPU kernel implementation (Highway SIMD) include/mllm_kernel/cpu/ my_kernel.hpp # CPU SIMD body (NO #pragma once) jit/my_kernel.py # Python JIT wrapper jit/__init__.py # Add export here tests/test_my_kernel.py # Pytest correctness tests benchmarks/bench_my_kernel.py # Profiler benchmark vs PyTorch reference ``` --- ## CUDA Kernel Walkthrough ### Step 1: Write the `.cuh` kernel Create `mllm_kernel/cuda/csrc/my_kernel.cuh`: ```cpp #pragma once #include <mllm_kernel/tensor.hpp> // TensorMatcher, SymbolicSize, SymbolicDevice, SymbolicDType #include <mllm_kernel/utils.hpp> // RuntimeCheck, Panic, div_ceil #include <mllm_kernel/utils.cuh> // LaunchKernel, fp16_t, bf16_t, PDL helpers #include <dlpack/dlpack.h> #include <tvm/ffi/container/tensor.h> #include <cstdint> namespace { // --------------------------------------------------------------------------- // 1. Parameter struct (trivially copyable, passed to kernel by value) // --------------------------------------------------------------------------- struct MyKernelParams { const float* __restrict__ input; float* __restrict__ output; int32_t num_elements; }; // --------------------------------------------------------------------------- // 2. CUDA kernel // --------------------------------------------------------------------------- __global__ void my_kernel(const MyKernelParams params) { const int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx >= params.num_elements) return; params.output[idx] = params.input[idx] * 2.0f; } // --------------------------------------------------------------------------- // 3. Host-side launcher (entry point for TVM FFI binding) // --------------------------------------------------------------------------- struct MyKernel { static void run(tvm::ffi::TensorView input, tvm::ffi::TensorView output) { using namespace mllm_kernel::host; // --- Validate tensors --- SymbolicSize N{"num_elements"}; SymbolicDevice device; (void)TensorMatcher({N}) .with_dtype<float>() .with_device<kDLCUDA>(device) .verify(input); (void)TensorMatcher({N}) .with_dtype<float>() .with_device(device) .verify(output); const int64_t n = N.unwrap(); RuntimeCheck(n > 0, "num_elements must be positive, got ", n); // --- Build params --- MyKernelParams params{ .input = static_cast<const float*>(input.data_ptr()), .output = static_cast<float*>(output.data_ptr()), .num_elements = static_cast<int32_t>(n), }; // --- Launch --- constexpr int kBlock = 256; const int grid = static_cast<int>(div_ceil(n, kBlock)); LaunchKernel(grid, kBlock, device.unwrap())(my_kernel, params); } }; } // namespace ``` **Key rules:** - **Always wrap in `namespace {}`** (anonymous namespace). - **Entry point** is a `static void run(tvm::ffi::TensorView ...)` method. - **Validate every tensor** with `TensorMatcher` before reading `.data_ptr()`. - **Never dereference device pointers on host** -- `data_ptr()` returns a GPU pointer. - **Use `LaunchKernel`** to launch -- it handles stream resolution and error checking. ### Step 2: Write the Python JIT wrapper Create `mllm_kernel/cuda/jit/my_kernel.py`: ```python """JIT wrapper for my_kernel CUDA kernel.""" import torch from mllm_kernel.jit_utils import jit @jit( args=[], device="cuda", cuda_files=["my_kernel.cuh"], cpp_wrappers=[], cuda_wrappers=[("my_kernel", "MyKernel::run")], func_name="my_kernel", ) def _kernel(compiled_module, input: torch.Tensor, output: torch.Tensor) -> None: compiled_module.my_kernel(input, output) def my_kernel(input: torch.Tensor) -> torch.Tensor: """Double every element in *input*. Parameters ---------- input : torch.Tensor 1-D float32 tensor on CUDA. Returns ------- torch.Tensor Same shape and dtype as *input*. """ output = torch.empty_like(input) _kernel(input, output) return output ``` ### Step 3: Export in `__init__.py` Edit `mllm_kernel/cuda/jit/__init__.py` and add: ```python from mllm_kernel.cuda.jit.my_kernel import my_kernel ``` ### Step 4: Clear JIT cache after editing `.cuh` Any time you modify the `.cuh` file, delete the cached `.so`: ```bash rm -rf ~/.cache/mllm_kernel/cuda_my_kernel* ``` The next Python call will trigger recompilation automatically. --- ## Template-Parameterized CUDA Kernels When the kernel takes compile-time constants (e.g. block size, dtype), use `make_cpp_args`: ```python from mllm_kernel.jit_utils import jit, make_cpp_args def _make_kernel(block_size: int, use_pdl: bool): cpp_args = make_cpp_args(block_size, use_pdl) # -> "256, true" @jit( args=[block_size, use_pdl], device="cuda", cuda_files=["my_kernel.cuh"], cpp_wrappers=[], cuda_wrappers=[("my_kernel", f"MyKernel<{cpp_args}>::run")], func_name="my_kernel", ) def _kernel(compiled_module, input, output): compiled_module.my_kernel(input, output) return _kernel ``` `make_cpp_args` converts Python types to C++ literals: - `int/float` -> string literal - `bool` -> `"true"` / `"false"` - `torch.dtype` -> C++ type (`torch.float32` -> `"fp32_t"`, `torch.float16` -> `"fp16_t"`, `torch.bfloat16` -> `"bf16_t"`, `torch.int32` -> `"int32_t"`, etc.) --- ## CPU Kernel Walkthrough CPU kernels use **Google Highway** for portable SIMD. The key difference: the `.hpp` body is included **multiple times** by Highway's `foreach_target` dispatch, so it must NOT have `#pragma once`. ### Step 1: Write the SIMD body (`.hpp`) Create `mllm_kernel/cpu/include/mllm_kernel/cpu/my_kernel.hpp`: ```cpp // NOTE: NO #pragma once -- this file is included multiple times by Highway. #include <hwy/highway.h> HWY_BEFORE_NAMESPACE(); namespace mllm_kernel::cpu { namespace HWY_NAMESPACE { namespace hn = hwy::HWY_NAMESPACE; template <int Constant> inline void my_kernel_impl(float* HWY_RESTRICT dst, const float* HWY_RESTRICT src, size_t count) { const hn::ScalableTag<float> d; const size_t lanes = hn::Lanes(d); const auto vc = hn::Set(d, static_cast<float>(Constant)); size_t i = 0; for (; i + lanes <= count; i += lanes) { const auto v = hn::Load(d, src + i); hn::Store(hn::Add(v, vc), d, dst + i); } for (; i < count; ++i) { dst[i] = src[i] + static_cast<float>(Constant); } } // Named entry points for HWY_EXPORT static HWY_NOINLINE HWY_MAYBE_UNUSED void my_kernel_1(float* d, const float* s, size_t n) { my_kernel_impl<1>(d, s, n); } } // namespace HWY_NAMESPACE } // namespace mllm_kernel::cpu HWY_AFTER_NAMESPACE(); ``` ### Step 2: Write the `.cpp` source Create `mllm_kernel/cpu/csrc/my_kernel.cpp`: ```cpp #include <mllm_kernel/tensor.hpp> #include <mllm_kernel/utils.hpp> #include <tvm/ffi/container/tensor.h> #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "../csrc/my_kernel.cpp" #include <hwy/foreach_target.h> #include <mllm_kernel/cpu/my_kernel.hpp> #if HWY_ONCE #include <hwy/targets.cc> #endif namespace mllm_kernel::cpu { #if HWY_ONCE HWY_EXPORT(my_kernel_1); template <int Constant> void my_kernel(tvm::ffi::TensorView dst, tvm::ffi::TensorView src) { using namespace mllm_kernel::host; SymbolicSize N{"num_elements"}; SymbolicDevice device_;
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen