{"owner":"flashinfer-ai","repo":"flashinfer","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md","CLAUDE.md"],"skills":{"AGENTS.md":"This repository’s agent instructions live in [CLAUDE.md](./CLAUDE.md).\n\nBefore making changes, read `CLAUDE.md` and follow its guidance for build, test, style, and contribution workflows.\n","CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nFlashInfer is a GPU kernel library for LLM serving that uses **JIT (Just-In-Time) compilation by default**. This means kernel code changes are automatically picked up without reinstalling the package - extremely convenient for development.\n\n## Quick Reference\n\n| Task | Command |\n|------|---------|\n| Install for development | `pip install --no-build-isolation -e . -v` |\n| Initialize submodules | `git submodule update --init --recursive` |\n| Install CUPTI for benchmarking | `pip install -U cupti-python` |\n| Run all tests | `pytest tests/` |\n| Run specific test | `pytest tests/path/test_file.py::test_function` |\n| Run multi-GPU test | `mpirun -np 4 pytest tests/comm/test_allreduce_unified_api.py` |\n| Run benchmark | `python benchmarks/flashinfer_benchmark.py --routine <name> <flags>` |\n| Run linting | `pre-commit run -a` |\n| Dump environment report (bug reports) | `python -m flashinfer.collect_env` (or `flashinfer collect-env [--json]`) |\n| Install pre-commit hooks | `pre-commit install` |\n| Clear JIT cache | `rm -rf ~/.cache/flashinfer/` |\n| Enable API logging (basic) | `export FLASHINFER_LOGLEVEL=1` |\n| Enable API logging (detailed) | `export FLASHINFER_LOGLEVEL=3` |\n| Enable API logging (with stats) | `export FLASHINFER_LOGLEVEL=5` |\n| Set API log destination | `export FLASHINFER_LOGDEST=mylog.txt` |\n| Enable verbose JIT logging | `export FLASHINFER_JIT_VERBOSE=1` |\n| Enable debug build | `export FLASHINFER_JIT_DEBUG=1` |\n| Set target architectures | `export FLASHINFER_CUDA_ARCH_LIST=\"8.0 9.0a\"` |\n| Set parallel compilation | `export FLASHINFER_NVCC_THREADS=4` |\n| Limit parallel ninja jobs | `export MAX_JOBS=4` |\n| Enable GDN native short-T path | `export FLASHINFER_GDN_WY_NATIVE_T=1` |\n| Enable GDN strided QKV path | `export FLASHINFER_GDN_WY_STRIDED_QKV=1` |\n| Enable GDN native A/B tensors | `export FLASHINFER_GDN_WY_NATIVE_AB=1` |\n| Override CuTe-DSL prefill scheduling | `export FLASHINFER_CUTE_PREFILL_PERSISTENT=0` (non-persistent) or `1` (persistent) |\n| Skip MoE EP CuTe-DSL import/version guard | `export FLASHINFER_MOE_EP_SKIP_DSL_CHECK=1` |\n| Override MoE EP knob-cache path | `export FLASHINFER_MOE_EP_KNOB_CACHE=/path/to/knobs.json` |\n| Disable MoE EP fused staging kernel | `export FLASHINFER_MEGA_FUSED_STAGE=0` |\n| Enable distribution-aware MoE autotune and kernel dispatch (experimental; TRT-LLM MoE only) | `export FLASHINFER_DIST_AWARE_AUTOTUNE=1` |\n\nThe minimum Python version used by CI and build tooling is defined in\n`.python-version`. CI Docker images use a stable Conda environment name and\nderive their interpreter and site-packages paths from that file. When raising\nthe minimum, also update `project.requires-python` and `tool.mypy.python_version`\nin `pyproject.toml`.\n\n## Quick Start for Development\n\n### Installation\n\n```bash\ngit clone https://github.com/flashinfer-ai/flashinfer.git --recursive\ncd flashinfer\npip install --no-build-isolation -e . -v\n```\n\n**Important**: The `--recursive` flag is required to initialize submodules in `3rdparty/` (cutlass, spdlog).\n\nIf you forgot `--recursive` when cloning:\n```bash\ngit submodule update --init --recursive\n```\n\nThat's it! You can now:\n\n- Run all benchmarks and unit tests\n- Modify kernel source code in `include/` without reinstalling\n- Changes are JIT-compiled on next use\n\nThe `--no-build-isolation` flag prevents pip from pulling incompatible PyTorch/CUDA versions from PyPI.\n\n### How JIT Compilation Works\n\nWhen you call a FlashInfer API:\n\n1. **First call**: Generates specialized CUDA code based on parameters (dtype, head_dim, etc.), compiles it with ninja, caches the .so file\n2. **Subsequent calls**: Uses cached compiled module\n3. **After kernel changes**: Automatically detects changes and recompiles\n\n**No manual rebuild step needed** - just edit `.cuh` files and run your code again.\n\n### Pre-compiled Packages (Optional)\n\nFlashInfer provides optional pre-compiled packages for users who want faster initialization:\n\n- `flashinfer-jit-cache`: Pre-built kernel cache\n- `flashinfer-cubin`: Pre-compiled kernel binaries\n\n**For development, you typically DON'T need these.** JIT compilation is fast enough and gives you live code reload.\n\n## Testing\n\nRun all tests:\n\n```bash\npytest tests/\n```\n\nRun specific test file:\n\n```bash\npytest tests/attention/test_hopper.py\n```\n\nRun specific test function:\n\n```bash\npytest tests/attention/test_hopper.py::test_single_prefill\n```\n\n### Skipping Tests Based on CUDA Architecture\n\nUse `flashinfer.utils` functions to skip tests on unsupported GPU architectures:\n\n**Available check functions:**\n- `get_compute_capability(device)` - Returns `(major, minor)` tuple\n- `is_sm90a_supported()` - Hopper (requires CUDA 12.3+)\n- `is_sm100a_supported()` - Blackwell (requires CUDA 12.8+)\n- `is_sm100f_supported()` - Blackwell feature-set (requires CUDA 12.9+)\n- `is_sm110a_supported()`, `is_sm120a_supported()`, `is_sm120f_supported()`, `is_sm121a_supported()`\n\n**APIs decorated with `@backend_requirement`** also provide:\n- `api_name.is_compute_capability_supported(cc)` - e.g., `mm_fp4.is_compute_capability_supported(100)`\n- `api_name.is_backend_supported(\"backend\")` - e.g., `mm_fp4.is_backend_supported(\"cudnn\")`\n- `api_name.is_backend_supported(\"backend\", cc)` - e.g., `mm_fp4.is_backend_supported(\"cudnn\", 80)`\n\n**Example:**\n```python\nfrom flashinfer.utils import is_sm90a_supported\n\ndef test_hopper_attention():\n    if not is_sm90a_supported(torch.device(\"cuda\")):\n        pytest.skip(\"Requires SM90a\")\n    # Test code...\n```\n\n**Common requirements:**\n\n| Feature | Min SM | Check Function |\n|---------|--------|----------------|\n| FlashAttention-3 | SM90a | `is_sm90a_supported()` |\n| MLA Attention | SM100a | `is_sm100a_supported()` |\n| FP8 GEMM | SM89+ | `get_compute_capability()[0] >= 9` |\n\n**Note:** `tests/conftest.py` auto-skips tests that trigger OOM, but tests should be written to avoid OOM by using appropriate problem sizes.\n\n## Benchmarking\n\nFlashInfer provides a unified benchmarking framework in `benchmarks/flashinfer_benchmark.py`.\n\n**Key features:**\n- Supports attention, GEMM, and MOE kernels\n- Multiple backends: FlashAttention2/3, cuDNN, CUTLASS, TensorRT-LLM, cuBLAS\n- **CUPTI timing (recommended)**: Hardware-level profiling for accurate GPU kernel time\n  - Automatically falls back to CUDA events if CUPTI unavailable\n  - Install: `pip install -U cupti-python` (requires CUDA 13+)\n- Batch testing, reference checking, CSV output\n\n**Quick example:**\n```bash\npython benchmarks/flashinfer_benchmark.py \\\n    --routine BatchDecodeWithPagedKVCacheWrapper \\\n    --backends fa2 cudnn \\\n    --batch_size 32 --s_kv 2048 \\\n    --num_qo_heads 32 --num_kv_heads 8 \\\n    --head_dim_qk 128 --head_dim_vo 128 \\\n    --page_size 16 --refcheck -vv\n```\n\n**Python API:**\n```python\nfrom flashinfer.testing import bench_gpu_time\n\n# CUPTI preferred, auto-fallback to CUDA events\nmedian_time, std_time = bench_gpu_time(\n    my_kernel, args=(x, y), enable_cupti=True, num_iters=30\n)\n```\n\n→ **For complete benchmarking guide, see [`.claude/skills/benchmark-kernel/skill.md`](.claude/skills/benchmark-kernel/skill.md)**\n\n## Code Linting\n\nRun all pre-commit hooks:\n\n```bash\npre-commit run -a\n```\n\nInstall hooks to run on every commit:\n\n```bash\npre-commit install\n```\n\n## Code Review\n\nWhen reviewing a diff (as an agent or a human), follow the shared focus areas, kernel-review\npolicy, and effort calibration in [`docs/code_review_guidance.md`](docs/code_review_guidance.md).\nNote: unlike human review, agents keep **kernel implementation details in scope** — read the\nkernel logic and report bugs, labeling findings by confidence.\n\n→ **For the complete review rule set, see [`docs/code_review_guidance.md`](docs/code_review_guidance.md)**\n\n## Architecture: JIT Compilation System\n\nFlashInfer's JIT system has three layers:\n\n### Layer 1: JitSpec (flashinfer/jit/core.py)\n\n`JitSpec` is an abstract base class defining the kernel-module lifecycle\n(`try_load()` / `build()` / `load()`, with the shared `build_and_load()`\ntemplate method handling caching, locking, and `FLASHINFER_DISABLE_JIT`).\nOne subclass per compilation toolchain:\n\n- `JitSpecNvcc` (nvcc/ninja modules, returned by `gen_jit_spec()`) defines:\n  - `name`: Unique identifier (URI hash from parameters)\n  - `sources`: List of .cu/.cpp files to compile\n  - `extra_cuda_cflags`, `extra_cflags`, `extra_ldflags`: Compiler flags\n- `JitSpecCuteDsl` (flashinfer/jit/cute_dsl_core.py) caches CuTe-DSL kernels\n  (see \"CuTe-DSL kernels\" under Module Caching below)\n\n### JIT Directory Rules\n\n**NEVER write to package directories** - they may be read-only after installation.\n\n| Directory | Writable | Use for |\n|-----------|----------|---------|\n| `FLASHINFER_GEN_SRC_DIR` | ✓ Yes | Generated source files (Jinja output, copied .cu files) |\n| `FLASHINFER_JIT_DIR` | ✓ Yes | Compiled `.so` outputs |\n| `FLASHINFER_CSRC_DIR` | ✗ No | Read-only source templates |\n| `FLASHINFER_AOT_DIR` | ✗ No | Read-only pre-compiled binaries |\n\n### Compilation Context: Architecture-Specific Compilation\n\nFlashInfer uses `CompilationContext` to manage CUDA architecture targets. Some kernels only work on specific GPU architectures (e.g., Hopper SM90, Blackwell SM100/SM12x).\n\n**How it works:**\n- Auto-detects GPUs in system or reads `FLASHINFER_CUDA_ARCH_LIST` environment variable\n- CuTe-DSL FP4 MM compilation parallelism can be controlled with `FLASHINFER_MM_FP4_CUTE_DSL_COMPILE_WORKERS`\n- JIT modules specify `supported_major_versions=[9, 10, 11, 12]` to limit compilation to specific SM versions\n- If GPU not supported → `RuntimeError: No supported CUDA architectures found`\n\n→ **See [`.claude/skills/add-cuda-kernel/skill.md`](.claude/skills/add-cuda-kernel/skill.md) for usage examples**\n\n### Layer 2: Code Generation\n\nEvery `gen_*_module()` function in `flashinfer/jit/` follows this pattern:\n\n```python\ndef gen_some_module(dtype_in, dtype_out, ...):\n    # 1. Compute unique identifier from parameters\n    uri = get_some_uri(dtype_in, dtype_out, ...)\n\n    # 2. Create generation directory\n    gen_directory = jit_env.FLASHINFER_GEN_SRC_DIR / uri\n\n    # 3. (Optional) Render Jinja template to generate type-specialized config\n    # Skip this step if you don't need type specialization\n    with open(jit_env.FLASHINFER_CSRC_DIR / \"some_customize_config.jinja\") as f:\n        template = jinja2.Template(f.read())\n    config_content = template.render(\n        dtype_in=dtype_map[dtype_in],\n        dtype_out=dtype_map[dtype_out],\n        # ... more parameters\n    )\n    write_if_different(gen_directory / \"some_config.inc\", config_content)\n\n    # 4. Copy source files to gen directory\n    sources = []\n    for fname in [\"some_kernel.cu\", \"some_jit_binding.cu\"]:\n        shutil.copy(jit_env.FLASHINFER_CSRC_DIR / fname, gen_directory / fname)\n        sources.append(gen_directory / fname)\n\n    # 5. Return JitSpec\n    return gen_jit_spec(uri, sources, extra_cuda_cflags=[...])\n```\n\n**Note**: If your operation doesn't need type specialization, you can skip step 3 entirely and just copy the source files directly.\n\n### Layer 3: Compilation and Loading\n\n`JitSpecNvcc` methods:\n\n- `write_ninja()` - Generates `build.ninja` file\n- `build()` - Executes `ninja` to compile sources\n- `build_and_load()` - Compiles and loads via TVM-FFI\n\nThe generated `build.ninja` file uses nvcc to compile .cu → .cuda.o → .so, then loads via TVM-FFI.\n\n### Jinja Templates (Optional)\n\n**Note: Jinja templates are NOT required.** You can write C++ code directly without templating.\n\nFor operations that need type specialization, templates in `csrc/*.jinja` can generate C++ code:\n\n```jinja\n// Input template\nusing DTypeIn = {{ dtype_in }};\nusing DTypeOut = {{ dtype_out }};\nconstexpr int PARAM = {{ param_value }};\n\n// After render\nusing DTypeIn = float16;\nusing DTypeOut = float16;\nconstexpr int PARAM = 128;\n```\n\nThis allows the same CUDA template code to be compiled with different concrete types. However, if your operation doesn't need this, you can skip Jinja and write the `.cu` files directly.\n\n## Directory Structure\n\n```\nflashinfer/\n├── include/flashinfer/           # Header-only CUDA kernel templates\n│   ├── attention/                # Attention kernels\n│   ├── gemm/                     # GEMM kernels\n│   ├── comm/                     # Communication kernels\n│   ├── mma.cuh                   # Matrix multiply utilities\n│   ├── utils.cuh                 # Common utilities\n│   └── [...]\n│\n├── csrc/                          # Framework bindings (via TVM-FFI)\n│   ├── *.cu                       # Kernel launcher implementations\n│   ├── *_jit_binding.cu           # TVM-FFI exports\n│   ├── *_customize_config.jinja   # Type config templates (optional)\n│   └── [...]\n│\n├── flashinfer/                    # Python package\n│   ├── jit/\n│   │   ├── core.py                # JitSpec, compilation infrastructure\n│   │   ├── cpp_ext.py             # Ninja build generation\n│   │   ├── env.py                 # Workspace paths\n│   │   ├── attention/             # Attention module generators\n│   │   ├── gemm/                  # GEMM module generators\n│   │   ├── fused_moe/             # MOE module generators\n│   │   └── [...]\n│   ├── gemm/                      # GEMM Python APIs\n│   ├── fused_moe/                 # MOE Python APIs\n│   ├── comm/                      # Communication Python APIs\n│   ├── *.py                       # Other high-level Python APIs\n│   ├── aot.py                     # AOT compilation for pre-built packages\n│   └── [...]\n│\n├── tests/                         # Test suite\n│   ├── attention/                 # Attention kernel tests\n│   ├── gemm/                      # GEMM kernel tests\n│   ├── moe/                       # MOE kernel tests\n│   ├── comm/                      # Communication tests\n│   ├── utils/                     # Utility tests\n│   └── conftest.py                # Pytest configuration\n│\n└── build_backend.py               # PEP 517 build backend\n```\n\n### Critical Rule: Framework Separation\n\n**Torch headers MUST NOT be included in `include/` directory files.**\n\n- `include/`: Framework-agnostic CUDA kernels (accept raw pointers)\n- `csrc/`: Framework bindings via TVM-FFI (currently PyTorch, but can support other frameworks)\n\n## Adding a New Operation\n\n→ **For complete step-by-step tutorial, see [`.claude/skills/add-cuda-kernel/skill.md`](.claude/skills/add-cuda-kernel/skill.md)**\n\n**Quick overview of the process:**\n1. Write kernel in `include/flashinfer/new_op.cuh` (framework-agnostic, raw pointers)\n2. Write launcher in `csrc/new_op.cu` (PyTorch tensor handling)\n3. Create TVM-FFI bindings in `csrc/new_op_jit_binding.cu`\n4. (Optional) Create Jinja template for type specialization\n5. Write JIT module generator in `flashinfer/jit/new_op.py`\n6. Write Python API in `flashinfer/new_op.py` with `@functools.cache`\n7. Write tests in `tests/`\n8. Register in `flashinfer/aot.py` for AOT compilation\n9. Export in `flashinfer/__init__.py`\n10. Add a `TraceTemplate` in `flashinfer/trace/templates/` and wire it via `@flashinfer_api(trace=...)` (see below)\n11. Add an example call in `tests/trace/example.py`, re-run to regenerate `fi_trace_out/`, and commit the new JSON files\n\n### Trace Template Checklist (for new or updated APIs)\n\nEvery public API decorated with `@flashinfer_api` should also carry a `trace=` argument so that `fi_trace()` works and auto-dump produces a benchmark definition JSON.\n\n1. **Create or update a `TraceTemplate`** in `flashinfer/trace/templates/<category>.py` (e.g., `norm.py`, `activation.py`, `cascade.py`, `gdn.py`). Define `axes`, `inputs`, `outputs`, and optionally a `reference` function.\n2. **Wire the template** to the API: `@flashinfer_api(trace=my_trace)` on the Python function (or class method's `run()`).\n3. **Add an example call** in `tests/trace/example.py` that exercises the new trace with realistic shapes.\n4. **Regenerate examples**: `rm -rf tests/trace/fi_trace_out && python tests/trace/example.py` — verify the expected JSON appears.\n5. **Update the docstring** in `tests/trace/example.py` to list the new file(s).\n6. **Run tests**: `pytest tests/trace/ -v` — all template-consistency and end-to-end tests must pass.\n7. **Commit the new JSON files** under `tests/trace/fi_trace_out/` alongside the code changes.\n\n**Example implementations:**\n- **Simple**: `flashinfer/norm/__init__.py` (RMSNorm) - no Jinja, good starting point\n- **Moderate**: `flashinfer/sampling.py` - with Jinja templating\n- **Complex**: `flashinfer/decode.py` - plan-run pattern, advanced workspace\n\n## Key Architectural Patterns\n\n### Module Caching\n\nFlashInfer uses two-level caching to avoid recompilation:\n\n1. **Python-level** (`@functools.cache`): In-memory cache of loaded modules\n2. **File-level** (`~/.cache/flashinfer/`): Compiled `.so` files on disk\n\n**Cache invalidation** (automatic):\n- Source file changes (SHA256 hash)\n- Compilation flags change\n- CUDA architecture change\n- FlashInfer version change\n\nURI computed as: `hash(operation_type + parameters + source_hashes + flags + cuda_arch)`\n\n**Cache management:**\n- Clear cache: `rm -rf ~/.cache/flashinfer/`\n- Override location: `export FLASHINFER_WORKSPACE_BASE=\"/scratch\"`\n\n**CuTe-DSL kernels** (`flashinfer/jit/cute_dsl_core.py`): `cute.compile()` has no\npersistent cache, so `JitSpecCuteDsl` (a `JitSpec` subclass, wrapped by the\n`build_and_load_cute_dsl_kernel()` helper) exports compiled kernels as\nobject files (`export_to_c()`) and reloads them with `cute.runtime.load_module(...,\nenable_tvm_ffi=True)`. Artifacts live in `cached_ops/` next to the nvcc modules, one\ndirectory per op family — `cached_ops/<module>_<arch>_cute_dsl/` (e.g.\n`nvfp4_quantize_sm100a_cute_dsl/`) holding one `meta.json` plus one `.o` per\nspecialization. The arch comes from the DSL's compile target (`CUTE_DSL_ARCH` or the\ncurrent device) since the artifacts are single-arch, unlike nvcc fatbins.\nInvalidation is module-granular: a changed nvidia-cutlass-dsl version or\nkernel-source SHA256 wipes and lazily rebuilds the module. Reference usage:\n`flashinfer/quantization/kernels/nvfp4_quantize.py`. Disable with\n`FLASHINFER_CUTE_DSL_DISABLE_CACHE=1`.\n\n### Dispatch Macros\n\nHandle combinatorial parameter spaces:\n\n```cpp\nDISPATCH_DTYPE(input_dtype, DTypeIn, {\n  DISPATCH_DTYPE(output_dtype, DTypeOut, {\n    DISPATCH_BLOCK_SIZE(block_size, BLOCK_SIZE, {\n      LaunchKernel<DTypeIn, DTypeOut, BLOCK_SIZE>(...);\n    });\n  });\n});\n```\n\nDefined in `.jinja` files and expanded after rendering.\n\n## API Logging with @flashinfer_api\n\nFlashInfer provides the `@flashinfer_api` decorator for debugging API calls.\n\n**Key features:**\n- **Crash-safe**: Logs inputs BEFORE execution (preserves info even if kernel crashes)\n- **Zero overhead when disabled**: `FLASHINFER_LOGLEVEL=0` (default)\n- **Multiple verbosity levels**: 0 (off), 1 (names), 3 (inputs/outputs), 5 (+ statistics)\n- **CUDA graph compatible**: Auto-skips stats during graph capture\n\n**Quick usage:**\n```bash\n# Enable detailed logging\nexport FLASHINFER_LOGLEVEL=3              # 0, 1, 3, or 5\nexport FLASHINFER_LOGDEST=debug.log       # stdout, stderr, or file path\n\npython my_script.py\n```\n\n**Why use this?**\n- Debug CUDA crashes (see inputs that caused crash)\n- Track tensor shapes/dtypes through pipeline\n- Detect NaN/Inf issues (level 5)\n\n→ **For complete debugging guide, see [`.claude/skills/debug-cuda-crash/skill.md`](.claude/skills/debug-cuda-crash/skill.md)**\n\n## Debugging\n\n### Enable Logging\n\n```bash\nexport FLASHINFER_JIT_VERBOSE=1      # Verbose JIT output\nexport FLASHINFER_JIT_DEBUG=1        # Debug symbols, -O0\nexport FLASHINFER_LOGLEVEL=3         # API logging (0=off, 1=basic, 3=detailed)\nexport FLASHINFER_LOGDEST=stdout\n```\n\n### Inspect Generated Code\n\n```bash\n# Generated sources\nls -la ~/.cache/flashinfer/0.6.0/*/generated/\n\n# Compiled modules\nls -la ~/.cache/flashinfer/0.6.0/*/cached_ops/\n\n# Build files\ncat ~/.cache/flashinfer/0.6.0/*/cached_ops/*/build.ninja\n```\n\n### Environment Variables\n\n```bash\n# Compilation\nexport FLASHINFER_NVCC_THREADS=4              # Threads per nvcc process (--threads=N)\nexport MAX_JOBS=4                             # Parallel ninja jobs (nvcc processes)\nexport FLASHINFER_CUDA_ARCH_LIST=\"8.0 9.0a\"  # Target architectures\n# Memory note: total compilation memory ≈ MAX_JOBS × FLASHINFER_NVCC_THREADS × per-thread mem.\n\n# Behavior\nexport FLASHINFER_WORKSPACE_BASE=\"/scratch\"   # Custom cache directory\n```\n\n#### Full Environment Variable Reference\n\nAlready covered above (Quick Reference / Debugging): `FLASHINFER_LOGLEVEL`,\n`FLASHINFER_LOGDEST`, `FLASHINFER_JIT_VERBOSE`, `FLASHINFER_JIT_DEBUG`,\n`FLASHINFER_CUDA_ARCH_LIST`, `FLASHINFER_NVCC_THREADS`,\n`FLASHINFER_WORKSPACE_BASE`, `MAX_JOBS`.\n\nThe remaining `FLASHINFER_*` knobs read by the code are listed below. Defaults\nmatch what the code uses today; values are strings unless noted.\n\n##### JIT / Build Toolchain\n\n| Variable | Default | Read in | Effect |\n|----------|---------|---------|--------|\n| `FLASHINFER_DISABLE_JIT` | unset | `flashinfer/jit/core.py` | If set (any non-empty value), JIT compilation is refused and modules must already exist in the cache or be provided via AOT packages. |\n| `FLASHINFER_CUTE_DSL_DISABLE_CACHE` | `0` | `flashinfer/jit/cute_dsl_core.py` | `1` disables the on-disk cache for JIT-compiled CuTe-DSL kernels (every process recompiles via `cute.compile`). |\n| `FLASHINFER_DISABLE_VERSION_CHECK` | unset | `flashinfer/jit/env.py` | Skip the AOT/JIT-cache version check that pins flashinfer-jit-cache to the installed flashinfer-python. Bypass only when you intentionally mix versions. |\n| `FLASHINFER_JIT_LINEINFO` | `0` | `flashinfer/jit/core.py` | `1` adds `-lineinfo` to nvcc so profiler / `cuda-gdb` can map PTX back to CUDA source. |\n| `FLASHINFER_NVCC` | `$cuda_home/bin/nvcc` | `flashinfer/jit/cpp_ext.py` | Override the nvcc binary used by the JIT (useful for sccache wrappers or non-default CUDA installs). |\n| `FLASHINFER_NVCC_LAUNCHER` | `\"\"` | `flashinfer/jit/cpp_ext.py` | Optional launcher prefix for nvcc (e.g. `ccache`, `sccache`). Combined with `FLASHINFER_NVCC`. |\n| `FLASHINFER_CXX_LAUNCHER` | `\"\"` | `flashinfer/jit/cpp_ext.py` | Same idea as `FLASHINFER_NVCC_LAUNCHER` but for the host C++ compiler. |\n| `FLASHINFER_FMHA_V2_VERBOSE` | unset | `flashinfer/jit/attention/fmha_v2/fmha_library.py` (FMHA v2 codegen, C++ side) | When set, the FMHA-v2 codegen / runtime prints verbose dispatcher diagnostics. Leave unset for normal runs. |\n| `FLASHINFER_EXTRA_CFLAGS` | unset | `flashinfer/jit/cpp_ext.py` | Extra compiler flags passed to the host C++ compiler. |\n| `FLASHINFER_EXTRA_CUDAFLAGS` | unset | `flashinfer/jit/cpp_ext.py` | Extra compiler flags passed to `nvcc`. |\n| `FLASHINFER_EXTRA_LDFLAGS` | unset | `flashinfer/jit/cpp_ext.py` | Extra linker flags passed to the linker. |\n\n##### Cubin / Artifact Loader\n\n| Variable | Default | Read in | Effect |\n|----------|---------|---------|--------|\n| `FLASHINFER_CUBIN_DIR` | `<FLASHINFER_WORKSPACE_BASE>/.cache/flashinfer/cubins` | `flashinfer/jit/env.py` (exposed via `flashinfer/__main__.py show-config`) | Local directory used to cache downloaded cubins. Override to share a cache between users. |\n| `FLASHINFER_CUBINS_REPOSITORY` | `https://edge.urm.nvidia.com/artifactory/sw-kernelinferencelibrary-public-generic-local` | `flashinfer/jit/cubin_loader.py` | Base URL the loader downloads cubins from. Point to a mirror for offline or air-gapped setups. |\n| `FLASHINFER_CUBIN_CHECKSUM_DISABLED` | unset | `flashinfer/jit/cubin_loader.py` | If set, skip SHA checksum verification of downloaded cubins. Debug aid only. |\n| `FLASHINFER_CUBIN_DOWNLOAD_THREADS` | `4` | `flashinfer/artifacts.py` | Thread-pool size used by `flashinfer artifacts download`. |\n| `FLASHINFER_NO_DOWNLOAD` | unset | `flashinfer/jit/cubin_loader.py` | Hard-fail if a cubin is missing locally instead of attempting to download. Useful in CI / locked-down environments. |\n| `FLASHINFER_DSL_FMHA_LOCAL_DIR` | unset | `flashinfer/attention/cute_dsl/fmha.py` | Path to a local checkout of the CuTe-DSL FMHA kernel sources. The loader checks here before downloading. |\n| `FLASHINFER_LOGGING_LEVEL` | `INFO` | `flashinfer/artifacts.py`, `flashinfer/jit/core.py` | Python logging level for the artifacts/cubin loader and the JIT compiler (`DEBUG`/`INFO`/`WARNING`/`ERROR`). Distinct from `FLASHINFER_LOGLEVEL`. |\n| `FLASHINFER_DISABLE_TINYGEMM2_SM100` | `0` | `flashinfer/gemm/routergemm.py` | Set to `1` to disable the generated SM100/SM103 `tinygemm2` backend and force the dispatcher to fall back to the legacy implementation. Useful as an escape hatch when debugging backend-selection or kernel issues on Blackwell systems. |\n\n##### API Dump / Logging Extensions\n\nThese complement `FLASHINFER_LOGLEVEL`/`FLASHINFER_LOGDEST` and are all read in `flashinfer/api_logging.py`.\n\n| Variable | Default | Effect |\n|----------|---------|--------|\n| `FLASHINFER_DUMP_DIR` | `flashinfer_dumps` | Directory where `@flashinfer_api` writes per-call tensor dumps when logging is enabled. |\n| `FLASHINFER_DUMP_INCLUDE` | `\"\"` | Comma-separated allow-list of API names; only matching calls are dumped. Wildcards (`*`) supported. |\n| `FLASHINFER_DUMP_EXCLUDE` | `\"\"` | Comma-separated deny-list of API names; matching calls skip the (expensive) stats / dump path. |\n| `FLASHINFER_DUMP_MAX_COUNT` | `1000` | Hard cap on number of dumped events; once reached, further dumps are dropped with a warning. |\n| `FLASHINFER_DUMP_MAX_SIZE_GB` | `20` | Hard cap on total dump size in **gigabytes** (GB) written to `FLASHINFER_DUMP_DIR` (parsed as `float`; see `_DUMP_MAX_SIZE_GB` in `flashinfer/api_logging.py`). |\n| `FLASHINFER_DUMP_SAFETENSORS` | `0` | `1` writes dumps as `.safetensors` (portable / Hugging Face-style); otherwise plain `.pt`. |\n\n##### Trace Capture\n\nUsed by `flashinfer.trace` / `fi_trace`.\n\n| Variable | Default | Read in | Effect |\n|----------|---------|---------|--------|\n| `FLASHINFER_TRACE_DUMP` | unset | `flashinfer/fi_trace.py` | If set, decorated APIs auto-dump benchmark-definition JSON for each call (the \"fi_trace\" feature). |\n| `FLASHINFER_TRACE_DUMP_DIR` | cwd | `flashinfer/fi_trace.py` | Directory where the trace JSON files are written. |\n| `FLASHINFER_TRACE_APPLY` | `0` | `flashinfer/__init__.py`, `flashinfer/trace_apply/config.py` | Set to `1` to enable Trace Apply (runtime kernel substitution) when FlashInfer is imported. |\n| `FLASHINFER_TRACE_APPLY_PATH` | unset | `flashinfer/trace_apply/config.py` | Directory from which deployment-configured solutions are loaded for Trace Apply. |\n\n##### Validation / Autotuning / Routing / Kernel Selection\n\n| Variable | Default | Read in | Effect |\n|----------|---------|---------|--------|\n| `FLASHINFER_VALIDATE_INPUTS` | `0` | `flashinfer/mla/_core.py` (MLA wrapper) | Non-zero / non-empty value enables defensive input validation inside the MLA wrapper. Adds host-side overhead; intended for debugging. |\n| `FLASHINFER_AUTOTUNER_LOAD_FROM_FILE` | `0` | `flashinfer/autotuner/autotuner.py` | `1` loads previously serialized autotune results from disk instead of re-running the search. |\n| `FLASHINFER_DIST_AWARE_AUTOTUNE` | `0` | `flashinfer/fused_moe/da_config.py` | `1` enables experimental distribution-aware autotune and kernel dispatch (TRT-LLM MoE only). |\n| `FLASHINFER_AUTOTUNE_DIR` | unset | `flashinfer/mla/_sparse_mla_sm120.py` | Override the disk path for MLA AutoTuner cache files. Falls back to `FLASHINFER_WORKSPACE_DIR` when unset. |\n| `FLASHINFER_AUTOTUNE_TIMER` | unset (auto) | `flashinfer/autotuner/autotuner.py` | Selects the autotuner's per-tactic timer: `globaltimer` forces the GPU `%globaltimer` register, `cuda_event` forces `cudaEvent`, unset/anything-else auto-detects (uses `%globaltimer` only when Confidential Computing is detected). Under CC `cudaEventElapsedTime` is unreliable (can go negative), so the globaltimer path keeps tactic ranking stable. |\n| `FLASHINFER_CONFIDENTIAL_COMPUTE` | unset | `flashinfer/utils.py` | Override NVIDIA Confidential Computing (CC) auto-detection used by `is_confidential_compute()` (which drives the autotuner timer above): `1` forces CC, `0` forces non-CC. Useful for CI or hosts without `pynvml`. |\n| `FLASHINFER_TOPK_ALGO` | unset | `flashinfer/topk.py` | Force a specific top-k algorithm (otherwise the dispatcher chooses based on shape). Used for benchmarking / regression bisection. |\n| `FLASHINFER_USE_CUDA_NORM` | `0` | `flashinfer/norm/__init__.py` | `1` switches the norm path from the default backend to the legacy CUDA-only kernels. Diagnostic toggle. |\n| `FLASHINFER_ROUTING_FORCE_BLOCK_PER_TOKEN` | unset | `csrc/fused_moe/trtllm_backend/trtllm_fused_moe_routing_custom.cu` | Forces the TRT-LLM MoE custom-routing kernel into \"one-block-per-token\" mode regardless of the active routing policy. Mainly used to reproduce specific perf points. |\n| `FLASHINFER_B12X_MICRO_SHARE_INPUT` | `1` | `flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_dispatch.py` | `0` disables the B12x MoE micro-batch input-sharing optimization. Internal/experimental — leave at the default unless investigating an SM12x MoE regression. |\n| `FLASHINFER_B12X_FORCE_MOE_W4A16` | unset | `flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_dispatch.py` | When set (any non-empty value), forces the SM12x MoE dispatcher onto the W4A16 kernel path regardless of weight dtype. Internal/experimental — used to reproduce W4A16-specific issues. |\n| `FLASHINFER_TACTICS_BLOCKLIST` | unset | `flashinfer/autotuner/autotuner.py` | Path to a JSON tactics-blocklist file generated by `flashinfer tactics-blocklist generate` (or `python -m flashinfer tactics-blocklist generate`). When set, the autotuner loads the file at startup and skips any kernel tactics listed as invalid for the current GPU/driver environment, preventing hang or crash on known-bad tactics. |\n\n## Development Workflow\n\n### Typical Development Loop\n\n1. Edit kernel code in `include/flashinfer/some_kernel.cuh`\n2. Run test: `pytest tests/test_some_kernel.py::test_specific_case`\n3. FlashInfer detects changes and recompiles automatically\n4. No `pip install` needed!\n\n### Modifying Existing Kernels\n\n- **Kernel templates**: `include/flashinfer/**/*.cuh` - Changes picked up on next JIT compile\n- **Launcher code**: `csrc/*.cu` - May need changes if adding new template parameters\n- **Jinja templates**: `csrc/*.jinja` - Update if adding new config parameters\n- **Python API**: `flashinfer/*.py` - Update if changing function signatures\n\n### Creating Pre-compiled Packages\n\nWhen ready to distribute:\n\n```bash\n# Build flashinfer-jit-cache package\ncd flashinfer-jit-cache\nexport FLASHINFER_CUDA_ARCH_LIST=\"7.5 8.0 8.9 9.0a 10.0a 11.0a 12.0f\"\npython -m build --no-isolation --wheel\n```\n\nThis runs `flashinfer/aot.py` which calls all registered `gen_*_module()` functions and pre-compiles them.\n\n## Build System Details\n\n- **Build backend**: Custom PEP 517 backend in `build_backend.py`\n- **Data directories**: Build creates symlinks for editable installs:\n  - `3rdparty/cutlass` → `flashinfer/data/cutlass`\n  - `csrc` → `flashinfer/data/csrc`\n  - `include` → `flashinfer/data/include`\n- **Version**: Generated in `flashinfer/_build_meta.py` from `version.txt`\n\n## External Integrations\n\n### TVM-FFI: Cross-Language Unified ABI\n\nFlashInfer uses **TVM-FFI** (Apache TVM's Foreign Function Interface) for bindings, which provides a **cross-language unified ABI**. This means:\n\n- **Not limited to PyTorch**: The same compiled kernels can be used from multiple frameworks\n- **Language agnostic**: Bindings can be created for Python, C++, Rust, etc.\n- **Type-safe marshaling**: Automatic tensor/array conversion between languages\n- **Export syntax**: Use `TVM_FFI_DLL_EXPORT_TYPED_FUNC(name, func)` to expose C++ functions\n\nWhile FlashInfer currently provides PyTorch bindings, the underlying kernels are framework-agnostic thanks to TVM-FFI.\n\n### Other Integrations\n\n- **PyTorch Custom Ops**: `torch.library` for `torch.compile()` and CUDA graph support\n- **Ninja Build**: Direct ninja generation, no CMake complexity\n\n## Supported GPU Architectures\n\nFlashInfer supports NVIDIA SM75, SM80, SM86, SM89, SM90, SM100, SM103, SM110, SM120, and SM121.\n\n## Release Versioning\n\nFlashInfer follows a \"right-shifted\" versioning scheme (`major.minor.patch[.post1]`):\n\n- **major**: Architectural milestone and/or incompatible API changes (similar to PyTorch 2.0)\n- **minor**: Significant backwards-compatible new features\n- **patch**: Small backwards-compatible features (new kernels, new SM support) and backwards-compatible bug fixes\n- **post1**: Optional suffix for quick follow-up release with just backwards-compatible bug fixes\n\n## External Documentation Resources\n\nWhen working with FlashInfer's dependencies and tools, refer to these official documentation sources:\n\n### Core Dependencies\n\n- **TVM-FFI**: Apache TVM's Foreign Function Interface\n  - Documentation: <https://tvm.apache.org/ffi/>\n  - Package: `apache-tvm-ffi` (<https://pypi.org/project/apache-tvm-ffi/>)\n  - Use for: Understanding FFI export syntax, cross-language bindings\n\n- **CUTLASS**: NVIDIA's CUDA Templates for Linear Algebra Subroutines\n  - **Recommended**: Read source code directly in `3rdparty/cutlass/` (documentation is often outdated)\n  - Repository: <https://github.com/NVIDIA/cutlass>\n  - Use for: GEMM kernel implementations, tensor core operations\n\n- **CuTe (CUTE DSL)**: CUTLASS's Cute Layout and Tensor DSL\n  - Documentation: <https://docs.nvidia.com/cutlass/media/docs/pythonDSL/cute_dsl.html>\n  - **Tip**: Add `.md` to get Markdown format: <https://docs.nvidia.com/cutlass/media/docs/pythonDSL/cute_dsl.html.md>\n  - The Cute DSL kernels rely on Python modules from the `nvidia-cutlass-dsl` pip package, not to be confused with Python modules in the `3rdparty/cutlass` submodule\n  - Tutorial: <https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL>\n\n- **PTX ISA (Parallel Thread Execution)**: NVIDIA's PTX instruction set documentation\n  - Documentation: <https://docs.nvidia.com/cuda/parallel-thread-execution/>\n  - **Index/Table of Contents**: <https://docs.nvidia.com/cuda/parallel-thread-execution/index.html.md>\n  - **Tip**: Add `.md` to any page URL to get Markdown format\n  - Use for: Low-level instruction details, new GPU architecture features, inline PTX assembly\n\n### When to Consult These Docs\n\n- **Understanding new GPU architecture features** → Check PTX ISA documentation for latest instruction details\n- **Working on FFI bindings** → Check TVM-FFI docs for export patterns and type marshaling\n- **Implementing Tensor-Core kernels using CUTLASS** → Read source code in `3rdparty/cutlass/`\n- **Using tensor layouts or warp-level operations** → Refer to CuTe documentation\n- **Writing inline PTX assembly** → Consult PTX ISA for instruction syntax and semantics\n\nThese dependencies are included in FlashInfer's `3rdparty/` directory or `requirements.txt`.\n\n### Some final suggestions for all AI agents\n\n> Because practical engineering involves the accumulated experience of trial and error, match the coding style, efficiency, complexity, verbosity, and defensiveness by learning from existing code as much as possible—this document contains many pointers on where to find examples. Document intentional departures with rationale. Mentioning \"AI-assisted\" in the git commit message is good transparency. For performance-critical hot paths, leave justification for the special algorithmic choices and other potential alternatives in a comment for review.\n\n**Keep documentation in sync with code changes:** When modifying code that is referenced in this document or in `.claude/skills/`, update the corresponding documentation immediately. This includes:\n- Important infrastructure changes (e.g., `@flashinfer_api`, `@backend_requirement`, TVM-FFI macros) → Update examples in `CLAUDE.md` and relevant skill files\n- New patterns or conventions → Document them for future reference\n- Deprecated approaches → Remove or mark as deprecated in docs\n- New error handling patterns, macros, or utilities → Add to relevant skill tutorials\n"},"files":{"AGENTS.md":"This repository’s agent instructions live in [CLAUDE.md](./CLAUDE.md).\n\nBefore making changes, read `CLAUDE.md` and follow its guidance for build, test, style, and contribution workflows.\n","CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nFlashInfer is a GPU kernel library for LLM serving that uses **JIT (Just-In-Time) compilation by default**. This means kernel code changes are automatically picked up without reinstalling the package - extremely convenient for development.\n\n## Quick Reference\n\n| Task | Command |\n|------|---------|\n| Install for development | `pip install --no-build-isolation -e . -v` |\n| Initialize submodules | `git submodule update --init --recursive` |\n| Install CUPTI for benchmarking | `pip install -U cupti-python` |\n| Run all tests | `pytest tests/` |\n| Run specific test | `pytest tests/path/test_file.py::test_function` |\n| Run multi-GPU test | `mpirun -np 4 pytest tests/comm/test_allreduce_unified_api.py` |\n| Run benchmark | `python benchmarks/flashinfer_benchmark.py --routine <name> <flags>` |\n| Run linting | `pre-commit run -a` |\n| Dump environment report (bug reports) | `python -m flashinfer.collect_env` (or `flashinfer collect-env [--json]`) |\n| Install pre-commit hooks | `pre-commit install` |\n| Clear JIT cache | `rm -rf ~/.cache/flashinfer/` |\n| Enable API logging (basic) | `export FLASHINFER_LOGLEVEL=1` |\n| Enable API logging (detailed) | `export FLASHINFER_LOGLEVEL=3` |\n| Enable API logging (with stats) | `export FLASHINFER_LOGLEVEL=5` |\n| Set API log destination | `export FLASHINFER_LOGDEST=mylog.txt` |\n| Enable verbose JIT logging | `export FLASHINFER_JIT_VERBOSE=1` |\n| Enable debug build | `export FLASHINFER_JIT_DEBUG=1` |\n| Set target architectures | `export FLASHINFER_CUDA_ARCH_LIST=\"8.0 9.0a\"` |\n| Set parallel compilation | `export FLASHINFER_NVCC_THREADS=4` |\n| Limit parallel ninja jobs | `export MAX_JOBS=4` |\n| Enable GDN native short-T path | `export FLASHINFER_GDN_WY_NATIVE_T=1` |\n| Enable GDN strided QKV path | `export FLASHINFER_GDN_WY_STRIDED_QKV=1` |\n| Enable GDN native A/B tensors | `export FLASHINFER_GDN_WY_NATIVE_AB=1` |\n| Override CuTe-DSL prefill scheduling | `export FLASHINFER_CUTE_PREFILL_PERSISTENT=0` (non-persistent) or `1` (persistent) |\n| Skip MoE EP CuTe-DSL import/version guard | `export FLASHINFER_MOE_EP_SKIP_DSL_CHECK=1` |\n| Override MoE EP knob-cache path | `export FLASHINFER_MOE_EP_KNOB_CACHE=/path/to/knobs.json` |\n| Disable MoE EP fused staging kernel | `export FLASHINFER_MEGA_FUSED_STAGE=0` |\n| Enable distribution-aware MoE autotune and kernel dispatch (experimental; TRT-LLM MoE only) | `export FLASHINFER_DIST_AWARE_AUTOTUNE=1` |\n\nThe minimum Python version used by CI and build tooling is defined in\n`.python-version`. CI Docker images use a stable Conda environment name and\nderive their interpreter and site-packages paths from that file. When raising\nthe minimum, also update `project.requires-python` and `tool.mypy.python_version`\nin `pyproject.toml`.\n\n## Quick Start for Development\n\n### Installation\n\n```bash\ngit clone https://github.com/flashinfer-ai/flashinfer.git --recursive\ncd flashinfer\npip install --no-build-isolation -e . -v\n```\n\n**Important**: The `--recursive` flag is required to initialize submodules in `3rdparty/` (cutlass, spdlog).\n\nIf you forgot `--recursive` when cloning:\n```bash\ngit submodule update --init --recursive\n```\n\nThat's it! You can now:\n\n- Run all benchmarks and unit tests\n- Modify kernel source code in `include/` without reinstalling\n- Changes are JIT-compiled on next use\n\nThe `--no-build-isolation` flag prevents pip from pulling incompatible PyTorch/CUDA versions from PyPI.\n\n### How JIT Compilation Works\n\nWhen you call a FlashInfer API:\n\n1. **First call**: Generates specialized CUDA code based on parameters (dtype, head_dim, etc.), compiles it with ninja, caches the .so file\n2. **Subsequent calls**: Uses cached compiled module\n3. **After kernel changes**: Automatically detects changes and recompiles\n\n**No manual rebuild step needed** - just edit `.cuh` files and run your code again.\n\n### Pre-compiled Packages (Optional)\n\nFlashInfer provides optional pre-compiled packages for users who want faster initialization:\n\n- `flashinfer-jit-cache`: Pre-built kernel cache\n- `flashinfer-cubin`: Pre-compiled kernel binaries\n\n**For development, you typically DON'T need these.** JIT compilation is fast enough and gives you live code reload.\n\n## Testing\n\nRun all tests:\n\n```bash\npytest tests/\n```\n\nRun specific test file:\n\n```bash\npytest tests/attention/test_hopper.py\n```\n\nRun specific test function:\n\n```bash\npytest tests/attention/test_hopper.py::test_single_prefill\n```\n\n### Skipping Tests Based on CUDA Architecture\n\nUse `flashinfer.utils` functions to skip tests on unsupported GPU architectures:\n\n**Available check functions:**\n- `get_compute_capability(device)` - Returns `(major, minor)` tuple\n- `is_sm90a_supported()` - Hopper (requires CUDA 12.3+)\n- `is_sm100a_supported()` - Blackwell (requires CUDA 12.8+)\n- `is_sm100f_supported()` - Blackwell feature-set (requires CUDA 12.9+)\n- `is_sm110a_supported()`, `is_sm120a_supported()`, `is_sm120f_supported()`, `is_sm121a_supported()`\n\n**APIs decorated with `@backend_requirement`** also provide:\n- `api_name.is_compute_capability_supported(cc)` - e.g., `mm_fp4.is_compute_capability_supported(100)`\n- `api_name.is_backend_supported(\"backend\")` - e.g., `mm_fp4.is_backend_supported(\"cudnn\")`\n- `api_name.is_backend_supported(\"backend\", cc)` - e.g., `mm_fp4.is_backend_supported(\"cudnn\", 80)`\n\n**Example:**\n```python\nfrom flashinfer.utils import is_sm90a_supported\n\ndef test_hopper_attention():\n    if not is_sm90a_supported(torch.device(\"cuda\")):\n        pytest.skip(\"Requires SM90a\")\n    # Test code...\n```\n\n**Common requirements:**\n\n| Feature | Min SM | Check Function |\n|---------|--------|----------------|\n| FlashAttention-3 | SM90a | `is_sm90a_supported()` |\n| MLA Attention | SM100a | `is_sm100a_supported()` |\n| FP8 GEMM | SM89+ | `get_compute_capability()[0] >= 9` |\n\n**Note:** `tests/conftest.py` auto-skips tests that trigger OOM, but tests should be written to avoid OOM by using appropriate problem sizes.\n\n## Benchmarking\n\nFlashInfer provides a unified benchmarking framework in `benchmarks/flashinfer_benchmark.py`.\n\n**Key features:**\n- Supports attention, GEMM, and MOE kernels\n- Multiple backends: FlashAttention2/3, cuDNN, CUTLASS, TensorRT-LLM, cuBLAS\n- **CUPTI timing (recommended)**: Hardware-level profiling for accurate GPU kernel time\n  - Automatically falls back to CUDA events if CUPTI unavailable\n  - Install: `pip install -U cupti-python` (requires CUDA 13+)\n- Batch testing, reference checking, CSV output\n\n**Quick example:**\n```bash\npython benchmarks/flashinfer_benchmark.py \\\n    --routine BatchDecodeWithPagedKVCacheWrapper \\\n    --backends fa2 cudnn \\\n    --batch_size 32 --s_kv 2048 \\\n    --num_qo_heads 32 --num_kv_heads 8 \\\n    --head_dim_qk 128 --head_dim_vo 128 \\\n    --page_size 16 --refcheck -vv\n```\n\n**Python API:**\n```python\nfrom flashinfer.testing import bench_gpu_time\n\n# CUPTI preferred, auto-fallback to CUDA events\nmedian_time, std_time = bench_gpu_time(\n    my_kernel, args=(x, y), enable_cupti=True, num_iters=30\n)\n```\n\n→ **For complete benchmarking guide, see [`.claude/skills/benchmark-kernel/skill.md`](.claude/skills/benchmark-kernel/skill.md)**\n\n## Code Linting\n\nRun all pre-commit hooks:\n\n```bash\npre-commit run -a\n```\n\nInstall hooks to run on every commit:\n\n```bash\npre-commit install\n```\n\n## Code Review\n\nWhen reviewing a diff (as an agent or a human), follow the shared focus areas, kernel-review\npolicy, and effort calibration in [`docs/code_review_guidance.md`](docs/code_review_guidance.md).\nNote: unlike human review, agents keep **kernel implementation details in scope** — read the\nkernel logic and report bugs, labeling findings by confidence.\n\n→ **For the complete review rule set, see [`docs/code_review_guidance.md`](docs/code_review_guidance.md)**\n\n## Architecture: JIT Compilation System\n\nFlashInfer's JIT system has three layers:\n\n### Layer 1: JitSpec (flashinfer/jit/core.py)\n\n`JitSpec` is an abstract base class defining the kernel-module lifecycle\n(`try_load()` / `build()` / `load()`, with the shared `build_and_load()`\ntemplate method handling caching, locking, and `FLASHINFER_DISABLE_JIT`).\nOne subclass per compilation toolchain:\n\n- `JitSpecNvcc` (nvcc/ninja modules, returned by `gen_jit_spec()`) defines:\n  - `name`: Unique identifier (URI hash from parameters)\n  - `sources`: List of .cu/.cpp files to compile\n  - `extra_cuda_cflags`, `extra_cflags`, `extra_ldflags`: Compiler flags\n- `JitSpecCuteDsl` (flashinfer/jit/cute_dsl_core.py) caches CuTe-DSL kernels\n  (see \"CuTe-DSL kernels\" under Module Caching below)\n\n### JIT Directory Rules\n\n**NEVER write to package directories** - they may be read-only after installation.\n\n| Directory | Writable | Use for |\n|-----------|----------|---------|\n| `FLASHINFER_GEN_SRC_DIR` | ✓ Yes | Generated source files (Jinja output, copied .cu files) |\n| `FLASHINFER_JIT_DIR` | ✓ Yes | Compiled `.so` outputs |\n| `FLASHINFER_CSRC_DIR` | ✗ No | Read-only source templates |\n| `FLASHINFER_AOT_DIR` | ✗ No | Read-only pre-compiled binaries |\n\n### Compilation Context: Architecture-Specific Compilation\n\nFlashInfer uses `CompilationContext` to manage CUDA architecture targets. Some kernels only work on specific GPU architectures (e.g., Hopper SM90, Blackwell SM100/SM12x).\n\n**How it works:**\n- Auto-detects GPUs in system or reads `FLASHINFER_CUDA_ARCH_LIST` environment variable\n- CuTe-DSL FP4 MM compilation parallelism can be controlled with `FLASHINFER_MM_FP4_CUTE_DSL_COMPILE_WORKERS`\n- JIT modules specify `supported_major_versions=[9, 10, 11, 12]` to limit compilation to specific SM versions\n- If GPU not supported → `RuntimeError: No supported CUDA architectures found`\n\n→ **See [`.claude/skills/add-cuda-kernel/skill.md`](.claude/skills/add-cuda-kernel/skill.md) for usage examples**\n\n### Layer 2: Code Generation\n\nEvery `gen_*_module()` function in `flashinfer/jit/` follows this pattern:\n\n```python\ndef gen_some_module(dtype_in, dtype_out, ...):\n    # 1. Compute unique identifier from parameters\n    uri = get_some_uri(dtype_in, dtype_out, ...)\n\n    # 2. Create generation directory\n    gen_directory = jit_env.FLASHINFER_GEN_SRC_DIR / uri\n\n    # 3. (Optional) Render Jinja template to generate type-specialized config\n    # Skip this step if you don't need type specialization\n    with open(jit_env.FLASHINFER_CSRC_DIR / \"some_customize_config.jinja\") as f:\n        template = jinja2.Template(f.read())\n    config_content = template.render(\n        dtype_in=dtype_map[dtype_in],\n        dtype_out=dtype_map[dtype_out],\n        # ... more parameters\n    )\n    write_if_different(gen_directory / \"some_config.inc\", config_content)\n\n    # 4. Copy source files to gen directory\n    sources = []\n    for fname in [\"some_kernel.cu\", \"some_jit_binding.cu\"]:\n        shutil.copy(jit_env.FLASHINFER_CSRC_DIR / fname, gen_directory / fname)\n        sources.append(gen_directory / fname)\n\n    # 5. Return JitSpec\n    return gen_jit_spec(uri, sources, extra_cuda_cflags=[...])\n```\n\n**Note**: If your operation doesn't need type specialization, you can skip step 3 entirely and just copy the source files directly.\n\n### Layer 3: Compilation and Loading\n\n`JitSpecNvcc` methods:\n\n- `write_ninja()` - Generates `build.ninja` file\n- `build()` - Executes `ninja` to compile sources\n- `build_and_load()` - Compiles and loads via TVM-FFI\n\nThe generated `build.ninja` file uses nvcc to compile .cu → .cuda.o → .so, then loads via TVM-FFI.\n\n### Jinja Templates (Optional)\n\n**Note: Jinja templates are NOT required.** You can write C++ code directly without templating.\n\nFor operations that need type specialization, templates in `csrc/*.jinja` can generate C++ code:\n\n```jinja\n// Input template\nusing DTypeIn = {{ dtype_in }};\nusing DTypeOut = {{ dtype_out }};\nconstexpr int PARAM = {{ param_value }};\n\n// After render\nusing DTypeIn = float16;\nusing DTypeOut = float16;\nconstexpr int PARAM = 128;\n```\n\nThis allows the same CUDA template code to be compiled with different concrete types. However, if your operation doesn't need this, you can skip Jinja and write the `.cu` files directly.\n\n## Directory Structure\n\n```\nflashinfer/\n├── include/flashinfer/           # Header-only CUDA kernel templates\n│   ├── attention/                # Attention kernels\n│   ├── gemm/                     # GEMM kernels\n│   ├── comm/                     # Communication kernels\n│   ├── mma.cuh                   # Matrix multiply utilities\n│   ├── utils.cuh                 # Common utilities\n│   └── [...]\n│\n├── csrc/                          # Framework bindings (via TVM-FFI)\n│   ├── *.cu                       # Kernel launcher implementations\n│   ├── *_jit_binding.cu           # TVM-FFI exports\n│   ├── *_customize_config.jinja   # Type config templates (optional)\n│   └── [...]\n│\n├── flashinfer/                    # Python package\n│   ├── jit/\n│   │   ├── core.py                # JitSpec, compilation infrastructure\n│   │   ├── cpp_ext.py             # Ninja build generation\n│   │   ├── env.py                 # Workspace paths\n│   │   ├── attention/             # Attention module generators\n│   │   ├── gemm/                  # GEMM module generators\n│   │   ├── fused_moe/             # MOE module generators\n│   │   └── [...]\n│   ├── gemm/                      # GEMM Python APIs\n│   ├── fused_moe/                 # MOE Python APIs\n│   ├── comm/                      # Communication Python APIs\n│   ├── *.py                       # Other high-level Python APIs\n│   ├── aot.py                     # AOT compilation for pre-built packages\n│   └── [...]\n│\n├── tests/                         # Test suite\n│   ├── attention/                 # Attention kernel tests\n│   ├── gemm/                      # GEMM kernel tests\n│   ├── moe/                       # MOE kernel tests\n│   ├── comm/                      # Communication tests\n│   ├── utils/                     # Utility tests\n│   └── conftest.py                # Pytest configuration\n│\n└── build_backend.py               # PEP 517 build backend\n```\n\n### Critical Rule: Framework Separation\n\n**Torch headers MUST NOT be included in `include/` directory files.**\n\n- `include/`: Framework-agnostic CUDA kernels (accept raw pointers)\n- `csrc/`: Framework bindings via TVM-FFI (currently PyTorch, but can support other frameworks)\n\n## Adding a New Operation\n\n→ **For complete step-by-step tutorial, see [`.claude/skills/add-cuda-kernel/skill.md`](.claude/skills/add-cuda-kernel/skill.md)**\n\n**Quick overview of the process:**\n1. Write kernel in `include/flashinfer/new_op.cuh` (framework-agnostic, raw pointers)\n2. Write launcher in `csrc/new_op.cu` (PyTorch tensor handling)\n3. Create TVM-FFI bindings in `csrc/new_op_jit_binding.cu`\n4. (Optional) Create Jinja template for type specialization\n5. Write JIT module generator in `flashinfer/jit/new_op.py`\n6. Write Python API in `flashinfer/new_op.py` with `@functools.cache`\n7. Write tests in `tests/`\n8. Register in `flashinfer/aot.py` for AOT compilation\n9. Export in `flashinfer/__init__.py`\n10. Add a `TraceTemplate` in `flashinfer/trace/templates/` and wire it via `@flashinfer_api(trace=...)` (see below)\n11. Add an example call in `tests/trace/example.py`, re-run to regenerate `fi_trace_out/`, and commit the new JSON files\n\n### Trace Template Checklist (for new or updated APIs)\n\nEvery public API decorated with `@flashinfer_api` should also carry a `trace=` argument so that `fi_trace()` works and auto-dump produces a benchmark definition JSON.\n\n1. **Create or update a `TraceTemplate`** in `flashinfer/trace/templates/<category>.py` (e.g., `norm.py`, `activation.py`, `cascade.py`, `gdn.py`). Define `axes`, `inputs`, `outputs`, and optionally a `reference` function.\n2. **Wire the template** to the API: `@flashinfer_api(trace=my_trace)` on the Python function (or class method's `run()`).\n3. **Add an example call** in `tests/trace/example.py` that exercises the new trace with realistic shapes.\n4. **Regenerate examples**: `rm -rf tests/trace/fi_trace_out && python tests/trace/example.py` — verify the expected JSON appears.\n5. **Update the docstring** in `tests/trace/example.py` to list the new file(s).\n6. **Run tests**: `pytest tests/trace/ -v` — all template-consistency and end-to-end tests must pass.\n7. **Commit the new JSON files** under `tests/trace/fi_trace_out/` alongside the code changes.\n\n**Example implementations:**\n- **Simple**: `flashinfer/norm/__init__.py` (RMSNorm) - no Jinja, good starting point\n- **Moderate**: `flashinfer/sampling.py` - with Jinja templating\n- **Complex**: `flashinfer/decode.py` - plan-run pattern, advanced workspace\n\n## Key Architectural Patterns\n\n### Module Caching\n\nFlashInfer uses two-level caching to avoid recompilation:\n\n1. **Python-level** (`@functools.cache`): In-memory cache of loaded modules\n2. **File-level** (`~/.cache/flashinfer/`): Compiled `.so` files on disk\n\n**Cache invalidation** (automatic):\n- Source file changes (SHA256 hash)\n- Compilation flags change\n- CUDA architecture change\n- FlashInfer version change\n\nURI computed as: `hash(operation_type + parameters + source_hashes + flags + cuda_arch)`\n\n**Cache management:**\n- Clear cache: `rm -rf ~/.cache/flashinfer/`\n- Override location: `export FLASHINFER_WORKSPACE_BASE=\"/scratch\"`\n\n**CuTe-DSL kernels** (`flashinfer/jit/cute_dsl_core.py`): `cute.compile()` has no\npersistent cache, so `JitSpecCuteDsl` (a `JitSpec` subclass, wrapped by the\n`build_and_load_cute_dsl_kernel()` helper) exports compiled kernels as\nobject files (`export_to_c()`) and reloads them with `cute.runtime.load_module(...,\nenable_tvm_ffi=True)`. Artifacts live in `cached_ops/` next to the nvcc modules, one\ndirectory per op family — `cached_ops/<module>_<arch>_cute_dsl/` (e.g.\n`nvfp4_quantize_sm100a_cute_dsl/`) holding one `meta.json` plus one `.o` per\nspecialization. The arch comes from the DSL's compile target (`CUTE_DSL_ARCH` or the\ncurrent device) since the artifacts are single-arch, unlike nvcc fatbins.\nInvalidation is module-granular: a changed nvidia-cutlass-dsl version or\nkernel-source SHA256 wipes and lazily rebuilds the module. Reference usage:\n`flashinfer/quantization/kernels/nvfp4_quantize.py`. Disable with\n`FLASHINFER_CUTE_DSL_DISABLE_CACHE=1`.\n\n### Dispatch Macros\n\nHandle combinatorial parameter spaces:\n\n```cpp\nDISPATCH_DTYPE(input_dtype, DTypeIn, {\n  DISPATCH_DTYPE(output_dtype, DTypeOut, {\n    DISPATCH_BLOCK_SIZE(block_size, BLOCK_SIZE, {\n      LaunchKernel<DTypeIn, DTypeOut, BLOCK_SIZE>(...);\n    });\n  });\n});\n```\n\nDefined in `.jinja` files and expanded after rendering.\n\n## API Logging with @flashinfer_api\n\nFlashInfer provides the `@flashinfer_api` decorator for debugging API calls.\n\n**Key features:**\n- **Crash-safe**: Logs inputs BEFORE execution (preserves info even if kernel crashes)\n- **Zero overhead when disabled**: `FLASHINFER_LOGLEVEL=0` (default)\n- **Multiple verbosity levels**: 0 (off), 1 (names), 3 (inputs/outputs), 5 (+ statistics)\n- **CUDA graph compatible**: Auto-skips stats during graph capture\n\n**Quick usage:**\n```bash\n# Enable detailed logging\nexport FLASHINFER_LOGLEVEL=3              # 0, 1, 3, or 5\nexport FLASHINFER_LOGDEST=debug.log       # stdout, stderr, or file path\n\npython my_script.py\n```\n\n**Why use this?**\n- Debug CUDA crashes (see inputs that caused crash)\n- Track tensor shapes/dtypes through pipeline\n- Detect NaN/Inf issues (level 5)\n\n→ **For complete debugging guide, see [`.claude/skills/debug-cuda-crash/skill.md`](.claude/skills/debug-cuda-crash/skill.md)**\n\n## Debugging\n\n### Enable Logging\n\n```bash\nexport FLASHINFER_JIT_VERBOSE=1      # Verbose JIT output\nexport FLASHINFER_JIT_DEBUG=1        # Debug symbols, -O0\nexport FLASHINFER_LOGLEVEL=3         # API logging (0=off, 1=basic, 3=detailed)\nexport FLASHINFER_LOGDEST=stdout\n```\n\n### Inspect Generated Code\n\n```bash\n# Generated sources\nls -la ~/.cache/flashinfer/0.6.0/*/generated/\n\n# Compiled modules\nls -la ~/.cache/flashinfer/0.6.0/*/cached_ops/\n\n# Build files\ncat ~/.cache/flashinfer/0.6.0/*/cached_ops/*/build.ninja\n```\n\n### Environment Variables\n\n```bash\n# Compilation\nexport FLASHINFER_NVCC_THREADS=4              # Threads per nvcc process (--threads=N)\nexport MAX_JOBS=4                             # Parallel ninja jobs (nvcc processes)\nexport FLASHINFER_CUDA_ARCH_LIST=\"8.0 9.0a\"  # Target architectures\n# Memory note: total compilation memory ≈ MAX_JOBS × FLASHINFER_NVCC_THREADS × per-thread mem.\n\n# Behavior\nexport FLASHINFER_WORKSPACE_BASE=\"/scratch\"   # Custom cache directory\n```\n\n#### Full Environment Variable Reference\n\nAlready covered above (Quick Reference / Debugging): `FLASHINFER_LOGLEVEL`,\n`FLASHINFER_LOGDEST`, `FLASHINFER_JIT_VERBOSE`, `FLASHINFER_JIT_DEBUG`,\n`FLASHINFER_CUDA_ARCH_LIST`, `FLASHINFER_NVCC_THREADS`,\n`FLASHINFER_WORKSPACE_BASE`, `MAX_JOBS`.\n\nThe remaining `FLASHINFER_*` knobs read by the code are listed below. Defaults\nmatch what the code uses today; values are strings unless noted.\n\n##### JIT / Build Toolchain\n\n| Variable | Default | Read in | Effect |\n|----------|---------|---------|--------|\n| `FLASHINFER_DISABLE_JIT` | unset | `flashinfer/jit/core.py` | If set (any non-empty value), JIT compilation is refused and modules must already exist in the cache or be provided via AOT packages. |\n| `FLASHINFER_CUTE_DSL_DISABLE_CACHE` | `0` | `flashinfer/jit/cute_dsl_core.py` | `1` disables the on-disk cache for JIT-compiled CuTe-DSL kernels (every process recompiles via `cute.compile`). |\n| `FLASHINFER_DISABLE_VERSION_CHECK` | unset | `flashinfer/jit/env.py` | Skip the AOT/JIT-cache version check that pins flashinfer-jit-cache to the installed flashinfer-python. Bypass only when you intentionally mix versions. |\n| `FLASHINFER_JIT_LINEINFO` | `0` | `flashinfer/jit/core.py` | `1` adds `-lineinfo` to nvcc so profiler / `cuda-gdb` can map PTX back to CUDA source. |\n| `FLASHINFER_NVCC` | `$cuda_home/bin/nvcc` | `flashinfer/jit/cpp_ext.py` | Override the nvcc binary used by the JIT (useful for sccache wrappers or non-default CUDA installs). |\n| `FLASHINFER_NVCC_LAUNCHER` | `\"\"` | `flashinfer/jit/cpp_ext.py` | Optional launcher prefix for nvcc (e.g. `ccache`, `sccache`). Combined with `FLASHINFER_NVCC`. |\n| `FLASHINFER_CXX_LAUNCHER` | `\"\"` | `flashinfer/jit/cpp_ext.py` | Same idea as `FLASHINFER_NVCC_LAUNCHER` but for the host C++ compiler. |\n| `FLASHINFER_FMHA_V2_VERBOSE` | unset | `flashinfer/jit/attention/fmha_v2/fmha_library.py` (FMHA v2 codegen, C++ side) | When set, the FMHA-v2 codegen / runtime prints verbose dispatcher diagnostics. Leave unset for normal runs. |\n| `FLASHINFER_EXTRA_CFLAGS` | unset | `flashinfer/jit/cpp_ext.py` | Extra compiler flags passed to the host C++ compiler. |\n| `FLASHINFER_EXTRA_CUDAFLAGS` | unset | `flashinfer/jit/cpp_ext.py` | Extra compiler flags passed to `nvcc`. |\n| `FLASHINFER_EXTRA_LDFLAGS` | unset | `flashinfer/jit/cpp_ext.py` | Extra linker flags passed to the linker. |\n\n##### Cubin / Artifact Loader\n\n| Variable | Default | Read in | Effect |\n|----------|---------|---------|--------|\n| `FLASHINFER_CUBIN_DIR` | `<FLASHINFER_WORKSPACE_BASE>/.cache/flashinfer/cubins` | `flashinfer/jit/env.py` (exposed via `flashinfer/__main__.py show-config`) | Local directory used to cache downloaded cubins. Override to share a cache between users. |\n| `FLASHINFER_CUBINS_REPOSITORY` | `https://edge.urm.nvidia.com/artifactory/sw-kernelinferencelibrary-public-generic-local` | `flashinfer/jit/cubin_loader.py` | Base URL the loader downloads cubins from. Point to a mirror for offline or air-gapped setups. |\n| `FLASHINFER_CUBIN_CHECKSUM_DISABLED` | unset | `flashinfer/jit/cubin_loader.py` | If set, skip SHA checksum verification of downloaded cubins. Debug aid only. |\n| `FLASHINFER_CUBIN_DOWNLOAD_THREADS` | `4` | `flashinfer/artifacts.py` | Thread-pool size used by `flashinfer artifacts download`. |\n| `FLASHINFER_NO_DOWNLOAD` | unset | `flashinfer/jit/cubin_loader.py` | Hard-fail if a cubin is missing locally instead of attempting to download. Useful in CI / locked-down environments. |\n| `FLASHINFER_DSL_FMHA_LOCAL_DIR` | unset | `flashinfer/attention/cute_dsl/fmha.py` | Path to a local checkout of the CuTe-DSL FMHA kernel sources. The loader checks here before downloading. |\n| `FLASHINFER_LOGGING_LEVEL` | `INFO` | `flashinfer/artifacts.py`, `flashinfer/jit/core.py` | Python logging level for the artifacts/cubin loader and the JIT compiler (`DEBUG`/`INFO`/`WARNING`/`ERROR`). Distinct from `FLASHINFER_LOGLEVEL`. |\n| `FLASHINFER_DISABLE_TINYGEMM2_SM100` | `0` | `flashinfer/gemm/routergemm.py` | Set to `1` to disable the generated SM100/SM103 `tinygemm2` backend and force the dispatcher to fall back to the legacy implementation. Useful as an escape hatch when debugging backend-selection or kernel issues on Blackwell systems. |\n\n##### API Dump / Logging Extensions\n\nThese complement `FLASHINFER_LOGLEVEL`/`FLASHINFER_LOGDEST` and are all read in `flashinfer/api_logging.py`.\n\n| Variable | Default | Effect |\n|----------|---------|--------|\n| `FLASHINFER_DUMP_DIR` | `flashinfer_dumps` | Directory where `@flashinfer_api` writes per-call tensor dumps when logging is enabled. |\n| `FLASHINFER_DUMP_INCLUDE` | `\"\"` | Comma-separated allow-list of API names; only matching calls are dumped. Wildcards (`*`) supported. |\n| `FLASHINFER_DUMP_EXCLUDE` | `\"\"` | Comma-separated deny-list of API names; matching calls skip the (expensive) stats / dump path. |\n| `FLASHINFER_DUMP_MAX_COUNT` | `1000` | Hard cap on number of dumped events; once reached, further dumps are dropped with a warning. |\n| `FLASHINFER_DUMP_MAX_SIZE_GB` | `20` | Hard cap on total dump size in **gigabytes** (GB) written to `FLASHINFER_DUMP_DIR` (parsed as `float`; see `_DUMP_MAX_SIZE_GB` in `flashinfer/api_logging.py`). |\n| `FLASHINFER_DUMP_SAFETENSORS` | `0` | `1` writes dumps as `.safetensors` (portable / Hugging Face-style); otherwise plain `.pt`. |\n\n##### Trace Capture\n\nUsed by `flashinfer.trace` / `fi_trace`.\n\n| Variable | Default | Read in | Effect |\n|----------|---------|---------|--------|\n| `FLASHINFER_TRACE_DUMP` | unset | `flashinfer/fi_trace.py` | If set, decorated APIs auto-dump benchmark-definition JSON for each call (the \"fi_trace\" feature). |\n| `FLASHINFER_TRACE_DUMP_DIR` | cwd | `flashinfer/fi_trace.py` | Directory where the trace JSON files are written. |\n| `FLASHINFER_TRACE_APPLY` | `0` | `flashinfer/__init__.py`, `flashinfer/trace_apply/config.py` | Set to `1` to enable Trace Apply (runtime kernel substitution) when FlashInfer is imported. |\n| `FLASHINFER_TRACE_APPLY_PATH` | unset | `flashinfer/trace_apply/config.py` | Directory from which deployment-configured solutions are loaded for Trace Apply. |\n\n##### Validation / Autotuning / Routing / Kernel Selection\n\n| Variable | Default | Read in | Effect |\n|----------|---------|---------|--------|\n| `FLASHINFER_VALIDATE_INPUTS` | `0` | `flashinfer/mla/_core.py` (MLA wrapper) | Non-zero / non-empty value enables defensive input validation inside the MLA wrapper. Adds host-side overhead; intended for debugging. |\n| `FLASHINFER_AUTOTUNER_LOAD_FROM_FILE` | `0` | `flashinfer/autotuner/autotuner.py` | `1` loads previously serialized autotune results from disk instead of re-running the search. |\n| `FLASHINFER_DIST_AWARE_AUTOTUNE` | `0` | `flashinfer/fused_moe/da_config.py` | `1` enables experimental distribution-aware autotune and kernel dispatch (TRT-LLM MoE only). |\n| `FLASHINFER_AUTOTUNE_DIR` | unset | `flashinfer/mla/_sparse_mla_sm120.py` | Override the disk path for MLA AutoTuner cache files. Falls back to `FLASHINFER_WORKSPACE_DIR` when unset. |\n| `FLASHINFER_AUTOTUNE_TIMER` | unset (auto) | `flashinfer/autotuner/autotuner.py` | Selects the autotuner's per-tactic timer: `globaltimer` forces the GPU `%globaltimer` register, `cuda_event` forces `cudaEvent`, unset/anything-else auto-detects (uses `%globaltimer` only when Confidential Computing is detected). Under CC `cudaEventElapsedTime` is unreliable (can go negative), so the globaltimer path keeps tactic ranking stable. |\n| `FLASHINFER_CONFIDENTIAL_COMPUTE` | unset | `flashinfer/utils.py` | Override NVIDIA Confidential Computing (CC) auto-detection used by `is_confidential_compute()` (which drives the autotuner timer above): `1` forces CC, `0` forces non-CC. Useful for CI or hosts without `pynvml`. |\n| `FLASHINFER_TOPK_ALGO` | unset | `flashinfer/topk.py` | Force a specific top-k algorithm (otherwise the dispatcher chooses based on shape). Used for benchmarking / regression bisection. |\n| `FLASHINFER_USE_CUDA_NORM` | `0` | `flashinfer/norm/__init__.py` | `1` switches the norm path from the default backend to the legacy CUDA-only kernels. Diagnostic toggle. |\n| `FLASHINFER_ROUTING_FORCE_BLOCK_PER_TOKEN` | unset | `csrc/fused_moe/trtllm_backend/trtllm_fused_moe_routing_custom.cu` | Forces the TRT-LLM MoE custom-routing kernel into \"one-block-per-token\" mode regardless of the active routing policy. Mainly used to reproduce specific perf points. |\n| `FLASHINFER_B12X_MICRO_SHARE_INPUT` | `1` | `flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_dispatch.py` | `0` disables the B12x MoE micro-batch input-sharing optimization. Internal/experimental — leave at the default unless investigating an SM12x MoE regression. |\n| `FLASHINFER_B12X_FORCE_MOE_W4A16` | unset | `flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_dispatch.py` | When set (any non-empty value), forces the SM12x MoE dispatcher onto the W4A16 kernel path regardless of weight dtype. Internal/experimental — used to reproduce W4A16-specific issues. |\n| `FLASHINFER_TACTICS_BLOCKLIST` | unset | `flashinfer/autotuner/autotuner.py` | Path to a JSON tactics-blocklist file generated by `flashinfer tactics-blocklist generate` (or `python -m flashinfer tactics-blocklist generate`). When set, the autotuner loads the file at startup and skips any kernel tactics listed as invalid for the current GPU/driver environment, preventing hang or crash on known-bad tactics. |\n\n## Development Workflow\n\n### Typical Development Loop\n\n1. Edit kernel code in `include/flashinfer/some_kernel.cuh`\n2. Run test: `pytest tests/test_some_kernel.py::test_specific_case`\n3. FlashInfer detects changes and recompiles automatically\n4. No `pip install` needed!\n\n### Modifying Existing Kernels\n\n- **Kernel templates**: `include/flashinfer/**/*.cuh` - Changes picked up on next JIT compile\n- **Launcher code**: `csrc/*.cu` - May need changes if adding new template parameters\n- **Jinja templates**: `csrc/*.jinja` - Update if adding new config parameters\n- **Python API**: `flashinfer/*.py` - Update if changing function signatures\n\n### Creating Pre-compiled Packages\n\nWhen ready to distribute:\n\n```bash\n# Build flashinfer-jit-cache package\ncd flashinfer-jit-cache\nexport FLASHINFER_CUDA_ARCH_LIST=\"7.5 8.0 8.9 9.0a 10.0a 11.0a 12.0f\"\npython -m build --no-isolation --wheel\n```\n\nThis runs `flashinfer/aot.py` which calls all registered `gen_*_module()` functions and pre-compiles them.\n\n## Build System Details\n\n- **Build backend**: Custom PEP 517 backend in `build_backend.py`\n- **Data directories**: Build creates symlinks for editable installs:\n  - `3rdparty/cutlass` → `flashinfer/data/cutlass`\n  - `csrc` → `flashinfer/data/csrc`\n  - `include` → `flashinfer/data/include`\n- **Version**: Generated in `flashinfer/_build_meta.py` from `version.txt`\n\n## External Integrations\n\n### TVM-FFI: Cross-Language Unified ABI\n\nFlashInfer uses **TVM-FFI** (Apache TVM's Foreign Function Interface) for bindings, which provides a **cross-language unified ABI**. This means:\n\n- **Not limited to PyTorch**: The same compiled kernels can be used from multiple frameworks\n- **Language agnostic**: Bindings can be created for Python, C++, Rust, etc.\n- **Type-safe marshaling**: Automatic tensor/array conversion between languages\n- **Export syntax**: Use `TVM_FFI_DLL_EXPORT_TYPED_FUNC(name, func)` to expose C++ functions\n\nWhile FlashInfer currently provides PyTorch bindings, the underlying kernels are framework-agnostic thanks to TVM-FFI.\n\n### Other Integrations\n\n- **PyTorch Custom Ops**: `torch.library` for `torch.compile()` and CUDA graph support\n- **Ninja Build**: Direct ninja generation, no CMake complexity\n\n## Supported GPU Architectures\n\nFlashInfer supports NVIDIA SM75, SM80, SM86, SM89, SM90, SM100, SM103, SM110, SM120, and SM121.\n\n## Release Versioning\n\nFlashInfer follows a \"right-shifted\" versioning scheme (`major.minor.patch[.post1]`):\n\n- **major**: Architectural milestone and/or incompatible API changes (similar to PyTorch 2.0)\n- **minor**: Significant backwards-compatible new features\n- **patch**: Small backwards-compatible features (new kernels, new SM support) and backwards-compatible bug fixes\n- **post1**: Optional suffix for quick follow-up release with just backwards-compatible bug fixes\n\n## External Documentation Resources\n\nWhen working with FlashInfer's dependencies and tools, refer to these official documentation sources:\n\n### Core Dependencies\n\n- **TVM-FFI**: Apache TVM's Foreign Function Interface\n  - Documentation: <https://tvm.apache.org/ffi/>\n  - Package: `apache-tvm-ffi` (<https://pypi.org/project/apache-tvm-ffi/>)\n  - Use for: Understanding FFI export syntax, cross-language bindings\n\n- **CUTLASS**: NVIDIA's CUDA Templates for Linear Algebra Subroutines\n  - **Recommended**: Read source code directly in `3rdparty/cutlass/` (documentation is often outdated)\n  - Repository: <https://github.com/NVIDIA/cutlass>\n  - Use for: GEMM kernel implementations, tensor core operations\n\n- **CuTe (CUTE DSL)**: CUTLASS's Cute Layout and Tensor DSL\n  - Documentation: <https://docs.nvidia.com/cutlass/media/docs/pythonDSL/cute_dsl.html>\n  - **Tip**: Add `.md` to get Markdown format: <https://docs.nvidia.com/cutlass/media/docs/pythonDSL/cute_dsl.html.md>\n  - The Cute DSL kernels rely on Python modules from the `nvidia-cutlass-dsl` pip package, not to be confused with Python modules in the `3rdparty/cutlass` submodule\n  - Tutorial: <https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL>\n\n- **PTX ISA (Parallel Thread Execution)**: NVIDIA's PTX instruction set documentation\n  - Documentation: <https://docs.nvidia.com/cuda/parallel-thread-execution/>\n  - **Index/Table of Contents**: <https://docs.nvidia.com/cuda/parallel-thread-execution/index.html.md>\n  - **Tip**: Add `.md` to any page URL to get Markdown format\n  - Use for: Low-level instruction details, new GPU architecture features, inline PTX assembly\n\n### When to Consult These Docs\n\n- **Understanding new GPU architecture features** → Check PTX ISA documentation for latest instruction details\n- **Working on FFI bindings** → Check TVM-FFI docs for export patterns and type marshaling\n- **Implementing Tensor-Core kernels using CUTLASS** → Read source code in `3rdparty/cutlass/`\n- **Using tensor layouts or warp-level operations** → Refer to CuTe documentation\n- **Writing inline PTX assembly** → Consult PTX ISA for instruction syntax and semantics\n\nThese dependencies are included in FlashInfer's `3rdparty/` directory or `requirements.txt`.\n\n### Some final suggestions for all AI agents\n\n> Because practical engineering involves the accumulated experience of trial and error, match the coding style, efficiency, complexity, verbosity, and defensiveness by learning from existing code as much as possible—this document contains many pointers on where to find examples. Document intentional departures with rationale. Mentioning \"AI-assisted\" in the git commit message is good transparency. For performance-critical hot paths, leave justification for the special algorithmic choices and other potential alternatives in a comment for review.\n\n**Keep documentation in sync with code changes:** When modifying code that is referenced in this document or in `.claude/skills/`, update the corresponding documentation immediately. This includes:\n- Important infrastructure changes (e.g., `@flashinfer_api`, `@backend_requirement`, TVM-FFI macros) → Update examples in `CLAUDE.md` and relevant skill files\n- New patterns or conventions → Document them for future reference\n- Deprecated approaches → Remove or mark as deprecated in docs\n- New error handling patterns, macros, or utilities → Add to relevant skill tutorials\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"This repository’s agent instructions live in [CLAUDE.md](./CLAUDE.md).\n\nBefore making changes, read `CLAUDE.md` and follow its guidance for build, test, style, and contribution workflows.\n","category":"root","tokens":47},{"name":"CLAUDE.md","path":"CLAUDE.md","title":"CLAUDE.md","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nFlashInfer is a GPU kernel library for LLM serving that uses **JIT (Just-In-Time) compilation by default**. This means kernel code changes are automatically picked up without reinstalling the package - extremely convenient for development.\n\n## Quick Reference\n\n| Task | Command |\n|------|---------|\n| Install for development | `pip install --no-build-isolation -e . -v` |\n| Initialize submodules | `git submodule update --init --recursive` |\n| Install CUPTI for benchmarking | `pip install -U cupti-python` |\n| Run all tests | `pytest tests/` |\n| Run specific test | `pytest tests/path/test_file.py::test_function` |\n| Run multi-GPU test | `mpirun -np 4 pytest tests/comm/test_allreduce_unified_api.py` |\n| Run benchmark | `python benchmarks/flashinfer_benchmark.py --routine <name> <flags>` |\n| Run linting | `pre-commit run -a` |\n| Dump environment report (bug reports) | `python -m flashinfer.collect_env` (or `flashinfer collect-env [--json]`) |\n| Install pre-commit hooks | `pre-commit install` |\n| Clear JIT cache | `rm -rf ~/.cache/flashinfer/` |\n| Enable API logging (basic) | `export FLASHINFER_LOGLEVEL=1` |\n| Enable API logging (detailed) | `export FLASHINFER_LOGLEVEL=3` |\n| Enable API logging (with stats) | `export FLASHINFER_LOGLEVEL=5` |\n| Set API log destination | `export FLASHINFER_LOGDEST=mylog.txt` |\n| Enable verbose JIT logging | `export FLASHINFER_JIT_VERBOSE=1` |\n| Enable debug build | `export FLASHINFER_JIT_DEBUG=1` |\n| Set target architectures | `export FLASHINFER_CUDA_ARCH_LIST=\"8.0 9.0a\"` |\n| Set parallel compilation | `export FLASHINFER_NVCC_THREADS=4` |\n| Limit parallel ninja jobs | `export MAX_JOBS=4` |\n| Enable GDN native short-T path | `export FLASHINFER_GDN_WY_NATIVE_T=1` |\n| Enable GDN strided QKV path | `export FLASHINFER_GDN_WY_STRIDED_QKV=1` |\n| Enable GDN native A/B tensors | `export FLASHINFER_GDN_WY_NATIVE_AB=1` |\n| Override CuTe-DSL prefill scheduling | `export FLASHINFER_CUTE_PREFILL_PERSISTENT=0` (non-persistent) or `1` (persistent) |\n| Skip MoE EP CuTe-DSL import/version guard | `export FLASHINFER_MOE_EP_SKIP_DSL_CHECK=1` |\n| Override MoE EP knob-cache path | `export FLASHINFER_MOE_EP_KNOB_CACHE=/path/to/knobs.json` |\n| Disable MoE EP fused staging kernel | `export FLASHINFER_MEGA_FUSED_STAGE=0` |\n| Enable distribution-aware MoE autotune and kernel dispatch (experimental; TRT-LLM MoE only) | `export FLASHINFER_DIST_AWARE_AUTOTUNE=1` |\n\nThe minimum Python version used by CI and build tooling is defined in\n`.python-version`. CI Docker images use a stable Conda environment name and\nderive their interpreter and site-packages paths from that file. When raising\nthe minimum, also update `project.requires-python` and `tool.mypy.python_version`\nin `pyproject.toml`.\n\n## Quick Start for Development\n\n### Installation\n\n```bash\ngit clone https://github.com/flashinfer-ai/flashinfer.git --recursive\ncd flashinfer\npip install --no-build-isolation -e . -v\n```\n\n**Important**: The `--recursive` flag is required to initialize submodules in `3rdparty/` (cutlass, spdlog).\n\nIf you forgot `--recursive` when cloning:\n```bash\ngit submodule update --init --recursive\n```\n\nThat's it! You can now:\n\n- Run all benchmarks and unit tests\n- Modify kernel source code in `include/` without reinstalling\n- Changes are JIT-compiled on next use\n\nThe `--no-build-isolation` flag prevents pip from pulling incompatible PyTorch/CUDA versions from PyPI.\n\n### How JIT Compilation Works\n\nWhen you call a FlashInfer API:\n\n1. **First call**: Generates specialized CUDA code based on parameters (dtype, head_dim, etc.), compiles it with ninja, caches the .so file\n2. **Subsequent calls**: Uses cached compiled module\n3. **After kernel changes**: Automatically detects changes and recompiles\n\n**No manual rebuild step needed** - just edit `.cuh` files and run your code again.\n\n### Pre-compiled Packages (Optional)\n\nFlashInfer provides optional pre-compiled packages for users who want faster initialization:\n\n- `flashinfer-jit-cache`: Pre-built kernel cache\n- `flashinfer-cubin`: Pre-compiled kernel binaries\n\n**For development, you typically DON'T need these.** JIT compilation is fast enough and gives you live code reload.\n\n## Testing\n\nRun all tests:\n\n```bash\npytest tests/\n```\n\nRun specific test file:\n\n```bash\npytest tests/attention/test_hopper.py\n```\n\nRun specific test function:\n\n```bash\npytest tests/attention/test_hopper.py::test_single_prefill\n```\n\n### Skipping Tests Based on CUDA Architecture\n\nUse `flashinfer.utils` functions to skip tests on unsupported GPU architectures:\n\n**Available check functions:**\n- `get_compute_capability(device)` - Returns `(major, minor)` tuple\n- `is_sm90a_supported()` - Hopper (requires CUDA 12.3+)\n- `is_sm100a_supported()` - Blackwell (requires CUDA 12.8+)\n- `is_sm100f_supported()` - Blackwell feature-set (requires CUDA 12.9+)\n- `is_sm110a_supported()`, `is_sm120a_supported()`, `is_sm120f_supported()`, `is_sm121a_supported()`\n\n**APIs decorated with `@backend_requirement`** also provide:\n- `api_name.is_compute_capability_supported(cc)` - e.g., `mm_fp4.is_compute_capability_supported(100)`\n- `api_name.is_backend_supported(\"backend\")` - e.g., `mm_fp4.is_backend_supported(\"cudnn\")`\n- `api_name.is_backend_supported(\"backend\", cc)` - e.g., `mm_fp4.is_backend_supported(\"cudnn\", 80)`\n\n**Example:**\n```python\nfrom flashinfer.utils import is_sm90a_supported\n\ndef test_hopper_attention():\n    if not is_sm90a_supported(torch.device(\"cuda\")):\n        pytest.skip(\"Requires SM90a\")\n    # Test code...\n```\n\n**Common requirements:**\n\n| Feature | Min SM | Check Function |\n|---------|--------|----------------|\n| FlashAttention-3 | SM90a | `is_sm90a_supported()` |\n| MLA Attention | SM100a | `is_sm100a_supported()` |\n| FP8 GEMM | SM89+ | `get_compute_capability()[0] >= 9` |\n\n**Note:** `tests/conftest.py` auto-skips tests that trigger OOM, but tests should be written to avoid OOM by using appropriate problem sizes.\n\n## Benchmarking\n\nFlashInfer provides a unified benchmarking framework in `benchmarks/flashinfer_benchmark.py`.\n\n**Key features:**\n- Supports attention, GEMM, and MOE kernels\n- Multiple backends: FlashAttention2/3, cuDNN, CUTLASS, TensorRT-LLM, cuBLAS\n- **CUPTI timing (recommended)**: Hardware-level profiling for accurate GPU kernel time\n  - Automatically falls back to CUDA events if CUPTI unavailable\n  - Install: `pip install -U cupti-python` (requires CUDA 13+)\n- Batch testing, reference checking, CSV output\n\n**Quick example:**\n```bash\npython benchmarks/flashinfer_benchmark.py \\\n    --routine BatchDecodeWithPagedKVCacheWrapper \\\n    --backends fa2 cudnn \\\n    --batch_size 32 --s_kv 2048 \\\n    --num_qo_heads 32 --num_kv_heads 8 \\\n    --head_dim_qk 128 --head_dim_vo 128 \\\n    --page_size 16 --refcheck -vv\n```\n\n**Python API:**\n```python\nfrom flashinfer.testing import bench_gpu_time\n\n# CUPTI preferred, auto-fallback to CUDA events\nmedian_time, std_time = bench_gpu_time(\n    my_kernel, args=(x, y), enable_cupti=True, num_iters=30\n)\n```\n\n→ **For complete benchmarking guide, see [`.claude/skills/benchmark-kernel/skill.md`](.claude/skills/benchmark-kernel/skill.md)**\n\n## Code Linting\n\nRun all pre-commit hooks:\n\n```bash\npre-commit run -a\n```\n\nInstall hooks to run on every commit:\n\n```bash\npre-commit install\n```\n\n## Code Review\n\nWhen reviewing a diff (as an agent or a human), follow the shared focus areas, kernel-review\npolicy, and effort calibration in [`docs/code_review_guidance.md`](docs/code_review_guidance.md).\nNote: unlike human review, agents keep **kernel implementation details in scope** — read the\nkernel logic and report bugs, labeling findings by confidence.\n\n→ **For the complete review rule set, see [`docs/code_review_guidance.md`](docs/code_review_guidance.md)**\n\n## Architecture: JIT Compilation System\n\nFlashInfer's JIT system has three layers:\n\n### Layer 1: JitSpec (flashinfer/jit/core.py)\n\n`JitSpec` is an abstract base class defining the kernel-module lifecycle\n(`try_load()` / `build()` / `load()`, with the shared `build_and_load()`\ntemplate method handling caching, locking, and `FLASHINFER_DISABLE_JIT`).\nOne subclass per compilation toolchain:\n\n- `JitSpecNvcc` (nvcc/ninja modules, returned by `gen_jit_spec()`) defines:\n  - `name`: Unique identifier (URI hash from parameters)\n  - `sources`: List of .cu/.cpp files to compile\n  - `extra_cuda_cflags`, `extra_cflags`, `extra_ldflags`: Compiler flags\n- `JitSpecCuteDsl` (flashinfer/jit/cute_dsl_core.py) caches CuTe-DSL kernels\n  (see \"CuTe-DSL kernels\" under Module Caching below)\n\n### JIT Directory Rules\n\n**NEVER write to package directories** - they may be read-only after installation.\n\n| Directory | Writable | Use for |\n|-----------|----------|---------|\n| `FLASHINFER_GEN_SRC_DIR` | ✓ Yes | Generated source files (Jinja output, copied .cu files) |\n| `FLASHINFER_JIT_DIR` | ✓ Yes | Compiled `.so` outputs |\n| `FLASHINFER_CSRC_DIR` | ✗ No | Read-only source templates |\n| `FLASHINFER_AOT_DIR` | ✗ No | Read-only pre-compiled binaries |\n\n### Compilation Context: Architecture-Specific Compilation\n\nFlashInfer uses `CompilationContext` to manage CUDA architecture targets. Some kernels only work on specific GPU architectures (e.g., Hopper SM90, Blackwell SM100/SM12x).\n\n**How it works:**\n- Auto-detects GPUs in system or reads `FLASHINFER_CUDA_ARCH_LIST` environment variable\n- CuTe-DSL FP4 MM compilation parallelism can be controlled with `FLASHINFER_MM_FP4_CUTE_DSL_COMPILE_WORKERS`\n- JIT modules specify `supported_major_versions=[9, 10, 11, 12]` to limit compilation to specific SM versions\n- If GPU not supported → `RuntimeError: No supported CUDA architectures found`\n\n→ **See [`.claude/skills/add-cuda-kernel/skill.md`](.claude/skills/add-cuda-kernel/skill.md) for usage examples**\n\n### Layer 2: Code Generation\n\nEvery `gen_*_module()` function in `flashinfer/jit/` follows this pattern:\n\n```python\ndef gen_some_module(dtype_in, dtype_out, ...):\n    # 1. Compute unique identifier from parameters\n    uri = get_some_uri(dtype_in, dtype_out, ...)\n\n    # 2. Create generation directory\n    gen_directory = jit_env.FLASHINFER_GEN_SRC_DIR / uri\n\n    # 3. (Optional) Render Jinja template to generate type-specialized config\n    # Skip this step if you don't need type specialization\n    with open(jit_env.FLASHINFER_CSRC_DIR / \"some_customize_config.jinja\") as f:\n        template = jinja2.Template(f.read())\n    config_content = template.render(\n        dtype_in=dtype_map[dtype_in],\n        dtype_out=dtype_map[dtype_out],\n        # ... more parameters\n    )\n    write_if_different(gen_directory / \"some_config.inc\", config_content)\n\n    # 4. Copy source files to gen directory\n    sources = []\n    for fname in [\"some_kernel.cu\", \"some_jit_binding.cu\"]:\n        shutil.copy(jit_env.FLASHINFER_CSRC_DIR / fname, gen_directory / fname)\n        sources.append(gen_directory / fname)\n\n    # 5. Return JitSpec\n    return gen_jit_spec(uri, sources, extra_cuda_cflags=[...])\n```\n\n**Note**: If your operation doesn't need type specialization, you can skip step 3 entirely and just copy the source files directly.\n\n### Layer 3: Compilation and Loading\n\n`JitSpecNvcc` methods:\n\n- `write_ninja()` - Generates `build.ninja` file\n- `build()` - Executes `ninja` to compile sources\n- `build_and_load()` - Compiles and loads via TVM-FFI\n\nThe generated `build.ninja` file uses nvcc to compile .cu → .cuda.o → .so, then loads via TVM-FFI.\n\n### Jinja Templates (Optional)\n\n**Note: Jinja templates are NOT required.** You can write C++ code directly without templating.\n\nFor operations that need type specialization, templates in `csrc/*.jinja` can generate C++ code:\n\n```jinja\n// Input template\nusing DTypeIn = {{ dtype_in }};\nusing DTypeOut = {{ dtype_out }};\nconstexpr int PARAM = {{ param_value }};\n\n// After render\nusing DTypeIn = float16;\nusing DTypeOut = float16;\nconstexpr int PARAM = 128;\n```\n\nThis allows the same CUDA template code to be compiled with different concrete types. However, if your operation doesn't need this, you can skip Jinja and write the `.cu` files directly.\n\n## Directory Structure\n\n```\nflashinfer/\n├── include/flashinfer/           # Header-only CUDA kernel templates\n│   ├── attention/                # Attention kernels\n│   ├── gemm/                     # GEMM kernels\n│   ├── comm/                     # Communication kernels\n│   ├── mma.cuh                   # Matrix multiply utilities\n│   ├── utils.cuh                 # Common utilities\n│   └── [...]\n│\n├── csrc/                          # Framework bindings (via TVM-FFI)\n│   ├── *.cu                       # Kernel launcher implementations\n│   ├── *_jit_binding.cu           # TVM-FFI exports\n│   ├── *_customize_config.jinja   # Type config templates (optional)\n│   └── [...]\n│\n├── flashinfer/                    # Python package\n│   ├── jit/\n│   │   ├── core.py                # JitSpec, compilation infrastructure\n│   │   ├── cpp_ext.py             # Ninja build generation\n│   │   ├── env.py                 # Workspace paths\n│   │   ├── attention/             # Attention module generators\n│   │   ├── gemm/                  # GEMM module generators\n│   │   ├── fused_moe/             # MOE module generators\n│   │   └── [...]\n│   ├── gemm/                      # GEMM Python APIs\n│   ├── fused_moe/                 # MOE Python APIs\n│   ├── comm/                      # Communication Python APIs\n│   ├── *.py                       # Other high-level Python APIs\n│   ├── aot.py                     # AOT compilation for pre-built packages\n│   └── [...]\n│\n├── tests/                         # Test suite\n│   ├── attention/                 # Attention kernel tests\n│   ├── gemm/                      # GEMM kernel tests\n│   ├── moe/                       # MOE kernel tests\n│   ├── comm/                      # Communication tests\n│   ├── utils/                     # Utility tests\n│   └── conftest.py                # Pytest configuration\n│\n└── build_backend.py               # PEP 517 build backend\n```\n\n### Critical Rule: Framework Separation\n\n**Torch headers MUST NOT be included in `include/` directory files.**\n\n- `include/`: Framework-agnostic CUDA kernels (accept raw pointers)\n- `csrc/`: Framework bindings via TVM-FFI (currently PyTorch, but can support other frameworks)\n\n## Adding a New Operation\n\n→ **For complete step-by-step tutorial, see [`.claude/skills/add-cuda-kernel/skill.md`](.claude/skills/add-cuda-kernel/skill.md)**\n\n**Quick overview of the process:**\n1. Write kernel in `include/flashinfer/new_op.cuh` (framework-agnostic, raw pointers)\n2. Write launcher in `csrc/new_op.cu` (PyTorch tensor handling)\n3. Create TVM-FFI bindings in `csrc/new_op_jit_binding.cu`\n4. (Optional) Create Jinja template for type specialization\n5. Write JIT module generator in `flashinfer/jit/new_op.py`\n6. Write Python API in `flashinfer/new_op.py` with `@functools.cache`\n7. Write tests in `tests/`\n8. Register in `flashinfer/aot.py` for AOT compilation\n9. Export in `flashinfer/__init__.py`\n10. Add a `TraceTemplate` in `flashinfer/trace/templates/` and wire it via `@flashinfer_api(trace=...)` (see below)\n11. Add an example call in `tests/trace/example.py`, re-run to regenerate `fi_trace_out/`, and commit the new JSON files\n\n### Trace Template Checklist (for new or updated APIs)\n\nEvery public API decorated with `@flashinfer_api` should also carry a `trace=` argument so that `fi_trace()` works and auto-dump produces a benchmark definition JSON.\n\n1. **Create or update a `TraceTemplate`** in `flashinfer/trace/templates/<category>.py` (e.g., `norm.py`, `activation.py`, `cascade.py`, `gdn.py`). Define `axes`, `inputs`, `outputs`, and optionally a `reference` function.\n2. **Wire the template** to the API: `@flashinfer_api(trace=my_trace)` on the Python function (or class method's `run()`).\n3. **Add an example call** in `tests/trace/example.py` that exercises the new trace with realistic shapes.\n4. **Regenerate examples**: `rm -rf tests/trace/fi_trace_out && python tests/trace/example.py` — verify the expected JSON appears.\n5. **Update the docstring** in `tests/trace/example.py` to list the new file(s).\n6. **Run tests**: `pytest tests/trace/ -v` — all template-consistency and end-to-end tests must pass.\n7. **Commit the new JSON files** under `tests/trace/fi_trace_out/` alongside the code changes.\n\n**Example implementations:**\n- **Simple**: `flashinfer/norm/__init__.py` (RMSNorm) - no Jinja, good starting point\n- **Moderate**: `flashinfer/sampling.py` - with Jinja templating\n- **Complex**: `flashinfer/decode.py` - plan-run pattern, advanced workspace\n\n## Key Architectural Patterns\n\n### Module Caching\n\nFlashInfer uses two-level caching to avoid recompilation:\n\n1. **Python-level** (`@functools.cache`): In-memory cache of loaded modules\n2. **File-level** (`~/.cache/flashinfer/`): Compiled `.so` files on disk\n\n**Cache invalidation** (automatic):\n- Source file changes (SHA256 hash)\n- Compilation flags change\n- CUDA architecture change\n- FlashInfer version change\n\nURI computed as: `hash(operation_type + parameters + source_hashes + flags + cuda_arch)`\n\n**Cache management:**\n- Clear cache: `rm -rf ~/.cache/flashinfer/`\n- Override location: `export FLASHINFER_WORKSPACE_BASE=\"/scratch\"`\n\n**CuTe-DSL kernels** (`flashinfer/jit/cute_dsl_core.py`): `cute.compile()` has no\npersistent cache, so `JitSpecCuteDsl` (a `JitSpec` subclass, wrapped by the\n`build_and_load_cute_dsl_kernel()` helper) exports compiled kernels as\nobject files (`export_to_c()`) and reloads them with `cute.runtime.load_module(...,\nenable_tvm_ffi=True)`. Artifacts live in `cached_ops/` next to the nvcc modules, one\ndirectory per op family — `cached_ops/<module>_<arch>_cute_dsl/` (e.g.\n`nvfp4_quantize_sm100a_cute_dsl/`) holding one `meta.json` plus one `.o` per\nspecialization. The arch comes from the DSL's compile target (`CUTE_DSL_ARCH` or the\ncurrent device) since the artifacts are single-arch, unlike nvcc fatbins.\nInvalidation is module-granular: a changed nvidia-cutlass-dsl version or\nkernel-source SHA256 wipes and lazily rebuilds the module. Reference usage:\n`flashinfer/quantization/kernels/nvfp4_quantize.py`. Disable with\n`FLASHINFER_CUTE_DSL_DISABLE_CACHE=1`.\n\n### Dispatch Macros\n\nHandle combinatorial parameter spaces:\n\n```cpp\nDISPATCH_DTYPE(input_dtype, DTypeIn, {\n  DISPATCH_DTYPE(output_dtype, DTypeOut, {\n    DISPATCH_BLOCK_SIZE(block_size, BLOCK_SIZE, {\n      LaunchKernel<DTypeIn, DTypeOut, BLOCK_SIZE>(...);\n    });\n  });\n});\n```\n\nDefined in `.jinja` files and expanded after rendering.\n\n## API Logging with @flashinfer_api\n\nFlashInfer provides the `@flashinfer_api` decorator for debugging API calls.\n\n**Key features:**\n- **Crash-safe**: Logs inputs BEFORE execution (preserves info even if kernel crashes)\n- **Zero overhead when disabled**: `FLASHINFER_LOGLEVEL=0` (default)\n- **Multiple verbosity levels**: 0 (off), 1 (names), 3 (inputs/outputs), 5 (+ statistics)\n- **CUDA graph compatible**: Auto-skips stats during graph capture\n\n**Quick usage:**\n```bash\n# Enable detailed logging\nexport FLASHINFER_LOGLEVEL=3              # 0, 1, 3, or 5\nexport FLASHINFER_LOGDEST=debug.log       # stdout, stderr, or file path\n\npython my_script.py\n```\n\n**Why use this?**\n- Debug CUDA crashes (see inputs that caused crash)\n- Track tensor shapes/dtypes through pipeline\n- Detect NaN/Inf issues (level 5)\n\n→ **For complete debugging guide, see [`.claude/skills/debug-cuda-crash/skill.md`](.claude/skills/debug-cuda-crash/skill.md)**\n\n## Debugging\n\n### Enable Logging\n\n```bash\nexport FLASHINFER_JIT_VERBOSE=1      # Verbose JIT output\nexport FLASHINFER_JIT_DEBUG=1        # Debug symbols, -O0\nexport FLASHINFER_LOGLEVEL=3         # API logging (0=off, 1=basic, 3=detailed)\nexport FLASHINFER_LOGDEST=stdout\n```\n\n### Inspect Generated Code\n\n```bash\n# Generated sources\nls -la ~/.cache/flashinfer/0.6.0/*/generated/\n\n# Compiled modules\nls -la ~/.cache/flashinfer/0.6.0/*/cached_ops/\n\n# Build files\ncat ~/.cache/flashinfer/0.6.0/*/cached_ops/*/build.ninja\n```\n\n### Environment Variables\n\n```bash\n# Compilation\nexport FLASHINFER_NVCC_THREADS=4              # Threads per nvcc process (--threads=N)\nexport MAX_JOBS=4                             # Parallel ninja jobs (nvcc processes)\nexport FLASHINFER_CUDA_ARCH_LIST=\"8.0 9.0a\"  # Target architectures\n# Memory note: total compilation memory ≈ MAX_JOBS × FLASHINFER_NVCC_THREADS × per-thread mem.\n\n# Behavior\nexport FLASHINFER_WORKSPACE_BASE=\"/scratch\"   # Custom cache directory\n```\n\n#### Full Environment Variable Reference\n\nAlready covered above (Quick Reference / Debugging): `FLASHINFER_LOGLEVEL`,\n`FLASHINFER_LOGDEST`, `FLASHINFER_JIT_VERBOSE`, `FLASHINFER_JIT_DEBUG`,\n`FLASHINFER_CUDA_ARCH_LIST`, `FLASHINFER_NVCC_THREADS`,\n`FLASHINFER_WORKSPACE_BASE`, `MAX_JOBS`.\n\nThe remaining `FLASHINFER_*` knobs read by the code are listed below. Defaults\nmatch what the code uses today; values are strings unless noted.\n\n##### JIT / Build Toolchain\n\n| Variable | Default | Read in | Effect |\n|----------|---------|---------|--------|\n| `FLASHINFER_DISABLE_JIT` | unset | `flashinfer/jit/core.py` | If set (any non-empty value), JIT compilation is refused and modules must already exist in the cache or be provided via AOT packages. |\n| `FLASHINFER_CUTE_DSL_DISABLE_CACHE` | `0` | `flashinfer/jit/cute_dsl_core.py` | `1` disables the on-disk cache for JIT-compiled CuTe-DSL kernels (every process recompiles via `cute.compile`). |\n| `FLASHINFER_DISABLE_VERSION_CHECK` | unset | `flashinfer/jit/env.py` | Skip the AOT/JIT-cache version check that pins flashinfer-jit-cache to the installed flashinfer-python. Bypass only when you intentionally mix versions. |\n| `FLASHINFER_JIT_LINEINFO` | `0` | `flashinfer/jit/core.py` | `1` adds `-lineinfo` to nvcc so profiler / `cuda-gdb` can map PTX back to CUDA source. |\n| `FLASHINFER_NVCC` | `$cuda_home/bin/nvcc` | `flashinfer/jit/cpp_ext.py` | Override the nvcc binary used by the JIT (useful for sccache wrappers or non-default CUDA installs). |\n| `FLASHINFER_NVCC_LAUNCHER` | `\"\"` | `flashinfer/jit/cpp_ext.py` | Optional launcher prefix for nvcc (e.g. `ccache`, `sccache`). Combined with `FLASHINFER_NVCC`. |\n| `FLASHINFER_CXX_LAUNCHER` | `\"\"` | `flashinfer/jit/cpp_ext.py` | Same idea as `FLASHINFER_NVCC_LAUNCHER` but for the host C++ compiler. |\n| `FLASHINFER_FMHA_V2_VERBOSE` | unset | `flashinfer/jit/attention/fmha_v2/fmha_library.py` (FMHA v2 codegen, C++ side) | When set, the FMHA-v2 codegen / runtime prints verbose dispatcher diagnostics. Leave unset for normal runs. |\n| `FLASHINFER_EXTRA_CFLAGS` | unset | `flashinfer/jit/cpp_ext.py` | Extra compiler flags passed to the host C++ compiler. |\n| `FLASHINFER_EXTRA_CUDAFLAGS` | unset | `flashinfer/jit/cpp_ext.py` | Extra compiler flags passed to `nvcc`. |\n| `FLASHINFER_EXTRA_LDFLAGS` | unset | `flashinfer/jit/cpp_ext.py` | Extra linker flags passed to the linker. |\n\n##### Cubin / Artifact Loader\n\n| Variable | Default | Read in | Effect |\n|----------|---------|---------|--------|\n| `FLASHINFER_CUBIN_DIR` | `<FLASHINFER_WORKSPACE_BASE>/.cache/flashinfer/cubins` | `flashinfer/jit/env.py` (exposed via `flashinfer/__main__.py show-config`) | Local directory used to cache downloaded cubins. Override to share a cache between users. |\n| `FLASHINFER_CUBINS_REPOSITORY` | `https://edge.urm.nvidia.com/artifactory/sw-kernelinferencelibrary-public-generic-local` | `flashinfer/jit/cubin_loader.py` | Base URL the loader downloads cubins from. Point to a mirror for offline or air-gapped setups. |\n| `FLASHINFER_CUBIN_CHECKSUM_DISABLED` | unset | `flashinfer/jit/cubin_loader.py` | If set, skip SHA checksum verification of downloaded cubins. Debug aid only. |\n| `FLASHINFER_CUBIN_DOWNLOAD_THREADS` | `4` | `flashinfer/artifacts.py` | Thread-pool size used by `flashinfer artifacts download`. |\n| `FLASHINFER_NO_DOWNLOAD` | unset | `flashinfer/jit/cubin_loader.py` | Hard-fail if a cubin is missing locally instead of attempting to download. Useful in CI / locked-down environments. |\n| `FLASHINFER_DSL_FMHA_LOCAL_DIR` | unset | `flashinfer/attention/cute_dsl/fmha.py` | Path to a local checkout of the CuTe-DSL FMHA kernel sources. The loader checks here before downloading. |\n| `FLASHINFER_LOGGING_LEVEL` | `INFO` | `flashinfer/artifacts.py`, `flashinfer/jit/core.py` | Python logging level for the artifacts/cubin loader and the JIT compiler (`DEBUG`/`INFO`/`WARNING`/`ERROR`). Distinct from `FLASHINFER_LOGLEVEL`. |\n| `FLASHINFER_DISABLE_TINYGEMM2_SM100` | `0` | `flashinfer/gemm/routergemm.py` | Set to `1` to disable the generated SM100/SM103 `tinygemm2` backend and force the dispatcher to fall back to the legacy implementation. Useful as an escape hatch when debugging backend-selection or kernel issues on Blackwell systems. |\n\n##### API Dump / Logging Extensions\n\nThese complement `FLASHINFER_LOGLEVEL`/`FLASHINFER_LOGDEST` and are all read in `flashinfer/api_logging.py`.\n\n| Variable | Default | Effect |\n|----------|---------|--------|\n| `FLASHINFER_DUMP_DIR` | `flashinfer_dumps` | Directory where `@flashinfer_api` writes per-call tensor dumps when logging is enabled. |\n| `FLASHINFER_DUMP_INCLUDE` | `\"\"` | Comma-separated allow-list of API names; only matching calls are dumped. Wildcards (`*`) supported. |\n| `FLASHINFER_DUMP_EXCLUDE` | `\"\"` | Comma-separated deny-list of API names; matching calls skip the (expensive) stats / dump path. |\n| `FLASHINFER_DUMP_MAX_COUNT` | `1000` | Hard cap on number of dumped events; once reached, further dumps are dropped with a warning. |\n| `FLASHINFER_DUMP_MAX_SIZE_GB` | `20` | Hard cap on total dump size in **gigabytes** (GB) written to `FLASHINFER_DUMP_DIR` (parsed as `float`; see `_DUMP_MAX_SIZE_GB` in `flashinfer/api_logging.py`). |\n| `FLASHINFER_DUMP_SAFETENSORS` | `0` | `1` writes dumps as `.safetensors` (portable / Hugging Face-style); otherwise plain `.pt`. |\n\n##### Trace Capture\n\nUsed by `flashinfer.trace` / `fi_trace`.\n\n| Variable | Default | Read in | Effect |\n|----------|---------|---------|--------|\n| `FLASHINFER_TRACE_DUMP` | unset | `flashinfer/fi_trace.py` | If set, decorated APIs auto-dump benchmark-definition JSON for each call (the \"fi_trace\" feature). |\n| `FLASHINFER_TRACE_DUMP_DIR` | cwd | `flashinfer/fi_trace.py` | Directory where the trace JSON files are written. |\n| `FLASHINFER_TRACE_APPLY` | `0` | `flashinfer/__init__.py`, `flashinfer/trace_apply/config.py` | Set to `1` to enable Trace Apply (runtime kernel substitution) when FlashInfer is imported. |\n| `FLASHINFER_TRACE_APPLY_PATH` | unset | `flashinfer/trace_apply/config.py` | Directory from which deployment-configured solutions are loaded for Trace Apply. |\n\n##### Validation / Autotuning / Routing / Kernel Selection\n\n| Variable | Default | Read in | Effect |\n|----------|---------|---------|--------|\n| `FLASHINFER_VALIDATE_INPUTS` | `0` | `flashinfer/mla/_core.py` (MLA wrapper) | Non-zero / non-empty value enables defensive input validation inside the MLA wrapper. Adds host-side overhead; intended for debugging. |\n| `FLASHINFER_AUTOTUNER_LOAD_FROM_FILE` | `0` | `flashinfer/autotuner/autotuner.py` | `1` loads previously serialized autotune results from disk instead of re-running the search. |\n| `FLASHINFER_DIST_AWARE_AUTOTUNE` | `0` | `flashinfer/fused_moe/da_config.py` | `1` enables experimental distribution-aware autotune and kernel dispatch (TRT-LLM MoE only). |\n| `FLASHINFER_AUTOTUNE_DIR` | unset | `flashinfer/mla/_sparse_mla_sm120.py` | Override the disk path for MLA AutoTuner cache files. Falls back to `FLASHINFER_WORKSPACE_DIR` when unset. |\n| `FLASHINFER_AUTOTUNE_TIMER` | unset (auto) | `flashinfer/autotuner/autotuner.py` | Selects the autotuner's per-tactic timer: `globaltimer` forces the GPU `%globaltimer` register, `cuda_event` forces `cudaEvent`, unset/anything-else auto-detects (uses `%globaltimer` only when Confidential Computing is detected). Under CC `cudaEventElapsedTime` is unreliable (can go negative), so the globaltimer path keeps tactic ranking stable. |\n| `FLASHINFER_CONFIDENTIAL_COMPUTE` | unset | `flashinfer/utils.py` | Override NVIDIA Confidential Computing (CC) auto-detection used by `is_confidential_compute()` (which drives the autotuner timer above): `1` forces CC, `0` forces non-CC. Useful for CI or hosts without `pynvml`. |\n| `FLASHINFER_TOPK_ALGO` | unset | `flashinfer/topk.py` | Force a specific top-k algorithm (otherwise the dispatcher chooses based on shape). Used for benchmarking / regression bisection. |\n| `FLASHINFER_USE_CUDA_NORM` | `0` | `flashinfer/norm/__init__.py` | `1` switches the norm path from the default backend to the legacy CUDA-only kernels. Diagnostic toggle. |\n| `FLASHINFER_ROUTING_FORCE_BLOCK_PER_TOKEN` | unset | `csrc/fused_moe/trtllm_backend/trtllm_fused_moe_routing_custom.cu` | Forces the TRT-LLM MoE custom-routing kernel into \"one-block-per-token\" mode regardless of the active routing policy. Mainly used to reproduce specific perf points. |\n| `FLASHINFER_B12X_MICRO_SHARE_INPUT` | `1` | `flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_dispatch.py` | `0` disables the B12x MoE micro-batch input-sharing optimization. Internal/experimental — leave at the default unless investigating an SM12x MoE regression. |\n| `FLASHINFER_B12X_FORCE_MOE_W4A16` | unset | `flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_dispatch.py` | When set (any non-empty value), forces the SM12x MoE dispatcher onto the W4A16 kernel path regardless of weight dtype. Internal/experimental — used to reproduce W4A16-specific issues. |\n| `FLASHINFER_TACTICS_BLOCKLIST` | unset | `flashinfer/autotuner/autotuner.py` | Path to a JSON tactics-blocklist file generated by `flashinfer tactics-blocklist generate` (or `python -m flashinfer tactics-blocklist generate`). When set, the autotuner loads the file at startup and skips any kernel tactics listed as invalid for the current GPU/driver environment, preventing hang or crash on known-bad tactics. |\n\n## Development Workflow\n\n### Typical Development Loop\n\n1. Edit kernel code in `include/flashinfer/some_kernel.cuh`\n2. Run test: `pytest tests/test_some_kernel.py::test_specific_case`\n3. FlashInfer detects changes and recompiles automatically\n4. No `pip install` needed!\n\n### Modifying Existing Kernels\n\n- **Kernel templates**: `include/flashinfer/**/*.cuh` - Changes picked up on next JIT compile\n- **Launcher code**: `csrc/*.cu` - May need changes if adding new template parameters\n- **Jinja templates**: `csrc/*.jinja` - Update if adding new config parameters\n- **Python API**: `flashinfer/*.py` - Update if changing function signatures\n\n### Creating Pre-compiled Packages\n\nWhen ready to distribute:\n\n```bash\n# Build flashinfer-jit-cache package\ncd flashinfer-jit-cache\nexport FLASHINFER_CUDA_ARCH_LIST=\"7.5 8.0 8.9 9.0a 10.0a 11.0a 12.0f\"\npython -m build --no-isolation --wheel\n```\n\nThis runs `flashinfer/aot.py` which calls all registered `gen_*_module()` functions and pre-compiles them.\n\n## Build System Details\n\n- **Build backend**: Custom PEP 517 backend in `build_backend.py`\n- **Data directories**: Build creates symlinks for editable installs:\n  - `3rdparty/cutlass` → `flashinfer/data/cutlass`\n  - `csrc` → `flashinfer/data/csrc`\n  - `include` → `flashinfer/data/include`\n- **Version**: Generated in `flashinfer/_build_meta.py` from `version.txt`\n\n## External Integrations\n\n### TVM-FFI: Cross-Language Unified ABI\n\nFlashInfer uses **TVM-FFI** (Apache TVM's Foreign Function Interface) for bindings, which provides a **cross-language unified ABI**. This means:\n\n- **Not limited to PyTorch**: The same compiled kernels can be used from multiple frameworks\n- **Language agnostic**: Bindings can be created for Python, C++, Rust, etc.\n- **Type-safe marshaling**: Automatic tensor/array conversion between languages\n- **Export syntax**: Use `TVM_FFI_DLL_EXPORT_TYPED_FUNC(name, func)` to expose C++ functions\n\nWhile FlashInfer currently provides PyTorch bindings, the underlying kernels are framework-agnostic thanks to TVM-FFI.\n\n### Other Integrations\n\n- **PyTorch Custom Ops**: `torch.library` for `torch.compile()` and CUDA graph support\n- **Ninja Build**: Direct ninja generation, no CMake complexity\n\n## Supported GPU Architectures\n\nFlashInfer supports NVIDIA SM75, SM80, SM86, SM89, SM90, SM100, SM103, SM110, SM120, and SM121.\n\n## Release Versioning\n\nFlashInfer follows a \"right-shifted\" versioning scheme (`major.minor.patch[.post1]`):\n\n- **major**: Architectural milestone and/or incompatible API changes (similar to PyTorch 2.0)\n- **minor**: Significant backwards-compatible new features\n- **patch**: Small backwards-compatible features (new kernels, new SM support) and backwards-compatible bug fixes\n- **post1**: Optional suffix for quick follow-up release with just backwards-compatible bug fixes\n\n## External Documentation Resources\n\nWhen working with FlashInfer's dependencies and tools, refer to these official documentation sources:\n\n### Core Dependencies\n\n- **TVM-FFI**: Apache TVM's Foreign Function Interface\n  - Documentation: <https://tvm.apache.org/ffi/>\n  - Package: `apache-tvm-ffi` (<https://pypi.org/project/apache-tvm-ffi/>)\n  - Use for: Understanding FFI export syntax, cross-language bindings\n\n- **CUTLASS**: NVIDIA's CUDA Templates for Linear Algebra Subroutines\n  - **Recommended**: Read source code directly in `3rdparty/cutlass/` (documentation is often outdated)\n  - Repository: <https://github.com/NVIDIA/cutlass>\n  - Use for: GEMM kernel implementations, tensor core operations\n\n- **CuTe (CUTE DSL)**: CUTLASS's Cute Layout and Tensor DSL\n  - Documentation: <https://docs.nvidia.com/cutlass/media/docs/pythonDSL/cute_dsl.html>\n  - **Tip**: Add `.md` to get Markdown format: <https://docs.nvidia.com/cutlass/media/docs/pythonDSL/cute_dsl.html.md>\n  - The Cute DSL kernels rely on Python modules from the `nvidia-cutlass-dsl` pip package, not to be confused with Python modules in the `3rdparty/cutlass` submodule\n  - Tutorial: <https://github.com/NVIDIA/cutlass/tree/main/examples/python/CuTeDSL>\n\n- **PTX ISA (Parallel Thread Execution)**: NVIDIA's PTX instruction set documentation\n  - Documentation: <https://docs.nvidia.com/cuda/parallel-thread-execution/>\n  - **Index/Table of Contents**: <https://docs.nvidia.com/cuda/parallel-thread-execution/index.html.md>\n  - **Tip**: Add `.md` to any page URL to get Markdown format\n  - Use for: Low-level instruction details, new GPU architecture features, inline PTX assembly\n\n### When to Consult These Docs\n\n- **Understanding new GPU architecture features** → Check PTX ISA documentation for latest instruction details\n- **Working on FFI bindings** → Check TVM-FFI docs for export patterns and type marshaling\n- **Implementing Tensor-Core kernels using CUTLASS** → Read source code in `3rdparty/cutlass/`\n- **Using tensor layouts or warp-level operations** → Refer to CuTe documentation\n- **Writing inline PTX assembly** → Consult PTX ISA for instruction syntax and semantics\n\nThese dependencies are included in FlashInfer's `3rdparty/` directory or `requirements.txt`.\n\n### Some final suggestions for all AI agents\n\n> Because practical engineering involves the accumulated experience of trial and error, match the coding style, efficiency, complexity, verbosity, and defensiveness by learning from existing code as much as possible—this document contains many pointers on where to find examples. Document intentional departures with rationale. Mentioning \"AI-assisted\" in the git commit message is good transparency. For performance-critical hot paths, leave justification for the special algorithmic choices and other potential alternatives in a comment for review.\n\n**Keep documentation in sync with code changes:** When modifying code that is referenced in this document or in `.claude/skills/`, update the corresponding documentation immediately. This includes:\n- Important infrastructure changes (e.g., `@flashinfer_api`, `@backend_requirement`, TVM-FFI macros) → Update examples in `CLAUDE.md` and relevant skill files\n- New patterns or conventions → Document them for future reference\n- Deprecated approaches → Remove or mark as deprecated in docs\n- New error handling patterns, macros, or utilities → Add to relevant skill tutorials\n","category":"root","tokens":9021}]}