[init] baseline7 from project_6

This commit is contained in:
root
2026-08-25 07:05:49 +00:00
commit 0bb337bc00
1392 changed files with 321187 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
/* Copyright 2026 The xLLM Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://github.com/jd-opensource/xllm/blob/main/LICENSE
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <ATen/cuda/CUDAContext.h>
#include <cuda_runtime.h>
#include <torch/script.h>
#include <torch/torch.h>
#include "cuda.h"
namespace xllm::kernel::cuda {
void beam_search(torch::Tensor acc_logprob,
torch::Tensor in_sequence_group,
torch::Tensor top_tokens,
torch::Tensor top_logprobs,
torch::Tensor out_acc_logprob,
torch::Tensor out_token_ids,
torch::Tensor out_token_index,
torch::Tensor out_beam_count_prefix_sums,
torch::Tensor out_sequence_group,
uint32_t batch_size,
uint32_t current_step) {
torch::Device device = acc_logprob.device();
uint32_t beam_size = in_sequence_group.size(1);
uint32_t top_k = top_tokens.size(1);
uint32_t total_rounds = in_sequence_group.size(2);
CHECK_EQ(beam_size, top_k) << "beam_size must be equal with top_k.";
if (current_step == 0) {
auto tokens_view =
top_tokens.view({batch_size, top_k}).slice(1, 0, beam_size);
auto init_probs_view =
top_logprobs.view({batch_size, top_k}).slice(1, 0, beam_size);
out_token_ids.view({batch_size, beam_size}).copy_(tokens_view);
out_acc_logprob.view({batch_size, beam_size}).copy_(init_probs_view);
auto indices =
torch::arange(
beam_size,
torch::TensorOptions().dtype(torch::kInt32).device(device))
.unsqueeze(0)
.expand({batch_size, -1})
.reshape({-1, 1});
out_token_index.copy_(indices);
auto sequence_view =
out_sequence_group.view({batch_size, beam_size, total_rounds});
sequence_view.slice(2, 0, 1).squeeze(2).copy_(tokens_view);
} else {
auto combined_probs =
(acc_logprob + top_logprobs).view({batch_size, beam_size * top_k});
auto topk_result = torch::topk(combined_probs, beam_size, -1);
auto new_probs = std::get<0>(topk_result); // [batch_size, beam_size]
auto new_indices = std::get<1>(topk_result); // [batch_size, beam_size]
auto ordered_indices = new_indices.argsort(static_cast<int64_t>(1), false);
// Reorder new_probs (and corresponding new_indices) by ordered_indices to
// keep alignment.
if (current_step < total_rounds - 1) {
new_probs = new_probs.gather(1, ordered_indices);
new_indices = new_indices.gather(1, ordered_indices);
}
auto parent_beam = (new_indices / top_k).to(torch::kLong);
auto token_in_beam = (new_indices % top_k).to(torch::kLong);
auto top_tokens_reshaped = top_tokens.view({batch_size, beam_size, top_k});
auto batch_idx =
torch::arange(batch_size,
torch::TensorOptions().dtype(torch::kLong).device(device))
.unsqueeze(1)
.expand_as(parent_beam);
using torch::indexing::TensorIndex;
auto new_tokens = top_tokens_reshaped.index({TensorIndex(batch_idx),
TensorIndex(parent_beam),
TensorIndex(token_in_beam)});
out_acc_logprob.view({batch_size, beam_size}).copy_(new_probs);
out_token_index.view({batch_size, beam_size})
.copy_(new_indices.to(torch::kInt32));
out_token_ids.view({batch_size, beam_size}).copy_(new_tokens);
auto batch_range =
torch::arange(
batch_size,
torch::TensorOptions().dtype(torch::kInt32).device(device))
.unsqueeze(1)
.expand({-1, beam_size});
auto beam_range =
torch::arange(
beam_size,
torch::TensorOptions().dtype(torch::kInt32).device(device))
.unsqueeze(0)
.expand({batch_size, -1});
using torch::indexing::Slice;
using torch::indexing::TensorIndex;
out_sequence_group.slice(2, 0, current_step) =
in_sequence_group.index({TensorIndex(batch_range),
TensorIndex(parent_beam.to(torch::kInt32)),
Slice(0, current_step)});
out_sequence_group.slice(2, current_step, current_step + 1) =
new_tokens.unsqueeze(2);
}
}
} // namespace xllm::kernel::cuda

View File

@@ -0,0 +1,312 @@
/* Copyright 2026 The xLLM Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://github.com/jd-opensource/xllm/blob/main/LICENSE
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAException.h>
#include <c10/cuda/CUDAGuard.h>
#include <cuda_runtime.h>
#include <glog/logging.h>
#include <torch/extension.h>
#include <cstdint>
#include <vector>
#include "xattention_ops_api.h"
namespace {
// In-place cache selection kernel for Xattention.
// Reorders KV cache entries based on beam search results. After beam search,
// the beam indices may have changed, and this kernel copies KV cache data from
// old beam positions to new beam positions to maintain consistency.
// Inputs:
// k_ptrs_i64 : [Layer] - pointers to K cache tensors for each layer
// v_ptrs_i64 : [Layer] - pointers to V cache tensors for each layer
// beam_index : [B*Beam] - mapping from new beam index to old beam index
// block_table : [B] - request ID per batch item (extracted from [B*Beam,
// 1]) B : batch size (actual batch size, not batch_size *
// beam_size) Beam : beam width Kv : number of KV
// heads MaxStep : maximum decode steps D : head
// dimension MaxReq : maximum number of requests Layer :
// number of transformer layers decode_step : current decode step
// (0-indexed)
// Cache layout: [MaxReq, Beam, MaxStep, Kv, D]
// The kernel performs two passes to avoid overwriting data:
// pass-1: copy from old_beam > new_beam (increasing new_beam)
// pass-2: copy from old_beam < new_beam (decreasing new_beam)
template <typename scalar_t>
__global__ void cache_select_inplace_ptrs_kernel(
const int64_t* __restrict__ k_ptrs_i64, // [Layer]
const int64_t* __restrict__ v_ptrs_i64, // [Layer]
const int32_t* __restrict__ beam_index, // [B*Beam]
const int32_t* __restrict__ block_table, // [B]
int32_t B,
int32_t Beam,
int32_t Kv,
int32_t MaxStep,
int32_t D,
int32_t MaxReq,
int32_t Layer,
int32_t decode_step) {
const int32_t b = static_cast<int32_t>(blockIdx.x);
const int32_t kv = static_cast<int32_t>(blockIdx.y);
const int32_t layer = static_cast<int32_t>(blockIdx.z);
if (b >= B || kv >= Kv || layer >= Layer) {
return;
}
const int32_t step_end =
decode_step < (MaxStep - 1) ? decode_step : (MaxStep - 1);
const int32_t req = block_table[b];
if (req < 0 || req >= MaxReq) {
return;
}
scalar_t* __restrict__ k_cache =
reinterpret_cast<scalar_t*>(static_cast<uintptr_t>(k_ptrs_i64[layer]));
scalar_t* __restrict__ v_cache =
reinterpret_cast<scalar_t*>(static_cast<uintptr_t>(v_ptrs_i64[layer]));
// base(req, beam, s, kv, d) = ((((req*Beam + beam)*MaxStep + s)*Kv + kv) * D
// + d)
const int64_t req_base = static_cast<int64_t>(req) * Beam;
const int64_t step_kv_stride = static_cast<int64_t>(Kv) * D;
const int64_t kv_d_base = static_cast<int64_t>(kv) * D;
// grid_step is typically small; loop over s in-kernel to reduce launch
// blocks.
for (int32_t s = 0; s <= step_end; ++s) {
// pass-1: new_beam increasing, copy if old_beam > new_beam
for (int32_t new_beam = 0; new_beam < Beam; ++new_beam) {
const int32_t old_beam = beam_index[b * Beam + new_beam] / Beam;
if (old_beam >= 0 && old_beam < Beam && old_beam > new_beam) {
const int64_t dst_base =
((req_base + new_beam) * MaxStep + s) * step_kv_stride + kv_d_base;
const int64_t src_base =
((req_base + old_beam) * MaxStep + s) * step_kv_stride + kv_d_base;
for (int32_t d = static_cast<int32_t>(threadIdx.x); d < D;
d += static_cast<int32_t>(blockDim.x)) {
k_cache[dst_base + d] = k_cache[src_base + d];
v_cache[dst_base + d] = v_cache[src_base + d];
}
}
}
// pass-2: new_beam decreasing, copy if old_beam < new_beam
for (int32_t new_beam = Beam - 1; new_beam >= 0; --new_beam) {
const int32_t old_beam = beam_index[b * Beam + new_beam] / Beam;
if (old_beam >= 0 && old_beam < Beam && old_beam < new_beam) {
const int64_t dst_base =
((req_base + new_beam) * MaxStep + s) * step_kv_stride + kv_d_base;
const int64_t src_base =
((req_base + old_beam) * MaxStep + s) * step_kv_stride + kv_d_base;
for (int32_t d = static_cast<int32_t>(threadIdx.x); d < D;
d += static_cast<int32_t>(blockDim.x)) {
k_cache[dst_base + d] = k_cache[src_base + d];
v_cache[dst_base + d] = v_cache[src_base + d];
}
}
}
}
}
void cache_select_cuda_launch_ptrs(
torch::Tensor k0,
torch::Tensor v0,
torch::Tensor k_ptrs_i64, // [Layer] int64 (CUDA)
torch::Tensor v_ptrs_i64, // [Layer] int64 (CUDA)
torch::Tensor beam_index_i32, // [B*Beam, 1] int32
torch::Tensor block_table_i32, // [B] int32
int64_t decode_step,
int64_t layer_num) {
CHECK(k_ptrs_i64.is_cuda() && v_ptrs_i64.is_cuda())
<< "k_ptrs_i64/v_ptrs_i64 must be CUDA";
CHECK_EQ(k_ptrs_i64.scalar_type(), torch::kInt64)
<< "k_ptrs_i64/v_ptrs_i64 must be int64";
CHECK_EQ(v_ptrs_i64.scalar_type(), torch::kInt64)
<< "k_ptrs_i64/v_ptrs_i64 must be int64";
CHECK(k_ptrs_i64.is_contiguous() && v_ptrs_i64.is_contiguous())
<< "k_ptrs_i64/v_ptrs_i64 must be contiguous";
const int64_t B64 = block_table_i32.size(0);
const int64_t Beam64 = k0.size(1);
const int64_t MaxStep64 = k0.size(2);
const int64_t Kv64 = k0.size(3);
const int64_t D64 = k0.size(4);
const int64_t MaxReq64 = k0.size(0);
const int64_t Layer64 = layer_num;
const int32_t B = static_cast<int32_t>(B64);
const int32_t Beam = static_cast<int32_t>(Beam64);
const int32_t Kv = static_cast<int32_t>(Kv64);
const int32_t MaxStep = static_cast<int32_t>(MaxStep64);
const int32_t D = static_cast<int32_t>(D64);
const int32_t MaxReq = static_cast<int32_t>(MaxReq64);
const int32_t Layer = static_cast<int32_t>(Layer64);
const int32_t decode_step_i32 = static_cast<int32_t>(decode_step);
// Warp-aligned threads, capped to keep occupancy reasonable.
int threads_per_block = ((D + 31) / 32) * 32;
if (threads_per_block < 32) {
threads_per_block = 32;
}
if (threads_per_block > 256) {
threads_per_block = 256;
}
dim3 block_dim(static_cast<unsigned int>(threads_per_block), 1, 1);
CHECK_LE(Kv64, static_cast<int64_t>(UINT32_MAX)) << "Kv too large for grid.y";
CHECK_LE(Layer64, 65535) << "layer_num too large for grid.z";
dim3 grid_dim(static_cast<unsigned int>(B),
static_cast<unsigned int>(Kv),
static_cast<unsigned int>(Layer));
cudaStream_t stream = at::cuda::getCurrentCUDAStream();
AT_DISPATCH_FLOATING_TYPES_AND2(torch::ScalarType::Half,
torch::ScalarType::BFloat16,
k0.scalar_type(),
"cache_select_inplace_ptrs_kernel",
[&] {
cache_select_inplace_ptrs_kernel<scalar_t>
<<<grid_dim, block_dim, 0, stream>>>(
k_ptrs_i64.data_ptr<int64_t>(),
v_ptrs_i64.data_ptr<int64_t>(),
beam_index_i32.data_ptr<int32_t>(),
block_table_i32.data_ptr<int32_t>(),
B,
Beam,
Kv,
MaxStep,
D,
MaxReq,
Layer,
decode_step_i32);
});
C10_CUDA_KERNEL_LAUNCH_CHECK();
}
} // namespace
namespace xllm::kernel::cuda {
void cache_select(const torch::Tensor& beam_index, // [B*Beam, 1]
std::vector<torch::Tensor>& unshared_k_cache,
std::vector<torch::Tensor>& unshared_v_cache,
const torch::Tensor& block_table, // [B*Beam, 1]
int64_t decode_step,
int64_t beam_size,
int64_t layer_num) {
CHECK_GE(layer_num, 0) << "layer_num must be >= 0";
if (layer_num == 0) {
return;
}
CHECK_EQ(static_cast<int64_t>(unshared_k_cache.size()), layer_num)
<< "unshared_k_cache length mismatch";
CHECK_EQ(static_cast<int64_t>(unshared_v_cache.size()), layer_num)
<< "unshared_v_cache length mismatch";
CHECK(beam_index.is_cuda()) << "beam_index must be CUDA";
CHECK(block_table.is_cuda()) << "block_table must be CUDA";
CHECK_EQ(block_table.dim(), 2) << "block_table must be [B*Beam, 1]";
CHECK_EQ(block_table.size(1), 1) << "block_table must be [B*Beam, 1]";
CHECK_EQ(beam_index.dim(), 2) << "beam_index must be [B*Beam, 1]";
CHECK_EQ(beam_index.size(1), 1) << "beam_index must be [B*Beam, 1]";
CHECK_GE(decode_step, 0) << "decode_step must be >= 0";
CHECK_GT(beam_size, 0) << "beam_size must be > 0";
// block_table is [B*Beam, 1] with sequential values [0,1,2,3,...]
// Infer actual batch_size
CHECK_EQ(block_table.size(0) % beam_size, 0)
<< "block_table.size(0) must be divisible by beam_size";
const int64_t B = block_table.size(0) / beam_size;
CHECK_EQ(beam_index.size(0), B * beam_size)
<< "beam_index size mismatch with B*beam_size";
// Prepare indices (int32, contiguous).
auto beam_index_i32 = beam_index.to(torch::kInt32).contiguous();
auto block_table_i32 = torch::arange(
0,
B,
torch::TensorOptions().dtype(torch::kInt32).device(block_table.device()));
// Validate shapes/dtypes against layer 0.
const auto& k0 = unshared_k_cache[0];
const auto& v0 = unshared_v_cache[0];
CHECK(k0.is_cuda() && v0.is_cuda()) << "cache must be CUDA";
CHECK(k0.is_contiguous() && v0.is_contiguous()) << "cache must be contiguous";
CHECK_EQ(k0.dim(), 5) << "cache must be 5D [MaxReq, Beam, MaxStep, Kv, D]";
CHECK_EQ(v0.sizes(), k0.sizes()) << "k/v cache shapes must match";
CHECK_EQ(k0.size(1), beam_size) << "beam_size mismatch with cache";
CHECK_LT(decode_step, k0.size(2)) << "decode_step must be < max_decode_step";
// Pack layer pointers into CUDA int64 tensors so we can launch once.
// Note: pointer values are produced on host (data_ptr()), then copied to GPU.
c10::cuda::CUDAGuard device_guard(k0.device());
auto ptr_cuda_opts =
torch::TensorOptions().dtype(torch::kInt64).device(k0.device());
auto k_ptrs_i64 = torch::empty({layer_num}, ptr_cuda_opts);
auto v_ptrs_i64 = torch::empty({layer_num}, ptr_cuda_opts);
std::vector<int64_t> k_ptrs_host(static_cast<size_t>(layer_num));
std::vector<int64_t> v_ptrs_host(static_cast<size_t>(layer_num));
for (int64_t layer = 0; layer < layer_num; ++layer) {
auto k = unshared_k_cache[static_cast<size_t>(layer)];
auto v = unshared_v_cache[static_cast<size_t>(layer)];
CHECK(k.is_cuda() && v.is_cuda()) << "cache must be CUDA";
CHECK(k.is_contiguous() && v.is_contiguous()) << "cache must be contiguous";
CHECK_EQ(k.sizes(), k0.sizes()) << "all layers must have same cache shape";
CHECK_EQ(v.sizes(), k0.sizes()) << "all layers must have same cache shape";
CHECK_EQ(k.scalar_type(), k0.scalar_type())
<< "all layers must have same dtype";
CHECK_EQ(v.scalar_type(), k0.scalar_type())
<< "all layers must have same dtype";
CHECK_EQ(k.get_device(), k0.get_device())
<< "all layers must be on the same CUDA device";
CHECK_EQ(v.get_device(), k0.get_device())
<< "all layers must be on the same CUDA device";
k_ptrs_host[static_cast<size_t>(layer)] =
static_cast<int64_t>(reinterpret_cast<uintptr_t>(k.data_ptr()));
v_ptrs_host[static_cast<size_t>(layer)] =
static_cast<int64_t>(reinterpret_cast<uintptr_t>(v.data_ptr()));
}
cudaStream_t stream = at::cuda::getCurrentCUDAStream();
C10_CUDA_CHECK(
cudaMemcpyAsync(k_ptrs_i64.data_ptr<int64_t>(),
k_ptrs_host.data(),
static_cast<size_t>(layer_num) * sizeof(int64_t),
cudaMemcpyHostToDevice,
stream));
C10_CUDA_CHECK(
cudaMemcpyAsync(v_ptrs_i64.data_ptr<int64_t>(),
v_ptrs_host.data(),
static_cast<size_t>(layer_num) * sizeof(int64_t),
cudaMemcpyHostToDevice,
stream));
cache_select_cuda_launch_ptrs(k0,
v0,
k_ptrs_i64,
v_ptrs_i64,
beam_index_i32,
block_table_i32,
decode_step,
layer_num);
}
} // namespace xllm::kernel::cuda

View File

@@ -0,0 +1,298 @@
/* Copyright 2026 The xLLM Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://github.com/jd-opensource/xllm/blob/main/LICENSE
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <c10/cuda/CUDAGuard.h>
#include <cuda_runtime.h>
#include <torch/cuda.h>
#include <cstdint>
#include <type_traits>
#include "kernels/cuda/utils.h"
#include "xattention_ops_api.h"
namespace {
template <typename scalar_t>
struct VecType;
template <>
struct VecType<c10::Half> {
using type = uint4; // 8 elements * 2 bytes = 16 bytes
static constexpr int32_t vec_width = 8;
};
template <>
struct VecType<c10::BFloat16> {
using type = uint4; // 8 elements * 2 bytes = 16 bytes
static constexpr int32_t vec_width = 8;
};
template <>
struct VecType<float> {
using type = float4; // 4 elements * 4 bytes = 16 bytes
static constexpr int32_t vec_width = 4;
};
// decoder reshape and cache kernel.
// Copies proj_k and proj_v into unshared_k_cache / unshared_v_cache.
// Inputs:
// proj_k : [batch_size, beam_size, kv_heads, head_dim]
// proj_v : [batch_size, beam_size, kv_heads, head_dim]
// step : [1] - current decode step
// batch_size : batch size
// beam_size : beam size
// kv_heads : number of kv heads
// head_dim : head dimension
// k_stride0 : proj_k.stride(0)
// k_stride1 : proj_k.stride(1)
// v_stride0 : proj_v.stride(0)
// v_stride1 : proj_v.stride(1)
// cache_stride0 : unshared_k_cache.stride(0)
// cache_stride1 : unshared_k_cache.stride(1)
// cache_stride2 : unshared_k_cache.stride(2)
// cache_stride3 : unshared_k_cache.stride(3)
// Outputs:
// unshared_k_cache : [max_batch_size, beam_size, max_step, kv_heads,
// head_dim]
// unshared_v_cache : [max_batch_size, beam_size, max_step, kv_heads,
// head_dim]
template <typename scalar_t>
__global__ void decoder_reshape_and_cache_kernel(
const scalar_t* __restrict__ proj_k,
const scalar_t* __restrict__ proj_v,
scalar_t* __restrict__ unshared_k_cache,
scalar_t* __restrict__ unshared_v_cache,
const int32_t* __restrict__ step,
const int64_t batch_size,
const int64_t beam_size,
const int64_t kv_heads,
const int64_t head_dim,
const int64_t k_stride0,
const int64_t k_stride1,
const int64_t v_stride0,
const int64_t v_stride1,
const int64_t cache_stride0,
const int64_t cache_stride1,
const int64_t cache_stride2,
const int64_t cache_stride3) {
using VecTypeT = typename VecType<scalar_t>::type;
constexpr int32_t VEC_WIDTH = VecType<scalar_t>::vec_width;
const int64_t token_idx = static_cast<int64_t>(blockIdx.y);
const int64_t total_tokens = batch_size * beam_size;
if (token_idx >= total_tokens) {
return;
}
const int64_t batch_idx = token_idx / beam_size;
const int64_t beam_idx = token_idx - batch_idx * beam_size;
__shared__ int32_t current_step_s;
if (threadIdx.x == 0) {
current_step_s = __ldg(step);
}
__syncthreads();
const int64_t current_step = static_cast<int64_t>(current_step_s);
const int64_t vecs_per_head = head_dim / VEC_WIDTH;
const int64_t total_vecs = kv_heads * vecs_per_head;
const int64_t k_token_base = batch_idx * k_stride0 + beam_idx * k_stride1;
const int64_t v_token_base = batch_idx * v_stride0 + beam_idx * v_stride1;
const int64_t dst_token_base = batch_idx * cache_stride0 +
beam_idx * cache_stride1 +
current_step * cache_stride2;
for (int64_t linear_idx = static_cast<int64_t>(threadIdx.x);
linear_idx < total_vecs;
linear_idx += static_cast<int64_t>(blockDim.x)) {
const int64_t head_idx = linear_idx / vecs_per_head;
const int64_t vec_idx = linear_idx - head_idx * vecs_per_head;
const int64_t vec_offset = vec_idx * VEC_WIDTH;
const auto* k_src_vec = reinterpret_cast<const VecTypeT*>(
proj_k + k_token_base + head_idx * head_dim + vec_offset);
const auto* v_src_vec = reinterpret_cast<const VecTypeT*>(
proj_v + v_token_base + head_idx * head_dim + vec_offset);
auto* k_dst_vec =
reinterpret_cast<VecTypeT*>(unshared_k_cache + dst_token_base +
head_idx * cache_stride3 + vec_offset);
auto* v_dst_vec =
reinterpret_cast<VecTypeT*>(unshared_v_cache + dst_token_base +
head_idx * cache_stride3 + vec_offset);
*k_dst_vec = *k_src_vec;
*v_dst_vec = *v_src_vec;
}
}
} // namespace
namespace xllm::kernel::cuda {
void decoder_reshape_and_cache(torch::Tensor proj_k,
torch::Tensor proj_v,
torch::Tensor unshared_k_cache,
torch::Tensor unshared_v_cache,
torch::Tensor step) {
CHECK_EQ(proj_k.dim(), 4) << "proj_k must be 4-dimensional";
CHECK_EQ(proj_v.dim(), 4) << "proj_v must be 4-dimensional";
CHECK_EQ(unshared_k_cache.dim(), 5)
<< "unshared_k_cache must be 5-dimensional";
CHECK_EQ(unshared_v_cache.dim(), 5)
<< "unshared_v_cache must be 5-dimensional";
CHECK(proj_k.is_cuda() && proj_v.is_cuda() && unshared_k_cache.is_cuda() &&
unshared_v_cache.is_cuda() && step.is_cuda())
<< "all tensors must be CUDA tensors";
CHECK_EQ(step.dim(), 1) << "step must be 1-dimensional";
CHECK_EQ(step.size(0), 1) << "step must have shape [1]";
CHECK_EQ(step.scalar_type(), at::ScalarType::Int)
<< "step must be int32 (torch::kInt32)";
const int64_t batch_size = proj_k.size(0);
const int64_t beam_size = proj_k.size(1);
const int64_t kv_heads = proj_k.size(2);
const int64_t head_dim = proj_k.size(3);
CHECK_EQ(proj_v.sizes(), proj_k.sizes())
<< "proj_v and proj_k must have same shape";
CHECK_EQ(unshared_k_cache.size(3), kv_heads)
<< "unshared_k_cache kv_heads mismatch";
CHECK_EQ(unshared_k_cache.size(4), head_dim)
<< "unshared_k_cache head_dim mismatch";
CHECK(unshared_v_cache.sizes() == unshared_k_cache.sizes())
<< "unshared_v_cache and unshared_k_cache must have same shape";
// This kernel is specialized for qkv-slice layouts:
// last dim contiguous and kv head stride tightly packed by head_dim.
CHECK_EQ(proj_k.stride(3), 1) << "proj_k must satisfy stride(3)=1";
CHECK_EQ(proj_v.stride(3), 1) << "proj_v must satisfy stride(3)=1";
CHECK_EQ(proj_k.stride(2), head_dim)
<< "proj_k must satisfy stride(2)=head_dim";
CHECK_EQ(proj_v.stride(2), head_dim)
<< "proj_v must satisfy stride(2)=head_dim";
CHECK_EQ(unshared_k_cache.stride(4), 1)
<< "unshared_k_cache must satisfy stride(4)=1";
CHECK_EQ(unshared_v_cache.stride(4), 1)
<< "unshared_v_cache must satisfy stride(4)=1";
CHECK_EQ(unshared_k_cache.stride(3), head_dim)
<< "unshared_k_cache must satisfy stride(3)=head_dim";
CHECK_EQ(unshared_v_cache.stride(3), head_dim)
<< "unshared_v_cache must satisfy stride(3)=head_dim";
const at::cuda::OptionalCUDAGuard device_guard(device_of(proj_k));
const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
const int64_t k_stride0 = proj_k.stride(0);
const int64_t k_stride1 = proj_k.stride(1);
const int64_t v_stride0 = proj_v.stride(0);
const int64_t v_stride1 = proj_v.stride(1);
const int64_t cache_stride0 = unshared_k_cache.stride(0);
const int64_t cache_stride1 = unshared_k_cache.stride(1);
const int64_t cache_stride2 = unshared_k_cache.stride(2);
const int64_t cache_stride3 = unshared_k_cache.stride(3);
// Launch kernel: one block per (batch, beam), threads cover
// kv_heads*head_dim.
const int64_t total_tokens = batch_size * beam_size;
dim3 grid_dim(1, static_cast<unsigned int>(total_tokens), 1);
DISPATCH_FLOATING_TYPES(
proj_k.scalar_type(), "decoder_reshape_and_cache_kernel", [&] {
constexpr int32_t VEC_WIDTH = (std::is_same_v<scalar_t, c10::Half> ||
std::is_same_v<scalar_t, c10::BFloat16>)
? 8
: 4; // FP16/BF16: 8, Float: 4
constexpr int32_t kWarpSize = 32;
constexpr int32_t kMaxThreadsPerBlock = 256;
constexpr int32_t kAlignmentBytes = 16; // 128-bit alignment
CHECK(head_dim % VEC_WIDTH == 0)
<< "head_dim must be divisible by vector width: " << VEC_WIDTH;
const int64_t vecs_per_head = head_dim / VEC_WIDTH;
const int64_t total_vecs = kv_heads * vecs_per_head;
CHECK(total_vecs > 0) << "total_vecs must be > 0";
int32_t threads_per_block = static_cast<int32_t>(
total_vecs > kMaxThreadsPerBlock ? kMaxThreadsPerBlock
: total_vecs);
threads_per_block =
((threads_per_block + kWarpSize - 1) / kWarpSize) * kWarpSize;
if (threads_per_block < kWarpSize) {
threads_per_block = kWarpSize;
}
dim3 block_dim(threads_per_block, 1, 1);
const auto proj_k_ptr =
reinterpret_cast<std::uintptr_t>(proj_k.data_ptr<scalar_t>());
const auto proj_v_ptr =
reinterpret_cast<std::uintptr_t>(proj_v.data_ptr<scalar_t>());
const auto k_cache_ptr = reinterpret_cast<std::uintptr_t>(
unshared_k_cache.data_ptr<scalar_t>());
const auto v_cache_ptr = reinterpret_cast<std::uintptr_t>(
unshared_v_cache.data_ptr<scalar_t>());
CHECK(proj_k_ptr % kAlignmentBytes == 0)
<< "proj_k data_ptr must be 16-byte aligned";
CHECK(proj_v_ptr % kAlignmentBytes == 0)
<< "proj_v data_ptr must be 16-byte aligned";
CHECK(k_cache_ptr % kAlignmentBytes == 0)
<< "unshared_k_cache data_ptr must be 16-byte aligned";
CHECK(v_cache_ptr % kAlignmentBytes == 0)
<< "unshared_v_cache data_ptr must be 16-byte aligned";
const int64_t scalar_bytes = static_cast<int64_t>(sizeof(scalar_t));
CHECK((k_stride0 * scalar_bytes) % kAlignmentBytes == 0)
<< "proj_k stride(0) bytes must be 16-byte aligned";
CHECK((k_stride1 * scalar_bytes) % kAlignmentBytes == 0)
<< "proj_k stride(1) bytes must be 16-byte aligned";
CHECK((v_stride0 * scalar_bytes) % kAlignmentBytes == 0)
<< "proj_v stride(0) bytes must be 16-byte aligned";
CHECK((v_stride1 * scalar_bytes) % kAlignmentBytes == 0)
<< "proj_v stride(1) bytes must be 16-byte aligned";
CHECK((cache_stride0 * scalar_bytes) % kAlignmentBytes == 0)
<< "cache stride(0) bytes must be 16-byte aligned";
CHECK((cache_stride1 * scalar_bytes) % kAlignmentBytes == 0)
<< "cache stride(1) bytes must be 16-byte aligned";
CHECK((cache_stride2 * scalar_bytes) % kAlignmentBytes == 0)
<< "cache stride(2) bytes must be 16-byte aligned";
CHECK((cache_stride3 * scalar_bytes) % kAlignmentBytes == 0)
<< "cache stride(3) bytes must be 16-byte aligned";
decoder_reshape_and_cache_kernel<scalar_t>
<<<grid_dim, block_dim, 0, stream>>>(
proj_k.data_ptr<scalar_t>(),
proj_v.data_ptr<scalar_t>(),
unshared_k_cache.data_ptr<scalar_t>(),
unshared_v_cache.data_ptr<scalar_t>(),
step.data_ptr<int32_t>(),
batch_size,
beam_size,
kv_heads,
head_dim,
k_stride0,
k_stride1,
v_stride0,
v_stride1,
cache_stride0,
cache_stride1,
cache_stride2,
cache_stride3);
});
C10_CUDA_KERNEL_LAUNCH_CHECK();
}
} // namespace xllm::kernel::cuda

View File

@@ -0,0 +1,168 @@
/* Copyright 2026 The xLLM Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://github.com/jd-opensource/xllm/blob/main/LICENSE
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <c10/cuda/CUDAGuard.h>
#include <cuda_runtime.h>
#include <torch/cuda.h>
#include <cmath>
#include "kernels/cuda/utils.h"
#include "xattention_ops_api.h"
namespace {
// Fused log-sum-exp combine kernel.
//
// Layout and strategy (aligned with the TileLang version):
// - Each block is responsible for one (batch_idx, head_idx) pair, i.e. one
// row in the flattened [B * H, D] layout.
// - Threads within a block parallelize along the head_dim (D) dimension to
// ensure coalesced global memory access.
//
// Tensors:
// shared_o : [B, H, D] - shared attention output
// shared_lse : [B, H, 1] - shared log-sum-exp (FP32)
// unshared_o : [B, H, D] - unshared attention output
// unshared_lse: [B, H, 1] - unshared log-sum-exp (FP32)
// output : [B, H, D] - combined output
template <typename scalar_t, typename out_scalar_t>
__global__ void lse_combine_kernel(
out_scalar_t* __restrict__ output, // [B, H, D]
const scalar_t* __restrict__ shared_o, // [B, H, D]
const float* __restrict__ shared_lse, // [B, H, 1], always FP32
const scalar_t* __restrict__ unshared_o, // [B, H, D]
const float* __restrict__ unshared_lse, // [B, H, 1], always FP32
const int64_t B, // batch_size * beam_size
const int64_t H, // num_heads
const int64_t D) { // head_dim
const int64_t total_elements = B * H;
const int64_t idx = static_cast<int64_t>(blockIdx.y);
if (idx >= total_elements) {
return;
}
// Load LSE scalars for this (batch, head) pair.
const float shared_lse_val = shared_lse[idx];
const float unshared_lse_val = unshared_lse[idx];
// 1. Compute element-wise max LSE.
const float lse_max = fmaxf(shared_lse_val, unshared_lse_val);
// 2. Compute base-2 exponentials relative to max.
const float exp_shared = exp2f(shared_lse_val - lse_max);
const float exp_unshared = exp2f(unshared_lse_val - lse_max);
// 3. Compute merged LSE.
const float lse_new = lse_max + log2f(exp_shared + exp_unshared);
// 4. Compute normalized weights.
const float w_shared = exp2f(shared_lse_val - lse_new);
const float w_unshared = exp2f(unshared_lse_val - lse_new);
// 5. Weighted combine along the head_dim.
const int64_t base_idx = idx * D;
// Threads in the block parallelize along D with stride blockDim.x for
// coalesced global memory access.
for (int64_t d = threadIdx.x; d < D; d += blockDim.x) {
const float shared_val = static_cast<float>(shared_o[base_idx + d]);
const float unshared_val = static_cast<float>(unshared_o[base_idx + d]);
const float combined = w_shared * shared_val + w_unshared * unshared_val;
output[base_idx + d] = static_cast<out_scalar_t>(combined);
}
}
} // namespace
namespace xllm::kernel::cuda {
// Host wrapper for the fused LSE combine kernel.
//
// All inputs are expected to be on the same CUDA device:
// shared_o : [B, H, D], floating type (including Half/BFloat16)
// shared_lse : [B, H, 1], float32
// unshared_o : [B, H, D], same type/shape as shared_o
// unshared_lse: [B, H, 1], float32
// output : [B, H, D], will be resized/allocated as needed.
void lse_combine(torch::Tensor output,
torch::Tensor shared_o,
torch::Tensor shared_lse,
torch::Tensor unshared_o,
torch::Tensor unshared_lse) {
CHECK_EQ(shared_o.dim(), 3) << "shared_o must be 3D [B, H, D]";
CHECK_EQ(unshared_o.dim(), 3) << "unshared_o must be 3D [B, H, D]";
CHECK_EQ(shared_lse.dim(), 3) << "shared_lse must be 3D [B, H, 1]";
CHECK_EQ(unshared_lse.dim(), 3) << "unshared_lse must be 3D [B, H, 1]";
const int64_t B = shared_o.size(0);
const int64_t H = shared_o.size(1);
const int64_t D = shared_o.size(2);
CHECK_EQ(shared_o.sizes(), unshared_o.sizes())
<< "shared_o and unshared_o must have same shape";
CHECK_EQ(shared_lse.scalar_type(), torch::kFloat32)
<< "shared_lse must be float32";
CHECK_EQ(unshared_lse.scalar_type(), torch::kFloat32)
<< "unshared_lse must be float32";
CHECK_EQ(shared_lse.size(0), B)
<< "shared_lse shape mismatch, expected [B, H, 1]";
CHECK_EQ(shared_lse.size(1), H)
<< "shared_lse shape mismatch, expected [B, H, 1]";
CHECK_EQ(shared_lse.size(2), 1)
<< "shared_lse shape mismatch, expected [B, H, 1]";
CHECK_EQ(unshared_lse.size(0), B)
<< "unshared_lse shape mismatch, expected [B, H, 1]";
CHECK_EQ(unshared_lse.size(1), H)
<< "unshared_lse shape mismatch, expected [B, H, 1]";
CHECK_EQ(unshared_lse.size(2), 1)
<< "unshared_lse shape mismatch, expected [B, H, 1]";
// Ensure output has the correct shape and dtype.
if (!output.defined() || output.sizes() != shared_o.sizes()) {
output = torch::empty_like(shared_o);
}
const at::cuda::OptionalCUDAGuard device_guard(device_of(shared_o));
const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
// Launch kernel: one block per (batch, head) pair, threads along D.
const int64_t total_elements = B * H;
const int threads_per_block = 128;
dim3 block_dim(threads_per_block, 1, 1);
dim3 grid_dim(1, static_cast<unsigned int>(total_elements), 1);
DISPATCH_FLOATING_TYPES(
shared_o.scalar_type(), "lse_combine_kernel_input", [&] {
using in_t = scalar_t;
DISPATCH_FLOATING_TYPES(
output.scalar_type(), "lse_combine_kernel_output", [&] {
using out_t = scalar_t;
lse_combine_kernel<in_t, out_t>
<<<grid_dim, block_dim, 0, stream>>>(
output.data_ptr<out_t>(),
shared_o.data_ptr<in_t>(),
shared_lse.data_ptr<float>(),
unshared_o.data_ptr<in_t>(),
unshared_lse.data_ptr<float>(),
B,
H,
D);
});
});
C10_CUDA_KERNEL_LAUNCH_CHECK();
}
} // namespace xllm::kernel::cuda

View File

@@ -0,0 +1,220 @@
/* Copyright 2026 The xLLM Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://github.com/jd-opensource/xllm/blob/main/LICENSE
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <c10/cuda/CUDAGuard.h>
#include <cuda_runtime.h>
#include <glog/logging.h>
#include <torch/cuda.h>
#include <cstdint>
#include <type_traits>
#include "kernels/cuda/cuda_ops_api.h"
#include "kernels/cuda/utils.h"
using at::device_of;
namespace {
template <typename scalar_t>
struct VecType;
template <>
struct VecType<c10::Half> {
using type = uint4; // 8 elements * 2 bytes = 16 bytes
static constexpr int32_t vec_width = 8;
};
template <>
struct VecType<c10::BFloat16> {
using type = uint4; // 8 elements * 2 bytes = 16 bytes
static constexpr int32_t vec_width = 8;
};
template <>
struct VecType<float> {
using type = float4; // 4 elements * 4 bytes = 16 bytes
static constexpr int32_t vec_width = 4;
};
template <typename scalar_t>
__global__ void prefill_reshape_and_cache_kernel(
const scalar_t* __restrict__ proj_k, // [shared_len, kv_heads, head_dim]
const scalar_t* __restrict__ proj_v, // [shared_len, kv_heads, head_dim]
scalar_t* __restrict__ shared_k_cache, // [shared_len, kv_heads, head_dim]
scalar_t* __restrict__ shared_v_cache, // [shared_len, kv_heads, head_dim]
const int64_t shared_len,
const int64_t kv_heads,
const int64_t head_dim,
const int64_t k_stride0, // proj_k.stride(0)
const int64_t v_stride0, // proj_v.stride(0)
const int64_t v_stride1) { // proj_v.stride(1), same as head_dim
using VecTypeT = typename VecType<scalar_t>::type;
constexpr int32_t VEC_WIDTH = VecType<scalar_t>::vec_width;
const int64_t token_idx = static_cast<int64_t>(blockIdx.y);
if (token_idx >= shared_len) {
return;
}
const int64_t vecs_per_head = head_dim / VEC_WIDTH;
const int64_t total_vecs = kv_heads * vecs_per_head;
const int64_t k_token_base = token_idx * k_stride0;
const int64_t v_token_base = token_idx * v_stride0;
const int64_t dst_token_base = token_idx * kv_heads * head_dim;
for (int64_t linear_idx = threadIdx.x; linear_idx < total_vecs;
linear_idx += blockDim.x) {
const int64_t head_idx = linear_idx / vecs_per_head;
const int64_t vec_idx = linear_idx - head_idx * vecs_per_head;
const int64_t head_offset = head_idx * head_dim;
const int64_t vec_offset = vec_idx * VEC_WIDTH;
const auto* k_src_vec = reinterpret_cast<const VecTypeT*>(
proj_k + k_token_base + head_offset + vec_offset);
const auto* v_src_vec = reinterpret_cast<const VecTypeT*>(
proj_v + v_token_base + head_idx * v_stride1 + vec_offset);
auto* k_dst_vec = reinterpret_cast<VecTypeT*>(
shared_k_cache + dst_token_base + head_offset + vec_offset);
auto* v_dst_vec = reinterpret_cast<VecTypeT*>(
shared_v_cache + dst_token_base + head_offset + vec_offset);
*k_dst_vec = *k_src_vec;
*v_dst_vec = *v_src_vec;
}
}
} // namespace
namespace xllm::kernel::cuda {
void prefill_reshape_and_cache(
torch::Tensor proj_k, // [shared_len, kv_heads, head_dim]
torch::Tensor proj_v, // [shared_len, kv_heads, head_dim]
torch::Tensor
shared_k_cache, // [num_shared_kv_seq_len, kv_heads, head_dim]
torch::Tensor shared_v_cache) {
CHECK(proj_k.dim() == 3) << "proj_k must be 3-dimensional";
CHECK(proj_v.dim() == 3) << "proj_v must be 3-dimensional";
CHECK(shared_k_cache.dim() == 3) << "shared_k_cache must be 3-dimensional";
CHECK(shared_v_cache.dim() == 3) << "shared_v_cache must be 3-dimensional";
CHECK(proj_k.is_cuda() && proj_v.is_cuda() && shared_k_cache.is_cuda() &&
shared_v_cache.is_cuda())
<< "all tensors must be CUDA tensors";
const int64_t shared_len = proj_k.size(0);
const int64_t kv_heads = proj_k.size(1);
const int64_t head_dim = proj_k.size(2);
CHECK(proj_v.sizes() == proj_k.sizes())
<< "proj_v and proj_k must have same shape";
CHECK(shared_k_cache.size(0) >= shared_len &&
shared_k_cache.size(1) == kv_heads &&
shared_k_cache.size(2) == head_dim)
<< "shared_k_cache shape mismatch";
CHECK(shared_v_cache.size(0) >= shared_len &&
shared_v_cache.size(1) == kv_heads &&
shared_v_cache.size(2) == head_dim)
<< "shared_v_cache shape mismatch";
shared_k_cache = shared_k_cache.slice(0, 0, shared_len);
shared_v_cache = shared_v_cache.slice(0, 0, shared_len);
// This kernel is specialized for qkv-slice layouts:
// last dim contiguous and head stride tightly packed by head_dim.
CHECK(proj_k.stride(2) == 1 && proj_v.stride(2) == 1)
<< "proj_k/proj_v must be contiguous on head_dim (stride(2)=1)";
CHECK(proj_k.stride(1) == head_dim && proj_v.stride(1) == head_dim)
<< "proj_k/proj_v must satisfy stride(1)=head_dim for qkv-slice layout";
CHECK(shared_k_cache.stride(2) == 1 && shared_v_cache.stride(2) == 1)
<< "shared caches must be contiguous on head_dim (stride(2)=1)";
CHECK(shared_k_cache.stride(1) == head_dim &&
shared_v_cache.stride(1) == head_dim)
<< "shared caches must satisfy stride(1)=head_dim";
CHECK(shared_k_cache.stride(0) == kv_heads * head_dim &&
shared_v_cache.stride(0) == kv_heads * head_dim)
<< "shared caches must be contiguous on token stride";
const at::cuda::OptionalCUDAGuard device_guard(device_of(proj_k));
const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
const int64_t k_stride0 = proj_k.stride(0);
const int64_t v_stride0 = proj_v.stride(0);
const int64_t v_stride1 = proj_v.stride(1);
dim3 grid_dim(1, static_cast<unsigned int>(shared_len), 1);
DISPATCH_FLOATING_TYPES(
proj_k.scalar_type(), "prefill_reshape_and_cache_kernel", [&] {
constexpr int32_t VEC_WIDTH = (std::is_same_v<scalar_t, c10::Half> ||
std::is_same_v<scalar_t, c10::BFloat16>)
? 8
: 4; // FP16/BF16: 8, Float: 4
constexpr int32_t kWarpSize = 32;
constexpr int32_t kMaxThreadsPerBlock = 256;
CHECK(head_dim % VEC_WIDTH == 0)
<< "head_dim must be divisible by vector width: " << VEC_WIDTH;
const int64_t vecs_per_head = head_dim / VEC_WIDTH;
const int64_t total_vecs = kv_heads * vecs_per_head;
CHECK(total_vecs > 0) << "total_vecs must be > 0";
int32_t threads_per_block = static_cast<int32_t>(
total_vecs > kMaxThreadsPerBlock ? kMaxThreadsPerBlock
: total_vecs);
threads_per_block =
((threads_per_block + kWarpSize - 1) / kWarpSize) * kWarpSize;
if (threads_per_block < kWarpSize) {
threads_per_block = kWarpSize;
}
dim3 block_dim(threads_per_block, 1, 1);
const auto proj_k_ptr =
reinterpret_cast<std::uintptr_t>(proj_k.data_ptr<scalar_t>());
const auto proj_v_ptr =
reinterpret_cast<std::uintptr_t>(proj_v.data_ptr<scalar_t>());
const auto k_cache_ptr = reinterpret_cast<std::uintptr_t>(
shared_k_cache.data_ptr<scalar_t>());
const auto v_cache_ptr = reinterpret_cast<std::uintptr_t>(
shared_v_cache.data_ptr<scalar_t>());
constexpr int32_t alignment_bytes = 16; // 128-bit alignment
CHECK(proj_k_ptr % alignment_bytes == 0)
<< "proj_k data_ptr must be 16-byte aligned";
CHECK(proj_v_ptr % alignment_bytes == 0)
<< "proj_v data_ptr must be 16-byte aligned";
CHECK(k_cache_ptr % alignment_bytes == 0)
<< "shared_k_cache data_ptr must be 16-byte aligned";
CHECK(v_cache_ptr % alignment_bytes == 0)
<< "shared_v_cache data_ptr must be 16-byte aligned";
const int64_t scalar_bytes = static_cast<int64_t>(sizeof(scalar_t));
CHECK((k_stride0 * scalar_bytes) % alignment_bytes == 0)
<< "proj_k stride(0) bytes must be 16-byte aligned";
CHECK((v_stride0 * scalar_bytes) % alignment_bytes == 0)
<< "proj_v stride(0) bytes must be 16-byte aligned";
CHECK((v_stride1 * scalar_bytes) % alignment_bytes == 0)
<< "proj_v stride(1) bytes must be 16-byte aligned";
prefill_reshape_and_cache_kernel<scalar_t>
<<<grid_dim, block_dim, 0, stream>>>(
proj_k.data_ptr<scalar_t>(),
proj_v.data_ptr<scalar_t>(),
shared_k_cache.data_ptr<scalar_t>(),
shared_v_cache.data_ptr<scalar_t>(),
shared_len,
kv_heads,
head_dim,
k_stride0,
v_stride0,
v_stride1);
});
C10_CUDA_KERNEL_LAUNCH_CHECK();
}
} // namespace xllm::kernel::cuda

View File

@@ -0,0 +1,63 @@
/* Copyright 2026 The xLLM Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://github.com/jd-opensource/xllm/blob/main/LICENSE
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#pragma once
#include <torch/torch.h>
#include <vector>
namespace xllm::kernel::cuda {
void decoder_reshape_and_cache(torch::Tensor proj_k,
torch::Tensor proj_v,
torch::Tensor unshared_k_cache,
torch::Tensor unshared_v_cache,
torch::Tensor step);
void cache_select(const torch::Tensor& beam_index,
std::vector<torch::Tensor>& unshared_k_cache,
std::vector<torch::Tensor>& unshared_v_cache,
const torch::Tensor& block_table,
int64_t decode_step,
int64_t beam_size,
int64_t layer_num);
void lse_combine(torch::Tensor output,
torch::Tensor shared_o,
torch::Tensor shared_lse,
torch::Tensor unshared_o,
torch::Tensor unshared_lse);
void prefill_reshape_and_cache(
torch::Tensor proj_k, // [shared_len, kv_heads, head_dim]
torch::Tensor proj_v, // [shared_len, kv_heads, head_dim]
torch::Tensor
shared_k_cache, // [num_shared_kv_seq_len, kv_heads, head_dim]
torch::Tensor shared_v_cache);
void beam_search(torch::Tensor acc_logprob,
torch::Tensor in_sequence_group,
torch::Tensor top_tokens,
torch::Tensor top_logprobs,
torch::Tensor out_acc_logprob,
torch::Tensor out_token_ids,
torch::Tensor out_token_index,
torch::Tensor out_beam_count_prefix_sums,
torch::Tensor out_sequence_group,
uint32_t batch_size,
uint32_t current_step);
} // namespace xllm::kernel::cuda