Skip to main content

fusedtok

CI PyPI License: MIT Python 3.10+

Fused CUDA kernels for LLM inference — RMSNorm / RoPE / SwiGLU and friends, with zero-copy torch tensor support: up to 6.2x faster than PyTorch eager (RoPE, RTX 3060, see Benchmarks).

中文文档请看 README_zh.md | English below.

Why

LLM inference frameworks launch many small, memory-bound operators per token. Each launch round-trips through global memory. fusedtok fuses them into single kernels to cut memory traffic and launch overhead.

Operators

Status Kernel Notes
RMSNorm (+residual) LLaMA/Qwen style, fused residual add
LayerNorm with affine
RoPE interleaved and NeoX layouts, kv-cache pos_offset
SwiGLU fused MLP activation
Softmax (row-wise) numerically stable
SiLU / GeLU / GeLU-tanh / ReLU / Tanh / Sigmoid elementwise
add / mul elementwise binary (fused add+residual pattern)
top-k / top-p (nucleus) arrival-ticket radix + early-exit compaction, replayed from a cached CUDA graph; deterministic ties (1.5x vs torch/CUB @131k on a 36-SM RTX 5060 Ti)
argmax / temperature greedy decoding helpers
sample_topp fused nucleus sampling: softmax -> top-p -> seeded draw, global-mass threshold
repetition penalty CTRL-style, applied to sampled token ids
decode_step the whole decode step fused: penalty -> temperature -> nucleus sample, one call, one readback
quantize_int8 / dequantize_int8 / qadd_int8 symmetric per-tensor INT8, fused dequant-add-requant
qgemm INT8 matmul, int32-exact: tensor-core IMMA GEMM + warp-per-row GEMV (M=1 decode; 2x vs fp16 projection)

Install

pip install fusedtok

Prebuilt Linux x86_64 wheels (manylinux, built with CUDA 12.4) are on PyPI. On Windows (or any platform without a matching wheel) pip builds from source automatically:

git clone https://github.com/Hai-Wenxiang/fusedtok.git
cd fusedtok
pip install .

Requirements:

  • NVIDIA GPU of RTX 30 series (Ampere) or newer — e.g. RTX 3060/3090, RTX 4080, RTX 5090, A100, H100
  • CUDA Toolkit >= 12.0
  • A C++17 compiler (MSVC on Windows, GCC/Clang on Linux); Python 3.10+
What is "compute capability"? (click to expand)

Compute capability is NVIDIA's version number for a GPU architecture generation — not a performance score. CUDA code must be compiled for a specific architecture to run on it. The wheel builds native cubins for compute capability 8.0 (A100) and 8.6 (RTX 30) plus a compute_86 PTX fallback, so Ampere runs natively and newer architectures (RTX 40/50, ...) JIT the PTX with their driver.

Compute capability Architecture Example GPUs
7.5 Turing GTX 16xx, RTX 20xx (not supported)
8.0 / 8.6 Ampere A100, RTX 30xx
8.9 Ada RTX 40xx (via PTX)
9.0 Hopper H100 (via PTX)
12.0 Blackwell RTX 50xx (via PTX)

Check yours: run nvidia-smi to see your GPU model, then look it up at https://developer.nvidia.com/cuda-gpus

Usage

numpy in / numpy out, or torch in / torch out — including zero-copy CUDA: kernels read and write torch device buffers directly via data_ptr(), with no staging copies and no host synchronization.

import numpy as np
import torch
import fusedtok

x = np.random.randn(4, 1024).astype(np.float32)
w = np.random.rand(1024).astype(np.float32)

# CPU reference implementation (ground truth, runs anywhere)
y = fusedtok.rmsnorm(x, w, eps=1e-6)

# staged CUDA: copies to GPU, runs kernel, copies back
y = fusedtok.rmsnorm(x, w, cuda=True)

# zero-copy CUDA with torch tensors: kernels run in torch's own buffers,
# stream-ordered with other torch operations
xt, wt = torch.from_numpy(x).cuda(), torch.from_numpy(w).cuda()
yt = fusedtok.rmsnorm(xt, wt)          # -> CUDA torch tensor

# RoPE with kv-cache position offset, NeoX (LLaMA-HF) layout
q = torch.randn(1, 4096, device="cuda")          # new token only
q_rot, k_rot = fusedtok.rope(q, k=None, pos_offset=1023, neox=True)

# sampling side: the whole decode step in one fused call
token = fusedtok.decode_step(logits, sampled_ids, penalty=1.1,
                             p=0.9, temperature=0.8, seed=step)
# or step by step:
logits = fusedtok.repetition_penalty(logits, sampled_ids, penalty=1.1)
token = fusedtok.sample_topp(logits, p=0.9, temperature=0.8, seed=step)

A minimal per-token sampling loop:

import torch, fusedtok as ft

h = torch.zeros(1, 4096, device="cuda")            # decoder state
w = torch.load("rms_weight.pt").cuda()             # float32 weights
wq, wscale = ft.quantize_int8(weight_f32.ravel())  # int8 weights
generated = []
for step in range(256):
    h = ft.rmsnorm(h, w, residual=h)               # fused add + norm
    q = ft.rope(q, k=None, pos_offset=step, neox=True)
    logits = model_output(h)                       # your model
    tok = ft.decode_step(logits, generated, penalty=1.1,
                         p=0.9, temperature=0.8, seed=step)
    generated.append(int(tok))

Every function accepts float32 numpy arrays or torch tensors (other dtypes are converted with a copy) and returns float32 outputs of the same family. CUDA torch tensors may also be bfloat16 - the kernels compute in float32 and convert at the load/store boundary (norm weights are upcast to float32 automatically; sampling/selection ops stay float32). CUDA torch tensors select the zero-copy path automatically.

See examples/demo.py for a runnable tour of every operator.

Correctness

Every kernel ships with a CPU reference implementation and element-wise parity tests (pytest). Tests run on machines without a GPU (CUDA cases skip automatically).

Benchmarks

RTX 3060 (sm_86), float32, zero-copy torch tensors, CUDA-event timing, vs the equivalent PyTorch eager expressions (full data: docs/benchmark_rt3060.json, reproduce with python benchmarks/bench.py):

Op Shape fusedtok PyTorch eager Speedup
RoPE NeoX (q+k) [2048×4096] 418 µs 2571 µs 6.2x
RMSNorm (+residual) [1024×4096] 156 µs 538 µs 3.4x
LayerNorm [1024×4096] 115 µs 161 µs 1.4x
SwiGLU [1024×4096] 158 µs 263 µs 1.7x
top-k (k=50) [131072] 74 µs 131 µs 1.8x
decode_step (penalty+sample) [131072] 309 µs 354 µs (3 calls) 1.15x
Softmax [1024×4096] 103 µs 118 µs 1.1x
SiLU [1024×4096] 104 µs 108 µs ~1.0x
argmax [131072] 67 µs 40 µs 0.6x (incl. host readback)
INT8 decode GEMV [1×4096] @ [131072×4096] 1595 µs 3186 µs (fp16) 2.0x

Row-wise kernels (norms, softmax) autotune their thread-block size per shape at first call (v0.4.1); the table reflects the tuned choices.

fusedtok vs PyTorch eager

RTX 5060 Ti (Blackwell, sm_120) — same suite, torch 2.11/cu128, highlights:

Op Shape fusedtok PyTorch eager Speedup
RoPE NeoX (q+k) [512×4096] 29 µs 239 µs 8.3x
RMSNorm (+residual) [4096×4096] 512 µs 1662 µs 3.3x
Softmax [1024×4096] 20 µs 51 µs 2.6x
top-k (k=50) [131072] 27 µs 41 µs (CUB) 1.5x
SwiGLU [4096×4096] 504 µs 859 µs 1.7x
argmax [32000] 15 µs 22 µs 1.5x
LayerNorm [1024×4096] 27 µs 28 µs ~1.0x

fusedtok vs PyTorch eager (RTX 5060 Ti)

The PyPI wheel ships sm_80/sm_86 cubins plus a compute_86 PTX fallback — verified to JIT and run correctly on Blackwell (sm_120) drivers.

Fusions win big (RoPE / RMSNorm / SwiGLU) because eager mode round-trips intermediate tensors through global memory. The v0.4 selection pipeline (arrival-ticket radix rounds + early-exit compaction, replayed from a cached CUDA graph) beats torch's CUB radix select at small k on both GPUs; mid-range k (2048..n) stays at or below parity — honest numbers, a pipelined tensor-core sort stays future work. The INT8 decode GEMV moves half the bytes of an fp16 projection and runs at full memory bandwidth (2x); the IMMA GEMM path (~17 TOPS) is correctness-first — cuBLASLt (torch._int_mm) remains faster for large prefill matmuls.

Development

See CONTRIBUTING.md for the full guide (test rules, error contract, determinism invariants). Quick start:

# Windows: run inside a VS developer prompt (vcvars64)
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
# from repo root: PYTHONPATH picks up the built module, conftest.py adds python/
$env:PYTHONPATH = "$PWD/build"        # Windows
PYTHONPATH=$PWD/build                 # Linux
python -m pytest tests -q
python benchmarks/bench.py            # GPU benchmark + chart

Windows / Linux. Windows uses MSVC via nvcc; CI builds and runs the CPU test suite on every push.

Roadmap

  • v0.2 (done): bf16 zero-copy, radix-select top-k/top-p, fused nucleus sampling, single-read softmax, CUDA-graph verified
  • v0.3 (done): chunk-merge selection sort + parallel nucleus count, bf16x4/x8 vectorized elementwise, INT8 quantize/dequantize utilities
  • v0.4 (done): arrival-ticket selection pipeline (no cooperative launch, early-exit compaction, cached CUDA graphs), stream-aware launchers everywhere (real CUDA-graph capture), INT8 compute path (IMMA qgemm + decode GEMV), fused decode_step sampling
  • v0.4.1 (done): runtime block-size autotuning for the row-wise kernels (norms/softmax pick 128..1024 threads per shape at first call)
  • v0.4+: lightweight fused attention; prebuilt wheels on PyPI; pipelined tensor-core INT8 GEMM

Community

License

MIT — see LICENSE. Third-party notices: NOTICES.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fusedtok-0.4.1.tar.gz (299.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fusedtok-0.4.1-cp310-cp310-manylinux_2_34_x86_64.whl (776.0 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.34+ x86-64

File details

Details for the file fusedtok-0.4.1.tar.gz.

File metadata

  • Download URL: fusedtok-0.4.1.tar.gz
  • Upload date:
  • Size: 299.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for fusedtok-0.4.1.tar.gz
Algorithm Hash digest
SHA256 c3f63c0c82741fcae189c6da14bc011b76aec4234f26e25afa0783d1c00ff5cb
MD5 479c1654aeed18c2e55ff7fb8c365f52
BLAKE2b-256 b5fd30842a9c284e485fbba3dff7b1adc50c5bf3977db2c89226b24e18aad8b5

See more details on using hashes here.

Provenance

The following attestation bundles were made for fusedtok-0.4.1.tar.gz:

Publisher: publish.yml on Hai-Wenxiang/fusedtok

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fusedtok-0.4.1-cp310-cp310-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for fusedtok-0.4.1-cp310-cp310-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 a8ffb8f5fc2c62f8efbf72bab49d223d3e89a42a5672e02d2a7b1c3b8e9df072
MD5 91a5ce08290d5669d73262a4aa41b339
BLAKE2b-256 9c20a969c203c4b9cfe8d7edcd8650a713b8ec1ff0dedddac69157b1a16245f9

See more details on using hashes here.

Provenance

The following attestation bundles were made for fusedtok-0.4.1-cp310-cp310-manylinux_2_34_x86_64.whl:

Publisher: publish.yml on Hai-Wenxiang/fusedtok

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.1.0

8 files

1.0.1

8 files

1.0.0

8 files

0.5.1

3 files

0.5.0

3 files

This release

0.4.1 This release

2 files

0.4.0

2 files

0.3.1

2 files

0.3.0

2 files

0.2.1

2 files

0.2.0

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page