Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bf8e9ed
feat: add swordfish w4a16 gemm kernels for blackwell
AlpinDale Jul 11, 2026
3320939
perf: dispatch k-heavy prefill shapes to a 256x128 tile
AlpinDale Jul 11, 2026
6c8d7dc
perf: stream-k scheduling for the decode window
AlpinDale Jul 11, 2026
8ec0917
perf: m-shared decode cta with single weight stream for m 48 to 96
AlpinDale Jul 11, 2026
66b3510
fix: skip unmatched expert weight tensors in fused-moe loading
AlpinDale Jul 11, 2026
52e76f3
feat: add awq support (uint4 weights with zero points) to swordfish
AlpinDale Jul 11, 2026
9b4c4df
feat: add 8-bit gptq support (uint8b128) to swordfish
AlpinDale Jul 11, 2026
4789c52
feat: add act_order support to swordfish
AlpinDale Jul 11, 2026
89d5206
feat: add fused moe swordfish path
AlpinDale Jul 11, 2026
9f30db8
feat: add fp16 and group 32/64 prefill, group-32 decode, and a determ…
AlpinDale Jul 11, 2026
d4381f2
feat: enable allspark w8a16 on blackwell
AlpinDale Jul 11, 2026
05142a5
fix: route channelwise checkpoints away from swordfish
AlpinDale Jul 11, 2026
282200f
feat: dense-gemm tier for large-m swordfish prefill
AlpinDale Jul 11, 2026
88e22ac
feat: dense tier for large-m fused moe
AlpinDale Jul 11, 2026
50db33b
perf: four-tile stream-k band with fixed pipeline waits replaces the …
AlpinDale Jul 11, 2026
f1c4016
revert: route channelwise checkpoints away from swordfish
AlpinDale Jul 11, 2026
53a9ec6
feat: run channelwise checkpoints through swordfish via scale replica…
AlpinDale Jul 11, 2026
5f18663
perf: extend the dense tier to 8-bit on few-sm parts
AlpinDale Jul 11, 2026
83b9f63
fix: run humming on sm110 via an sm100 heuristics compat shim
AlpinDale Jul 11, 2026
243c669
feat: expose allspark as a named linear backend
AlpinDale Jul 11, 2026
0c92f45
perf: pad the dense-tier dequant tile to kill smem bank conflicts
AlpinDale Jul 11, 2026
311d86c
perf: hand the decode band to the fused atomic grid on many-sm parts
AlpinDale Jul 11, 2026
07e2e41
perf: decode replicated-channelwise scales through the native path
AlpinDale Jul 12, 2026
88a420e
perf: stop splitting k on few-sm parts once columns fill the machine
AlpinDale Jul 12, 2026
736e132
perf: zero the atomic-path output with a compute kernel instead of me…
AlpinDale Jul 12, 2026
4c16259
perf: zero the moe output with a compute kernel instead of memset
AlpinDale Jul 12, 2026
70068be
perf: fuse the act_order sort and output zeroing into one prep launch
AlpinDale Jul 12, 2026
712fc57
perf: cross act_order into the dense tier at m 512 outside wide n
AlpinDale Jul 12, 2026
de4b1a0
perf: claim the few-sm decode band in cta column quads
AlpinDale Jul 12, 2026
2674203
style: satisfy pre-commit across the swordfish diff
AlpinDale Jul 12, 2026
f545674
chore: regenerate the test requirements lockfile
AlpinDale Jul 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,34 @@ if(APHRODITE_GPU_LANG STREQUAL "CUDA" OR APHRODITE_GPU_LANG STREQUAL "HIP")
SRCS "${APHRODITE_STABLE_EXT_SRC}"
CUDA_ARCHS "${CUDA_ARCHS}")

#
# Swordfish kernels: w4a16 GEMM for the Blackwell sm100 family
# (datacenter sm100 + Thor sm110). Family (f) variants need CUDA >= 13.0;
# older toolchains fall back to arch-exact sm100.
#
if(${CMAKE_CUDA_COMPILER_VERSION} VERSION_GREATER_EQUAL 13.0)
cuda_archs_loose_intersection(SWORDFISH_ARCHS "10.0f;11.0f" "${CUDA_ARCHS}")
else()
cuda_archs_loose_intersection(SWORDFISH_ARCHS "10.0a" "${CUDA_ARCHS}")
endif()
if(SWORDFISH_ARCHS)
set(SWORDFISH_SRCS
"csrc/libtorch_stable/quantization/swordfish/swordfish_prepack.cu"
"csrc/libtorch_stable/quantization/swordfish/swordfish_mm.cu"
"csrc/libtorch_stable/quantization/swordfish/swordfish_moe.cu"
"csrc/libtorch_stable/quantization/swordfish/swordfish_dense_tier.cu"
"csrc/libtorch_stable/quantization/swordfish/swordfish_prefill.cu"
"csrc/libtorch_stable/quantization/swordfish/swordfish_prefill_f16.cu")
set_gencode_flags_for_srcs(
SRCS "${SWORDFISH_SRCS}"
CUDA_ARCHS "${SWORDFISH_ARCHS}")
list(APPEND APHRODITE_STABLE_EXT_SRC "${SWORDFISH_SRCS}")
message(STATUS "Building Swordfish kernels for archs: ${SWORDFISH_ARCHS}")
else()
message(STATUS "Not building Swordfish kernels as no compatible archs "
"found in CUDA target architectures")
endif()

if(COOPERATIVE_TOPK_ARCHS)
list(APPEND APHRODITE_STABLE_EXT_SRC
"csrc/libtorch_stable/cooperative_topk.cu")
Expand Down Expand Up @@ -737,7 +765,10 @@ if(APHRODITE_GPU_LANG STREQUAL "CUDA" OR APHRODITE_GPU_LANG STREQUAL "HIP")
endif()

# Only build AllSpark kernels if we are building for at least some compatible archs.
cuda_archs_loose_intersection(ALLSPARK_ARCHS "8.0;8.6;8.7;8.9" "${CUDA_ARCHS}")
# 8.0+PTX JIT-forwards onto Blackwell like marlin; the kernel is plain
# mma.sync + cp.async and its large-M path is dequant + cuBLAS, which owns
# channelwise w8a16 prefill on sm100/sm110.
cuda_archs_loose_intersection(ALLSPARK_ARCHS "8.0+PTX" "${CUDA_ARCHS}")
if (ALLSPARK_ARCHS)
set(ALLSPARK_SRCS
"csrc/libtorch_stable/quantization/gptq_allspark/allspark_repack.cu"
Expand Down
203 changes: 203 additions & 0 deletions aphrodite/_custom_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -1550,6 +1550,209 @@ def machete_prepack_B_fake(
return torch.empty_like(b_q_weight, memory_format=torch.contiguous_format)


# Swordfish (Blackwell sm100/sm110 w4a16)
def swordfish_prepack_B(
b_q_weight: torch.Tensor,
size_k: int,
size_n: int,
num_bits: int = 4,
perm: torch.Tensor | None = None,
) -> torch.Tensor:
"""Pack a GPTQ int weight (int32 [K*bits/32, N]) into Swordfish ABI v1
(int32 [NB, KB, 512*bits/4] block-linear). perm applies the act_order
row sort during the repack."""
return torch.ops._C.swordfish_prepack_B(b_q_weight, perm, size_k, size_n, num_bits)


if hasattr(torch.ops._C, "swordfish_prepack_B"):

@register_fake("_C::swordfish_prepack_B")
def swordfish_prepack_B_fake(
b_q_weight: torch.Tensor,
perm: torch.Tensor | None,
size_k: int,
size_n: int,
num_bits: int,
) -> torch.Tensor:
return torch.empty(
(size_n // 64, size_k // 64, 128 * num_bits),
dtype=torch.int32,
device=b_q_weight.device,
)


def swordfish_mm(
a: torch.Tensor,
b_packed: torch.Tensor,
group_scales: torch.Tensor,
group_size: int,
size_k: int,
size_n: int,
group_zps: torch.Tensor | None = None,
num_bits: int = 4,
perm: torch.Tensor | None = None,
) -> torch.Tensor:
"""w4a16/w8a16 GEMM: a [M, K] fp16/bf16 times a Swordfish ABI v1 packed
weight with per-group scales [groups, N]. group_zps holds prescaled
(8 - zp) * scale rows for zero-point checkpoints (AWQ/HQQ, 4-bit only).
perm is the act_order column sort; the op permutes the activations for
the fused paths and folds the sort into the dense tier's weight scatter."""
return torch.ops._C.swordfish_mm(a, b_packed, group_scales, group_zps, perm, num_bits, group_size, size_k, size_n)


if hasattr(torch.ops._C, "swordfish_mm"):

@register_fake("_C::swordfish_mm")
def swordfish_mm_fake(
a: torch.Tensor,
b_packed: torch.Tensor,
group_scales: torch.Tensor,
group_zps: torch.Tensor | None,
perm: torch.Tensor | None,
num_bits: int,
group_size: int,
size_k: int,
size_n: int,
) -> torch.Tensor:
return torch.empty((a.shape[0], size_n), dtype=a.dtype, device=a.device)


def swordfish_dequant_dense(
b_packed: torch.Tensor,
group_scales: torch.Tensor,
group_zps: torch.Tensor | None,
num_bits: int,
group_size: int,
size_k: int,
size_n: int,
transpose: bool,
) -> torch.Tensor:
"""Dequantize Swordfish-packed weights to dense fp16/bf16, [K, N] or
out-major [N, K], with an optional leading expert dimension."""
return torch.ops._C.swordfish_dequant_dense(
b_packed,
group_scales,
group_zps,
num_bits,
group_size,
size_k,
size_n,
transpose,
)


if hasattr(torch.ops._C, "swordfish_dequant_dense"):

@register_fake("_C::swordfish_dequant_dense")
def swordfish_dequant_dense_fake(
b_packed: torch.Tensor,
group_scales: torch.Tensor,
group_zps: torch.Tensor | None,
num_bits: int,
group_size: int,
size_k: int,
size_n: int,
transpose: bool,
) -> torch.Tensor:
shape: tuple[int, ...] = (size_n, size_k) if transpose else (size_k, size_n)
if b_packed.dim() == 4:
shape = (b_packed.shape[0],) + shape
return torch.empty(shape, dtype=group_scales.dtype, device=b_packed.device)


def swordfish_moe_mm(
a: torch.Tensor,
b_packed: torch.Tensor,
group_scales: torch.Tensor,
sorted_token_ids: torch.Tensor,
expert_ids: torch.Tensor,
num_tokens_post_padded: torch.Tensor,
topk_weights: torch.Tensor | None,
moe_block_size: int,
top_k: int,
mul_topk_weights: bool,
num_bits: int,
group_size: int,
size_k: int,
size_n: int,
) -> torch.Tensor:
"""Fused-MoE GEMM over per-expert Swordfish ABI v1 weights
(int32 [E, NB, KB, words]) with token blocks prepared by
moe_align_block_size(moe_block_size in {16, 64}). The 64-token blocks
run the CTA-wide weight-staging kernel for batched shapes. Returns
[M * top_k, size_n]."""
return torch.ops._C.swordfish_moe_mm(
a,
b_packed,
group_scales,
sorted_token_ids,
expert_ids,
num_tokens_post_padded,
topk_weights,
moe_block_size,
top_k,
mul_topk_weights,
num_bits,
group_size,
size_k,
size_n,
)


if hasattr(torch.ops._C, "swordfish_moe_mm"):

@register_fake("_C::swordfish_moe_mm")
def swordfish_moe_mm_fake(
a: torch.Tensor,
b_packed: torch.Tensor,
group_scales: torch.Tensor,
sorted_token_ids: torch.Tensor,
expert_ids: torch.Tensor,
num_tokens_post_padded: torch.Tensor,
topk_weights: torch.Tensor | None,
moe_block_size: int,
top_k: int,
mul_topk_weights: bool,
num_bits: int,
group_size: int,
size_k: int,
size_n: int,
) -> torch.Tensor:
return torch.empty((a.shape[0] * top_k, size_n), dtype=a.dtype, device=a.device)


def swordfish_prefill_mm(
a: torch.Tensor,
b_packed: torch.Tensor,
group_scales: torch.Tensor,
group_size: int,
size_k: int,
size_n: int,
group_zps: torch.Tensor | None = None,
num_bits: int = 4,
) -> torch.Tensor:
"""w4a16 prefill GEMM (sm100 tcgen05 mixed-input mainloop fork): a [M, K]
bf16 times a Swordfish ABI v1 packed weight (int32 [NB, KB, 512]) with
bf16 per-group scales [groups, N]. v1: group_size 128, K/N % 128 == 0."""
return torch.ops._C.swordfish_prefill_mm(a, b_packed, group_scales, group_zps, num_bits, group_size, size_k, size_n)


if hasattr(torch.ops._C, "swordfish_prefill_mm"):

@register_fake("_C::swordfish_prefill_mm")
def swordfish_prefill_mm_fake(
a: torch.Tensor,
b_packed: torch.Tensor,
group_scales: torch.Tensor,
group_zps: torch.Tensor | None,
num_bits: int,
group_size: int,
size_k: int,
size_n: int,
) -> torch.Tensor:
return torch.empty((a.shape[0], size_n), dtype=a.dtype, device=a.device)


# CUTLASS W4A8
def cutlass_w4a8_mm(
a: torch.Tensor,
Expand Down
1 change: 1 addition & 0 deletions aphrodite/config/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ def with_default(cls, default: list[str], /, **kwargs: list[str]) -> "IrOpPriori
"torch",
"aiter",
"machete",
"swordfish",
"fbgemm",
"conch",
"exllama",
Expand Down
11 changes: 11 additions & 0 deletions aphrodite/model_executor/kernels/linear/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
from aphrodite.model_executor.kernels.linear.mixed_precision.rdna3_w4a16 import (
RDNA3W4A16LinearKernel,
)
from aphrodite.model_executor.kernels.linear.mixed_precision.swordfish import (
SwordfishLinearKernel,
)
from aphrodite.model_executor.kernels.linear.mixed_precision.triton_w4a16 import (
TritonW4A16LinearKernel,
)
Expand Down Expand Up @@ -275,6 +278,12 @@ def _get_linear_backend() -> str:
"machete": {
MacheteLinearKernel,
},
"swordfish": {
SwordfishLinearKernel,
},
"allspark": {
AllSparkLinearKernel,
},
"fbgemm": {
FbgemmNvFp4LinearKernel,
},
Expand Down Expand Up @@ -391,6 +400,7 @@ def _filter_kernels_by_backend(
_POSSIBLE_KERNELS: dict[PlatformEnum, list[type[MPLinearKernel]]] = {
PlatformEnum.CUDA: [
CutlassW4A8LinearKernel,
SwordfishLinearKernel,
MacheteLinearKernel,
AllSparkLinearKernel,
MarlinLinearKernel,
Expand Down Expand Up @@ -1037,6 +1047,7 @@ def register_linear_kernel(
"ExllamaLinearKernel",
"MacheteLinearKernel",
"MarlinLinearKernel",
"SwordfishLinearKernel",
"TritonW4A16LinearKernel",
"XPUW4A8IntLinearKernel",
"XPUwNa16LinearKernel",
Expand Down
Loading
Loading