Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
36 changes: 36 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,30 @@ if(VLLM_GPU_LANG STREQUAL "CUDA" OR VLLM_GPU_LANG STREQUAL "HIP")
"csrc/libtorch_stable/custom_all_reduce.cu"
"csrc/libtorch_stable/fused_deepseek_v4_qnorm_rope_kv_insert_kernel.cu")

if(VLLM_GPU_LANG STREQUAL "CUDA" AND
DEFINED CMAKE_CUDA_COMPILER_VERSION AND
CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0)

if(${CMAKE_CUDA_COMPILER_VERSION} VERSION_GREATER_EQUAL 13.0)
cuda_archs_loose_intersection(COOPERATIVE_TOPK_ARCHS
"9.0a;10.0f;10.1f;10.3f;11.0f;12.0f;12.1f" "${CUDA_ARCHS}")
else()
cuda_archs_loose_intersection(COOPERATIVE_TOPK_ARCHS
"9.0a;10.0a;10.1a;10.3a;12.0a;12.1a" "${CUDA_ARCHS}")
endif()

if(COOPERATIVE_TOPK_ARCHS)
list(APPEND VLLM_GPU_FLAGS "-DVLLM_ENABLE_COOPERATIVE_TOPK=1")

cuda_archs_loose_intersection(COOPERATIVE_TOPK_SM90_ARCHS
"9.0a" "${COOPERATIVE_TOPK_ARCHS}")
if(COOPERATIVE_TOPK_SM90_ARCHS)
list(APPEND VLLM_GPU_FLAGS
"-DVLLM_COOPERATIVE_TOPK_PORTABLE_CLUSTER_ONLY=1")
endif()
Comment thread
LopezCastroRoberto marked this conversation as resolved.
Outdated
endif()
endif()

if(VLLM_GPU_LANG STREQUAL "CUDA")
SET(CUTLASS_ENABLE_HEADERS_ONLY ON CACHE BOOL "Enable only the header library")

Expand Down Expand Up @@ -498,6 +522,14 @@ if(VLLM_GPU_LANG STREQUAL "CUDA" OR VLLM_GPU_LANG STREQUAL "HIP")
SRCS "${VLLM_STABLE_EXT_SRC}"
CUDA_ARCHS "${CUDA_ARCHS}")

if(COOPERATIVE_TOPK_ARCHS)
list(APPEND VLLM_STABLE_EXT_SRC
"csrc/libtorch_stable/cooperative_topk.cu")
set_gencode_flags_for_srcs(
SRCS "csrc/libtorch_stable/cooperative_topk.cu"
CUDA_ARCHS "${COOPERATIVE_TOPK_ARCHS}")
endif()

# Only build Marlin kernels if we are building for at least some compatible archs.
# Keep building Marlin for 9.0 as there are some group sizes and shapes that
# are not supported by Machete yet.
Expand Down Expand Up @@ -1049,6 +1081,10 @@ if(VLLM_GPU_LANG STREQUAL "CUDA" OR VLLM_GPU_LANG STREQUAL "HIP")
target_compile_definitions(_C_stable_libtorch PRIVATE
TORCH_TARGET_VERSION=0x020B000000000000ULL)
target_compile_definitions(_C_stable_libtorch PRIVATE USE_CUDA)
if(COOPERATIVE_TOPK_ARCHS)
target_compile_definitions(_C_stable_libtorch PRIVATE
VLLM_ENABLE_COOPERATIVE_TOPK=1)
endif()
# Needed by CUTLASS kernels
target_compile_definitions(_C_stable_libtorch PRIVATE
CUTLASS_ENABLE_DIRECT_CUDA_DRIVER_CALL=1)
Expand Down
155 changes: 155 additions & 0 deletions csrc/libtorch_stable/cooperative_topk.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// Cooperative cluster TopK for DeepSeek V3 sparse attention indexer.
// See cooperative_topk.cuh for kernel implementation.

#include <cuda_runtime.h>

#include "torch_utils.h"

#ifndef USE_ROCM
#include "cooperative_topk.cuh"
namespace ct = vllm::cooperative;
#endif

#ifndef USE_ROCM
template <uint32_t TopK, uint32_t CS>
void launch_cooperative_cluster(ct::CooperativeTopKParams<TopK>& params,
size_t smem, cudaStream_t stream) {
auto kernel = []() {
if constexpr (CS == 16) {
#ifndef VLLM_COOPERATIVE_TOPK_PORTABLE_CLUSTER_ONLY
return &ct::cooperative_topk_cs16<TopK>;
#else
static_assert(CS != 16,
"CS=16 cooperative_topk requires non-portable cluster "
"support");
#endif
} else if constexpr (CS == 8) {
return &ct::cooperative_topk_cs8<TopK>;
} else {
static_assert(CS == 4, "unsupported cooperative_topk cluster size");
return &ct::cooperative_topk_cs4<TopK>;
}
}();
#ifndef VLLM_COOPERATIVE_TOPK_PORTABLE_CLUSTER_ONLY
if constexpr (CS > 8) {
cudaFuncSetAttribute(kernel, cudaFuncAttributeNonPortableClusterSizeAllowed,
1);
}
#endif
cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize,
smem);

cudaLaunchConfig_t cfg = {};
cfg.gridDim = dim3(params.num_rows, CS);
cfg.blockDim = dim3(ct::kBlockSize);
cfg.dynamicSmemBytes = smem;
cfg.stream = stream;
cudaLaunchAttribute attrs[1];
attrs[0].id = cudaLaunchAttributeClusterDimension;
attrs[0].val.clusterDim = {1, CS, 1};
cfg.numAttrs = 1;
cfg.attrs = attrs;
cudaError_t err = cudaLaunchKernelEx(&cfg, kernel, params);
STD_TORCH_CHECK(err == cudaSuccess,
"cooperative_topk launch failed: ", cudaGetErrorString(err));
}

template <uint32_t TopK>
void launch_cooperative_topk_impl(const torch::stable::Tensor& logits,
const torch::stable::Tensor& lengths,
torch::stable::Tensor& output,
torch::stable::Tensor& workspace,
int64_t max_seq_len) {
Comment thread
LopezCastroRoberto marked this conversation as resolved.
const int64_t num_rows = logits.size(0);
const cudaStream_t stream = get_current_cuda_stream();

const uint32_t stride = static_cast<uint32_t>(logits.stride(0));
// 32 = max clusters for CS=4 (32 x 4 = 128 CTAs = 66% of SMs, leaves
// headroom)
STD_TORCH_CHECK(
num_rows <= 32,
"cooperative_topk supports <=32 rows; use persistent_topk for "
"larger batches");

STD_TORCH_CHECK(stride % 4 == 0,
"cooperative_topk: stride must be multiple of 4 for TMA "
"alignment, got stride (max_model_len)=",
stride);

STD_TORCH_CHECK(workspace.is_cuda(), "workspace must be CUDA tensor");
STD_TORCH_CHECK(
workspace.scalar_type() == torch::headeronly::ScalarType::Byte,
"workspace must be uint8");

ct::CooperativeTopKParams<TopK> params;
params.input = logits.const_data_ptr<float>();
params.output = output.mutable_data_ptr<int32_t>();
params.lengths = lengths.const_data_ptr<int32_t>();
params.num_rows = static_cast<uint32_t>(num_rows);
params.stride = stride;
params.tie_ws =
reinterpret_cast<ct::Tie*>(workspace.mutable_data_ptr<uint8_t>());

constexpr uint32_t kTieWsPerRow =
TopK <= ct::kBlockSize ? ct::kMaxTies : TopK;
STD_TORCH_CHECK(
workspace.size(0) >=
static_cast<int64_t>(num_rows * kTieWsPerRow * sizeof(ct::Tie)),
"workspace too small");

#ifndef VLLM_COOPERATIVE_TOPK_PORTABLE_CLUSTER_ONLY
if (num_rows <= 4) {
launch_cooperative_cluster<TopK, 16>(params, ct::kSmemSize8, stream);
} else if (num_rows <= 8) {
#else
if (num_rows <= 8) {
#endif
launch_cooperative_cluster<TopK, 8>(params, ct::kSmemSize8, stream);
} else {
launch_cooperative_cluster<TopK, 4>(params, ct::kSmemSize4, stream);
}
}
#endif // USE_ROCM

void cooperative_topk(const torch::stable::Tensor& logits,
const torch::stable::Tensor& lengths,
torch::stable::Tensor& output,
torch::stable::Tensor& workspace, int64_t k,
int64_t max_seq_len) {
#ifndef USE_ROCM
STD_TORCH_CHECK(logits.is_cuda(), "logits must be CUDA tensor");
STD_TORCH_CHECK(lengths.is_cuda(), "lengths must be CUDA tensor");
STD_TORCH_CHECK(output.is_cuda(), "output must be CUDA tensor");
STD_TORCH_CHECK(logits.scalar_type() == torch::headeronly::ScalarType::Float,
"Only float32 supported");
STD_TORCH_CHECK(lengths.scalar_type() == torch::headeronly::ScalarType::Int,
"lengths must be int32");
STD_TORCH_CHECK(output.scalar_type() == torch::headeronly::ScalarType::Int,
"output must be int32");
STD_TORCH_CHECK(logits.dim() == 2, "logits must be 2D");
STD_TORCH_CHECK(lengths.dim() == 1 || lengths.dim() == 2,
"lengths must be 1D or 2D");
STD_TORCH_CHECK(lengths.is_contiguous(), "lengths must be contiguous");
STD_TORCH_CHECK(output.dim() == 2, "output must be 2D");
const int64_t num_rows = logits.size(0);
STD_TORCH_CHECK(lengths.numel() == num_rows, "lengths size mismatch");
STD_TORCH_CHECK(output.size(0) == num_rows && output.size(1) == k,
"output size mismatch");
STD_TORCH_CHECK(
k == 512 || k == 1024 || k == 2048,
"cooperative_topk supports k=512, k=1024, or k=2048, got k=", k);

if (k == 512) {
launch_cooperative_topk_impl<512>(logits, lengths, output, workspace,
max_seq_len);
} else if (k == 1024) {
launch_cooperative_topk_impl<1024>(logits, lengths, output, workspace,
max_seq_len);
} else {
launch_cooperative_topk_impl<2048>(logits, lengths, output, workspace,
max_seq_len);
}
#else
STD_TORCH_CHECK(false, "cooperative_topk is not supported on ROCm");
#endif
}
Loading
Loading