Update Wall-X to 1.1.0 (#104)
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
// Auto-generated by scripts/extract_cuda_kernels.py
|
||||
// This file registers CUDA kernel bindings via pybind11.
|
||||
//
|
||||
// Kernel .cu files are compiled separately so ninja can parallelize builds.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <torch/extension.h>
|
||||
|
||||
namespace wallx_cuda_get_rope_index {
|
||||
void GetRopeIndex(const c10::optional<at::Tensor>& input_ids, const c10::optional<at::Tensor>& image_grid_thw, const c10::optional<at::Tensor>& video_grid_thw, const c10::optional<at::Tensor>& second_per_grid_ts, const c10::optional<at::Tensor>& attention_mask, const at::Tensor& position_ids, const at::Tensor& mrope_deltas, const at::Tensor& workspace, int spatial_merge_size, int image_token_id, int video_token_id, int vision_start_token_id, float tokens_per_second);
|
||||
int64_t GetRopeIndexGetWorkSpace(const c10::optional<at::Tensor>& input_ids, const c10::optional<at::Tensor>& image_grid_thw, const c10::optional<at::Tensor>& video_grid_thw);
|
||||
} // namespace wallx_cuda_get_rope_index
|
||||
|
||||
namespace wallx_cuda_m_rope {
|
||||
void MRope(const at::Tensor& q, const at::Tensor& k, const at::Tensor& q_embed, const at::Tensor& k_embed, const at::Tensor& cos, const at::Tensor& sin, const int first, const int second );
|
||||
void MRopeInplace(const at::Tensor& q, const at::Tensor& k, const at::Tensor& cos, const at::Tensor& sin, const int first, const int second );
|
||||
void MRopeInplacePack(const at::Tensor& qkv, const at::Tensor& cos, const at::Tensor& sin, int64_t q_num_heads, int64_t kv_num_heads, const int first, const int second);
|
||||
void MRopeInplacePackBackward(const at::Tensor& dqkv, const at::Tensor& cos, const at::Tensor& sin, int64_t q_num_heads, int64_t kv_num_heads, const int first, const int second);
|
||||
void MRopeBackward( const at::Tensor& grad_q_embed, const at::Tensor& grad_k_embed, const at::Tensor& grad_q, const at::Tensor& grad_k, const at::Tensor& cos, const at::Tensor& sin, const int first, const int second );
|
||||
} // namespace wallx_cuda_m_rope
|
||||
|
||||
namespace wallx_cuda_permute {
|
||||
void MoePermuteTopKOp( const at::Tensor& input, const at::Tensor& indices, const at::Tensor& sorted_indices, const at::Tensor& row_id, const at::Tensor& sorted_row_id, const at::Tensor& temp_storage, const at::Tensor& permuted_output, const at::Tensor& row_id_map, int64_t num_out_tokens, int64_t max_expanded_token_num );
|
||||
void MoeRecoverTopKOp( const at::Tensor& input, const at::Tensor& row_id_map, const c10::optional<at::Tensor>& prob, const at::Tensor& unpermuted_output, int64_t num_tokens, int64_t num_topK );
|
||||
void MoeRecoverTopKBwdOp( const at::Tensor& input_bwd, const at::Tensor& input_fwd, const at::Tensor& row_id_map, const at::Tensor& prob, const at::Tensor& act_grad, const at::Tensor& prob_grad );
|
||||
size_t CubSortPairGetStorageBytes(int64_t num_items);
|
||||
} // namespace wallx_cuda_permute
|
||||
|
||||
namespace wallx_cuda_rope {
|
||||
void Rope(const at::Tensor& q, const at::Tensor& k, const at::Tensor& q_embed, const at::Tensor& k_embed, const at::Tensor& cos, const at::Tensor& sin, bool interleave );
|
||||
void RopeInplace(const at::Tensor& q, const at::Tensor& k, const at::Tensor& cos, const at::Tensor& sin, bool interleave );
|
||||
void RopeInplacePack(const at::Tensor& qkv, const at::Tensor& cos, const at::Tensor& sin, int64_t q_num_heads, int64_t kv_num_heads, bool interleave);
|
||||
void RopeInplacePackBackward(const at::Tensor& dqkv, const at::Tensor& cos, const at::Tensor& sin, int64_t q_num_heads, int64_t kv_num_heads, bool interleave);
|
||||
void RopeBackward( const at::Tensor& grad_q_embed, const at::Tensor& grad_k_embed, const at::Tensor& grad_q, const at::Tensor& grad_k, const at::Tensor& cos, const at::Tensor& sin, bool interleave );
|
||||
} // namespace wallx_cuda_rope
|
||||
|
||||
namespace wallx_cuda_rot_pos {
|
||||
void GetTokenCounts( const at::Tensor& grid_thw, const at::Tensor& token_counts, int spatial_merge_size );
|
||||
void RotPosEmb( const at::Tensor& inv_freq, const at::Tensor& grid_thw, const at::Tensor& output, const at::Tensor& cumsum_tokens, int spatial_merge_size );
|
||||
} // namespace wallx_cuda_rot_pos
|
||||
|
||||
namespace wallx_cuda_window_index {
|
||||
void GetWindowIndex( const at::Tensor& grid_thw, const at::Tensor& grid_info_tensor, const at::Tensor& window_indices, const at::Tensor& cu_window_seqlens, const at::Tensor& window_counts_tensor, int max_grid_t, int spatial_merge_size, int vit_merger_window_size, int patch_size, int spatial_merge_unit);
|
||||
void GetTotals( const at::Tensor& grid_thw, const at::Tensor& grid_info_tensor, const at::Tensor& global_totals_tensor, int spatial_merge_size, int vit_merger_window_size );
|
||||
} // namespace wallx_cuda_window_index
|
||||
|
||||
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
||||
m.def("rope", &wallx_cuda_rope::Rope);
|
||||
m.def("rope_inplace", &wallx_cuda_rope::RopeInplace);
|
||||
m.def("rope_inplace_pack", &wallx_cuda_rope::RopeInplacePack);
|
||||
m.def("rope_inplace_pack_bwd", &wallx_cuda_rope::RopeInplacePackBackward);
|
||||
m.def("rope_bwd", &wallx_cuda_rope::RopeBackward);
|
||||
m.def("m_rope", &wallx_cuda_m_rope::MRope);
|
||||
m.def("m_rope_inplace", &wallx_cuda_m_rope::MRopeInplace);
|
||||
m.def("m_rope_inplace_pack", &wallx_cuda_m_rope::MRopeInplacePack);
|
||||
m.def("m_rope_inplace_pack_bwd", &wallx_cuda_m_rope::MRopeInplacePackBackward);
|
||||
m.def("m_rope_bwd", &wallx_cuda_m_rope::MRopeBackward);
|
||||
m.def("get_token_counts", &wallx_cuda_rot_pos::GetTokenCounts);
|
||||
m.def("rot_pos", &wallx_cuda_rot_pos::RotPosEmb);
|
||||
m.def("get_rope_index", &wallx_cuda_get_rope_index::GetRopeIndex);
|
||||
m.def("get_rope_index_getworkspace", &wallx_cuda_get_rope_index::GetRopeIndexGetWorkSpace);
|
||||
m.def("get_window_index", &wallx_cuda_window_index::GetWindowIndex);
|
||||
m.def("get_totals", &wallx_cuda_window_index::GetTotals);
|
||||
m.def("permute", &wallx_cuda_permute::MoePermuteTopKOp);
|
||||
m.def("unpermute", &wallx_cuda_permute::MoeRecoverTopKOp);
|
||||
m.def("unpermute_bwd", &wallx_cuda_permute::MoeRecoverTopKBwdOp);
|
||||
m.def("cub_sort_pair_get_storage_bytes", &wallx_cuda_permute::CubSortPairGetStorageBytes);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace wallx_cuda {
|
||||
|
||||
enum class ActivationType {
|
||||
RELU,
|
||||
SILU,
|
||||
GELU
|
||||
};
|
||||
|
||||
} // namespace wallx_cuda
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cuda_fp16.h>
|
||||
#include "activation_types.h"
|
||||
|
||||
namespace wallx_cuda {
|
||||
|
||||
template<typename T, ActivationType activation_type>
|
||||
__forceinline__ __device__ T applyActivation(const T &x) {
|
||||
if constexpr (activation_type == ActivationType::RELU) {
|
||||
return x > (T)0.0f ? x : (T)0.0f;
|
||||
}
|
||||
else if constexpr (activation_type == ActivationType::SILU) {
|
||||
return (T)((float)x / (1.0f + __expf((float)-x)));
|
||||
}
|
||||
else if constexpr (activation_type == ActivationType::GELU) {
|
||||
// GELU implementation from vllm (gelu_new_kernel)
|
||||
const float x_f = (float)x;
|
||||
const float x3 = x_f * x_f * x_f;
|
||||
const float t = tanhf(0.79788456f * (x_f + 0.044715f * x3));
|
||||
return (T)(0.5f * x_f * (1.0f + t));
|
||||
}
|
||||
else {
|
||||
// No activation matches
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wallx_cuda
|
||||
@@ -0,0 +1,111 @@
|
||||
// Re-enable CUDA half operators (PyTorch disables them)
|
||||
#undef __CUDA_NO_HALF_OPERATORS__
|
||||
#undef __CUDA_NO_HALF_CONVERSIONS__
|
||||
#undef __CUDA_NO_HALF2_OPERATORS__
|
||||
#undef __CUDA_NO_BFLOAT16_CONVERSIONS__
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
#include <cuda_fp16.h>
|
||||
#include <cuda_bf16.h>
|
||||
|
||||
#define CUDA_CHECK(cmd) do { \
|
||||
cudaError_t result = cmd; \
|
||||
if (result != cudaSuccess) { \
|
||||
printf("[ERROR] CUDA error %s:%d '%s': (%d) %s\n", __FILE__, __LINE__, #cmd, (int)result, cudaGetErrorString(result)); \
|
||||
exit(-1); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
inline void syncAndCheck(const char* const file, int const line, bool force_check = false) {
|
||||
#ifdef DEBUG
|
||||
force_check = true;
|
||||
#endif
|
||||
if (force_check) {
|
||||
cudaDeviceSynchronize();
|
||||
cudaError_t result = cudaGetLastError();
|
||||
if (result) {
|
||||
throw std::runtime_error(std::string("[ST] CUDA runtime error: ") + cudaGetErrorString(result) + " "
|
||||
+ file + ":" + std::to_string(line) + " \n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define sync_check_cuda_error() syncAndCheck(__FILE__, __LINE__, false)
|
||||
#define sync_check_cuda_error_force() syncAndCheck(__FILE__, __LINE__, true)
|
||||
|
||||
// #ifdef DEBUG
|
||||
#define ASSERT_CHECK(__cond) \
|
||||
do { \
|
||||
const bool __cond_var = (__cond); \
|
||||
if (!__cond_var) { \
|
||||
::std::string __err_msg = \
|
||||
::std::string("`") + #__cond + \
|
||||
"` check failed at " + \
|
||||
__FILE__ + ":" + \
|
||||
::std::to_string(__LINE__); \
|
||||
throw std::runtime_error(__err_msg); \
|
||||
} \
|
||||
} while (0)
|
||||
// #else
|
||||
// #define ASSERT_CHECK(__cond) do { } while (0)
|
||||
// #endif
|
||||
|
||||
// Some stuff for indexing into an 1-D array
|
||||
#define INDEX_2D(dim1, dim2, index1, index2) \
|
||||
(((int64_t)index1) * (dim2) + (index2))
|
||||
#define INDEX_3D(dim1, dim2, dim3, index1, index2, index3) \
|
||||
(((int64_t)index1) * (dim2) * (dim3) + ((int64_t)index2) * (dim3) + (index3))
|
||||
#define INDEX_4D(dim1, dim2, dim3, dim4, index1, index2, index3, index4) \
|
||||
(((int64_t)index1) * (dim2) * (dim3) * (dim4) + ((int64_t)index2) * (dim3) * (dim4) + ((int64_t)index3) * (dim4) + (index4))
|
||||
#define INDEX_5D(dim1, dim2, dim3, dim4, dim5, index1, index2, index3, index4, index5) \
|
||||
(((int64_t)index1) * (dim2) * (dim3) * (dim4) * (dim5) + ((int64_t)index2) * (dim3) * (dim4) * (dim5) + ((int64_t)index3) * (dim4) * (dim5) + (index4) * (dim5) + (index5))
|
||||
|
||||
template<typename T>
|
||||
struct VecTraits;
|
||||
|
||||
template<>
|
||||
struct VecTraits<float> {
|
||||
using Type = float4;
|
||||
static constexpr int vec_size = 4;
|
||||
__device__ static inline Type load(const float* p) { return *reinterpret_cast<const float4*>(p); }
|
||||
__device__ static inline void store(float* p, const Type& v) { *reinterpret_cast<float4*>(p) = v; }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct VecTraits<__half> {
|
||||
using Type = __half2;
|
||||
static constexpr int vec_size = 2;
|
||||
__device__ static inline Type load(const __half* p) { return *reinterpret_cast<const __half2*>(p); }
|
||||
__device__ static inline void store(__half* p, const Type& v) { *reinterpret_cast<__half2*>(p) = v; }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct VecTraits<__nv_bfloat16> {
|
||||
using Type = __nv_bfloat162;
|
||||
static constexpr int vec_size = 2;
|
||||
__device__ static inline Type load(const __nv_bfloat16* p) { return *reinterpret_cast<const __nv_bfloat162*>(p); }
|
||||
__device__ static inline void store(__nv_bfloat16* p, const Type& v) { *reinterpret_cast<__nv_bfloat162*>(p) = v; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
cudaDataType_t getCudaDataType() {
|
||||
if (std::is_same<T, half>::value) {
|
||||
return CUDA_R_16F;
|
||||
}
|
||||
else if (std::is_same<T, __nv_bfloat16>::value) {
|
||||
return CUDA_R_16BF;
|
||||
}
|
||||
else if (std::is_same<T, float>::value) {
|
||||
return CUDA_R_32F;
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Cuda data type: Unsupported type");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,692 @@
|
||||
#undef __CUDA_NO_HALF_OPERATORS__
|
||||
#undef __CUDA_NO_HALF_CONVERSIONS__
|
||||
#undef __CUDA_NO_BFLOAT16_CONVERSIONS__
|
||||
#undef __CUDA_NO_HALF2_OPERATORS__
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
#include <cuda.h>
|
||||
#include <cub/cub.cuh>
|
||||
|
||||
#include "../common/cuda_utils.h"
|
||||
#include <torch/extension.h>
|
||||
#include <c10/cuda/CUDAStream.h>
|
||||
|
||||
namespace wallx_cuda_get_rope_index {
|
||||
|
||||
// Constants and macros
|
||||
#define MAX_SEQ_LEN 8192
|
||||
#define MAX_VISION_TOKENS 64
|
||||
#define WARP_SIZE 32
|
||||
#define MAX_THREADS_PER_BLOCK 1024
|
||||
|
||||
// Vision content descriptor
|
||||
struct VisionDescriptor
|
||||
{
|
||||
int64_t start_pos; // Starting position in the sequence
|
||||
int64_t token_pos; // Position of the vision token
|
||||
int64_t patch_count; // Number of patches
|
||||
int64_t grid_t, grid_h, grid_w; // Grid dimensions (T, H, W)
|
||||
float time_interval; // Time interval (for video)
|
||||
int64_t is_video; // 0 = image, 1 = video
|
||||
int64_t position_offset; // Positional encoding offset
|
||||
};
|
||||
|
||||
// Device function: fast integer division using reciprocal multiplication
|
||||
__device__ __forceinline__ int64_t fast_div(int64_t a, int64_t b)
|
||||
{
|
||||
return __float2ll_rd(__ll2float_rn(a) * __frcp_rn(__ll2float_rn(b)));
|
||||
}
|
||||
|
||||
// Device function: map 3D patch index to (t, h, w) coordinates
|
||||
__device__ __forceinline__ void get_3d_coords(int64_t patch_idx, int64_t H, int64_t W,
|
||||
int64_t &t, int64_t &h, int64_t &w)
|
||||
{
|
||||
int64_t hw = H * W;
|
||||
t = fast_div(patch_idx, hw);
|
||||
int64_t remaining = patch_idx - t * hw;
|
||||
h = fast_div(remaining, W);
|
||||
w = remaining - h * W;
|
||||
}
|
||||
|
||||
// Stage 1: Kernel to count image and video tokens per batch
|
||||
__global__ void compute_vision_counts(
|
||||
const int64_t *input_ids, // (batch_size, seq_len)
|
||||
const int64_t *attention_mask, // (batch_size, seq_len)
|
||||
int64_t *image_counts, // (batch_size,) - output: number of images per batch
|
||||
int64_t *video_counts, // (batch_size,) - output: number of videos per batch
|
||||
const int64_t batch_size,
|
||||
const int64_t seq_len,
|
||||
const int64_t image_token_id,
|
||||
const int64_t video_token_id,
|
||||
const int64_t vision_start_token_id)
|
||||
{
|
||||
int64_t batch_idx = blockIdx.x;
|
||||
int64_t thread_idx = threadIdx.x;
|
||||
|
||||
if (batch_idx >= batch_size)
|
||||
return;
|
||||
|
||||
__shared__ int64_t shared_image_counts[MAX_THREADS_PER_BLOCK];
|
||||
__shared__ int64_t shared_video_counts[MAX_THREADS_PER_BLOCK];
|
||||
|
||||
// Initialize per-thread counters
|
||||
int64_t thread_image_count = 0;
|
||||
int64_t thread_video_count = 0;
|
||||
|
||||
// Parallel scan over sequence tokens (each thread processes multiple tokens)
|
||||
for (int64_t i = thread_idx; i < seq_len - 1; i += blockDim.x)
|
||||
{
|
||||
// Skip if masked out
|
||||
if ((attention_mask != nullptr) && attention_mask[batch_idx * seq_len + i] == 0)
|
||||
continue;
|
||||
|
||||
int64_t token_id = input_ids[batch_idx * seq_len + i];
|
||||
|
||||
// Check if current token is a vision start token followed by image/video token
|
||||
if (token_id == vision_start_token_id && i + 1 < seq_len)
|
||||
{
|
||||
int64_t next_token = input_ids[batch_idx * seq_len + i + 1];
|
||||
|
||||
if (next_token == image_token_id)
|
||||
{
|
||||
thread_image_count++;
|
||||
}
|
||||
else if (next_token == video_token_id)
|
||||
{
|
||||
thread_video_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store per-thread counts in shared memory
|
||||
shared_image_counts[thread_idx] = thread_image_count;
|
||||
shared_video_counts[thread_idx] = thread_video_count;
|
||||
__syncthreads();
|
||||
|
||||
// Parallel reduction to sum counts across threads
|
||||
for (int64_t stride = blockDim.x / 2; stride > 0; stride /= 2)
|
||||
{
|
||||
if (thread_idx < stride)
|
||||
{
|
||||
shared_image_counts[thread_idx] += shared_image_counts[thread_idx + stride];
|
||||
shared_video_counts[thread_idx] += shared_video_counts[thread_idx + stride];
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// Write final result for this batch to global memory
|
||||
if (thread_idx == 0)
|
||||
{
|
||||
image_counts[batch_idx] = shared_image_counts[0];
|
||||
video_counts[batch_idx] = shared_video_counts[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Stage 2: Preprocessing kernel - parse and analyze all vision content
|
||||
__global__ void preprocess_vision_tokens(
|
||||
const int64_t *input_ids, // (batch_size, seq_len)
|
||||
const int64_t *attention_mask, // (batch_size, seq_len)
|
||||
const int64_t *image_grid_thw, // (max_images, 3)
|
||||
const int64_t *video_grid_thw, // (max_videos, 3)
|
||||
const float *second_per_grid_ts, // (max_videos,)
|
||||
const int64_t *image_counts, // (batch_size,)
|
||||
const int64_t *video_counts, // (batch_size,)
|
||||
VisionDescriptor *vision_desc, // (batch_size, MAX_VISION_TOKENS)
|
||||
int64_t *vision_counts, // (batch_size,) - total vision tokens per batch
|
||||
int64_t *text_lengths, // (batch_size, MAX_VISION_TOKENS+1) - lengths of text segments
|
||||
int64_t *position_offsets, // (batch_size, MAX_VISION_TOKENS+1) - cumulative position offsets
|
||||
const int64_t batch_size,
|
||||
const int64_t seq_len,
|
||||
const int64_t spatial_merge_size,
|
||||
const int64_t image_token_id,
|
||||
const int64_t video_token_id,
|
||||
const int64_t vision_start_token_id,
|
||||
const float tokens_per_second)
|
||||
{
|
||||
// Each thread processes one batch sequence
|
||||
int64_t batch_idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (batch_idx >= batch_size)
|
||||
return;
|
||||
|
||||
// Compute cumulative image/video indices before this batch
|
||||
int64_t image_idx = 0, video_idx = 0;
|
||||
for (int64_t i = 0; i < batch_idx; i++)
|
||||
{
|
||||
image_idx += image_counts[i];
|
||||
video_idx += video_counts[i];
|
||||
}
|
||||
|
||||
// Local state
|
||||
int64_t vision_count = 0;
|
||||
int64_t current_pos = 0;
|
||||
int64_t position_offset = 0;
|
||||
|
||||
// Sequential scan over the sequence (single-threaded per batch)
|
||||
for (int64_t i = 0; i < seq_len - 1; i++)
|
||||
{
|
||||
// Skip masked tokens
|
||||
if ((attention_mask != nullptr) && attention_mask[batch_idx * seq_len + i] == 0)
|
||||
continue;
|
||||
|
||||
int64_t token_id = input_ids[batch_idx * seq_len + i];
|
||||
|
||||
// Check for vision start token
|
||||
if (token_id == vision_start_token_id)
|
||||
{
|
||||
int64_t next_token = input_ids[batch_idx * seq_len + i + 1];
|
||||
|
||||
if (next_token == image_token_id || next_token == video_token_id)
|
||||
{
|
||||
// Record length of preceding text segment
|
||||
int64_t vision_pos = i + 1;
|
||||
text_lengths[batch_idx * (MAX_VISION_TOKENS + 1) + vision_count] = vision_pos - current_pos;
|
||||
position_offsets[batch_idx * (MAX_VISION_TOKENS + 1) + vision_count] = position_offset;
|
||||
position_offset += (vision_pos - current_pos);
|
||||
|
||||
// Fetch grid dimensions
|
||||
int64_t T, H, W;
|
||||
float time_interval = 0.0f;
|
||||
int64_t is_video = (next_token == video_token_id) ? 1 : 0;
|
||||
|
||||
if (is_video == 0)
|
||||
{
|
||||
// Image case
|
||||
T = image_grid_thw[image_idx * 3 + 0];
|
||||
H = image_grid_thw[image_idx * 3 + 1];
|
||||
W = image_grid_thw[image_idx * 3 + 2];
|
||||
image_idx++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Video case
|
||||
T = video_grid_thw[video_idx * 3 + 0];
|
||||
H = video_grid_thw[video_idx * 3 + 1];
|
||||
W = video_grid_thw[video_idx * 3 + 2];
|
||||
time_interval = second_per_grid_ts[video_idx];
|
||||
video_idx++;
|
||||
}
|
||||
|
||||
// Compute number of patches after spatial merging
|
||||
int64_t H_merged = H / spatial_merge_size;
|
||||
int64_t W_merged = W / spatial_merge_size;
|
||||
int64_t patch_count = T * H_merged * W_merged;
|
||||
|
||||
// Populate vision descriptor
|
||||
if (vision_count < MAX_VISION_TOKENS)
|
||||
{
|
||||
VisionDescriptor &desc = vision_desc[batch_idx * MAX_VISION_TOKENS + vision_count];
|
||||
desc.start_pos = current_pos;
|
||||
desc.token_pos = vision_pos;
|
||||
desc.patch_count = patch_count;
|
||||
desc.grid_t = T;
|
||||
desc.grid_h = H_merged;
|
||||
desc.grid_w = W_merged;
|
||||
desc.time_interval = time_interval;
|
||||
desc.is_video = is_video;
|
||||
desc.position_offset = position_offset;
|
||||
|
||||
// Update position offset based on content type
|
||||
if (is_video)
|
||||
{
|
||||
position_offset += max(static_cast<int64_t>((T - 1) * time_interval * tokens_per_second) + 1,
|
||||
static_cast<int64_t>(max(H_merged, W_merged)));
|
||||
}
|
||||
else
|
||||
{
|
||||
position_offset += max(H_merged, W_merged);
|
||||
}
|
||||
|
||||
current_pos = vision_pos + patch_count;
|
||||
vision_count++;
|
||||
}
|
||||
|
||||
// Skip over the vision patch tokens
|
||||
i = vision_pos + patch_count - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store total vision token count for this batch
|
||||
vision_counts[batch_idx] = vision_count;
|
||||
|
||||
// Handle final text segment after last vision token
|
||||
int64_t effective_len = seq_len;
|
||||
if (attention_mask != nullptr)
|
||||
{
|
||||
// Find last valid (unmasked) token
|
||||
for (int64_t i = seq_len - 1; i >= 0; i--)
|
||||
{
|
||||
if (attention_mask[batch_idx * seq_len + i] != 0)
|
||||
{
|
||||
effective_len = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text_lengths[batch_idx * (MAX_VISION_TOKENS + 1) + vision_count] = effective_len - current_pos;
|
||||
position_offsets[batch_idx * (MAX_VISION_TOKENS + 1) + vision_count] = position_offset;
|
||||
}
|
||||
|
||||
|
||||
// Stage 3: Main kernel - compute 3D position IDs for all tokens in parallel
|
||||
__global__ void compute_3d_positions(
|
||||
const int64_t *input_ids, // (batch_size, seq_len)
|
||||
const int64_t *attention_mask, // (batch_size, seq_len)
|
||||
const VisionDescriptor *vision_desc, // (batch_size, MAX_VISION_TOKENS)
|
||||
const int64_t *vision_counts, // (batch_size,)
|
||||
const int64_t *text_lengths, // (batch_size, MAX_VISION_TOKENS+1)
|
||||
const int64_t *position_offsets, // (batch_size, MAX_VISION_TOKENS+1)
|
||||
int64_t *position_ids, // (3, batch_size, seq_len) - output 3D position IDs
|
||||
int64_t *mrope_deltas, // (batch_size,) - RoPE length adjustment
|
||||
const int64_t batch_size,
|
||||
const int64_t seq_len,
|
||||
const float tokens_per_second)
|
||||
{
|
||||
// Grid: (batch_size), Block: (threads_per_block)
|
||||
int64_t batch_idx = blockIdx.x;
|
||||
int64_t thread_idx = threadIdx.x;
|
||||
|
||||
if (batch_idx >= batch_size)
|
||||
return;
|
||||
|
||||
__shared__ VisionDescriptor shared_visions[MAX_VISION_TOKENS];
|
||||
__shared__ int64_t shared_position_offsets[MAX_VISION_TOKENS + 1];
|
||||
__shared__ int64_t shared_max_positions[MAX_THREADS_PER_BLOCK]; // For block-wide reduction
|
||||
|
||||
int64_t shared_vision_count = vision_counts[batch_idx];
|
||||
|
||||
// Cooperative loading of vision descriptors into shared memory
|
||||
for (int64_t i = thread_idx; i < MAX_VISION_TOKENS; i += blockDim.x)
|
||||
{
|
||||
if (i < shared_vision_count)
|
||||
{
|
||||
shared_visions[i] = vision_desc[batch_idx * MAX_VISION_TOKENS + i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int64_t i = thread_idx; i < MAX_VISION_TOKENS + 1; i += blockDim.x)
|
||||
{
|
||||
shared_position_offsets[i] = position_offsets[batch_idx * (MAX_VISION_TOKENS + 1) + i];
|
||||
}
|
||||
|
||||
// Initialize per-thread max position
|
||||
int64_t thread_max_position = -1;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Parallel processing: each thread handles multiple tokens
|
||||
for (int64_t token_idx = thread_idx; token_idx < seq_len; token_idx += blockDim.x)
|
||||
{
|
||||
// Check validity via attention mask
|
||||
int mask_offset = 0;
|
||||
bool is_valid_token = true;
|
||||
if (attention_mask != nullptr)
|
||||
{
|
||||
is_valid_token = attention_mask[batch_idx * seq_len + token_idx] != 0;
|
||||
for (int i = 0; i < token_idx; i++) {
|
||||
if (attention_mask[batch_idx * seq_len + i] == 0) {
|
||||
mask_offset += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_valid_token)
|
||||
{
|
||||
// Set masked tokens to default position (1)
|
||||
position_ids[0 * batch_size * seq_len + batch_idx * seq_len + token_idx] = 1;
|
||||
position_ids[1 * batch_size * seq_len + batch_idx * seq_len + token_idx] = 1;
|
||||
position_ids[2 * batch_size * seq_len + batch_idx * seq_len + token_idx] = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine which segment this token belongs to
|
||||
int64_t segment_idx = -9999999;
|
||||
int64_t local_pos = token_idx;
|
||||
|
||||
// Linear search (vision count is small, so this is efficient)
|
||||
for (int64_t v = 0; v < shared_vision_count; v++)
|
||||
{
|
||||
if (token_idx < shared_visions[v].token_pos)
|
||||
{
|
||||
// Token belongs to text segment before vision v
|
||||
segment_idx = v;
|
||||
local_pos = token_idx - (v > 0 ? shared_visions[v - 1].token_pos + shared_visions[v - 1].patch_count : 0);
|
||||
break;
|
||||
}
|
||||
else if (token_idx < shared_visions[v].token_pos + shared_visions[v].patch_count)
|
||||
{
|
||||
// Token belongs to vision patch v
|
||||
segment_idx = -(v + 1); // Negative index indicates vision
|
||||
local_pos = token_idx - shared_visions[v].token_pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (segment_idx == -9999999)
|
||||
{
|
||||
// Token belongs to final text segment
|
||||
segment_idx = shared_vision_count;
|
||||
int64_t last_vision_end = 0;
|
||||
if (shared_vision_count > 0)
|
||||
{
|
||||
last_vision_end = shared_visions[shared_vision_count - 1].token_pos +
|
||||
shared_visions[shared_vision_count - 1].patch_count;
|
||||
}
|
||||
local_pos = token_idx - last_vision_end;
|
||||
}
|
||||
|
||||
int64_t pos_t, pos_h, pos_w;
|
||||
|
||||
if (segment_idx >= 0)
|
||||
{
|
||||
// Text token: use 1D position encoding
|
||||
int64_t offset = shared_position_offsets[segment_idx];
|
||||
pos_t = pos_h = pos_w = offset + local_pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Vision patch: compute 3D position encoding
|
||||
int64_t vision_idx = -(segment_idx + 1);
|
||||
const VisionDescriptor &desc = shared_visions[vision_idx];
|
||||
|
||||
// Map linear patch index to (t, h, w)
|
||||
int64_t t, h, w;
|
||||
get_3d_coords(local_pos, desc.grid_h, desc.grid_w, t, h, w);
|
||||
|
||||
// Compute 3D positions with temporal scaling for video
|
||||
pos_t = static_cast<int64_t>(t * desc.time_interval * tokens_per_second) + desc.position_offset;
|
||||
pos_h = h + desc.position_offset;
|
||||
pos_w = w + desc.position_offset;
|
||||
}
|
||||
|
||||
// Write 3D position IDs
|
||||
position_ids[0 * batch_size * seq_len + batch_idx * seq_len + token_idx] = pos_t - mask_offset;
|
||||
position_ids[1 * batch_size * seq_len + batch_idx * seq_len + token_idx] = pos_h - mask_offset;
|
||||
position_ids[2 * batch_size * seq_len + batch_idx * seq_len + token_idx] = pos_w - mask_offset;
|
||||
|
||||
// Track per-thread maximum position
|
||||
int64_t max_pos = max(pos_t - mask_offset, max(pos_h - mask_offset, pos_w - mask_offset));
|
||||
thread_max_position = max(thread_max_position, max_pos);
|
||||
}
|
||||
|
||||
// Store per-thread max into shared memory
|
||||
shared_max_positions[thread_idx] = thread_max_position;
|
||||
__syncthreads();
|
||||
|
||||
// Block-wide reduction to find global max position
|
||||
for (int64_t stride = blockDim.x / 2; stride > 0; stride /= 2)
|
||||
{
|
||||
if (thread_idx < stride)
|
||||
{
|
||||
shared_max_positions[thread_idx] = max(shared_max_positions[thread_idx],
|
||||
shared_max_positions[thread_idx + stride]);
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// Compute mRoPE delta: (max_pos + 1) - seq_len
|
||||
if (thread_idx == 0)
|
||||
{
|
||||
int64_t global_max_position = shared_max_positions[0];
|
||||
mrope_deltas[batch_idx] = global_max_position + 1 - seq_len;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback kernel when no vision tokens exist: assign sequential positions based on attention mask
|
||||
__global__ void compute_3d_positions_mask_text(
|
||||
const int64_t* __restrict__ attention_mask, // (batch_size, seq_len)
|
||||
int64_t* __restrict__ position_ids, // (3, batch_size, seq_len) - flattened row-major
|
||||
int64_t* __restrict__ mrope_deltas, // (batch_size,)
|
||||
const int64_t batch_size,
|
||||
const int64_t seq_len
|
||||
) {
|
||||
// One block per batch
|
||||
int batch_idx = blockIdx.x;
|
||||
if (batch_idx >= batch_size) return;
|
||||
|
||||
extern __shared__ int64_t shared_mask[];
|
||||
int64_t* mask = shared_mask;
|
||||
|
||||
// Load attention mask for current batch into shared memory
|
||||
for (int i = threadIdx.x; i < seq_len; i += blockDim.x) {
|
||||
mask[i] = attention_mask[batch_idx * seq_len + i];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
int tid = threadIdx.x;
|
||||
int64_t base_offset = batch_idx * seq_len;
|
||||
int64_t total_valid = 0;
|
||||
|
||||
// Compute prefix sum (cumulative count of valid tokens)
|
||||
// Simple O(seq_len^2) per-thread loop - acceptable for moderate seq_len
|
||||
for (int idx = tid; idx < seq_len; idx += blockDim.x) {
|
||||
int64_t sum = 0;
|
||||
for (int i = 0; i <= idx; ++i) {
|
||||
sum += mask[i];
|
||||
}
|
||||
int64_t pos_val = (mask[idx] ? (sum - 1) : 1); // Use 0-based if valid, else 1
|
||||
|
||||
// Write same position to all three dimensions
|
||||
int64_t flat_idx0 = 0 * batch_size * seq_len + base_offset + idx;
|
||||
int64_t flat_idx1 = 1 * batch_size * seq_len + base_offset + idx;
|
||||
int64_t flat_idx2 = 2 * batch_size * seq_len + base_offset + idx;
|
||||
|
||||
position_ids[flat_idx0] = pos_val;
|
||||
position_ids[flat_idx1] = pos_val;
|
||||
position_ids[flat_idx2] = pos_val;
|
||||
|
||||
// Only thread 0 tracks total valid tokens
|
||||
if (tid == 0) {
|
||||
total_valid = sum; // After last idx, sum = total number of 1s
|
||||
}
|
||||
}
|
||||
|
||||
// Compute mrope_deltas: (total_valid) - seq_len
|
||||
if (tid == 0) {
|
||||
// Re-compute total_valid robustly
|
||||
total_valid = 0;
|
||||
for (int i = 0; i < seq_len; ++i) {
|
||||
total_valid += mask[i];
|
||||
}
|
||||
mrope_deltas[batch_idx] = total_valid - seq_len;
|
||||
}
|
||||
}
|
||||
|
||||
// Kernel to fill position_ids with arange(seq_len) in 3D layout when no vision or mask
|
||||
__global__ void arange_3d(int64_t seq_len,
|
||||
int64_t* out) {
|
||||
int i = blockIdx.x; // dimension index: 0, 1, 2
|
||||
int j = blockIdx.y; // batch index
|
||||
int64_t k = blockIdx.z * blockDim.x + threadIdx.x; // token index
|
||||
|
||||
if (k >= seq_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Flatten index: (dim, batch, token) -> linear
|
||||
int64_t idx = i * gridDim.y * seq_len + j * seq_len + k;
|
||||
out[idx] = k;
|
||||
}
|
||||
|
||||
// Kernel to zero-initialize a 1D tensor
|
||||
__global__ void fill_zeros_1d(int64_t numel, int64_t* data) {
|
||||
int64_t idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (idx < numel) {
|
||||
data[idx] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute required workspace size (in bytes)
|
||||
int64_t GetRopeIndexGetWorkSpace(const c10::optional<at::Tensor>& input_ids,
|
||||
const c10::optional<at::Tensor>& image_grid_thw,
|
||||
const c10::optional<at::Tensor>& video_grid_thw) {
|
||||
|
||||
if (!image_grid_thw.has_value() && !video_grid_thw.has_value()) {
|
||||
return 1; // minimal workspace
|
||||
} else {
|
||||
const at::Tensor& input_ids_tensor = input_ids.value();
|
||||
const int batch_size = input_ids_tensor.size(0);
|
||||
return (batch_size * MAX_VISION_TOKENS * static_cast<int64_t>(sizeof(VisionDescriptor)) +
|
||||
3 * batch_size * sizeof(int64_t) +
|
||||
2 * batch_size * (MAX_VISION_TOKENS + 1) * sizeof(int64_t));
|
||||
}
|
||||
}
|
||||
|
||||
// Main entry function: compute RoPE position IDs and mRoPE deltas
|
||||
void GetRopeIndex(const c10::optional<at::Tensor>& input_ids,
|
||||
const c10::optional<at::Tensor>& image_grid_thw,
|
||||
const c10::optional<at::Tensor>& video_grid_thw,
|
||||
const c10::optional<at::Tensor>& second_per_grid_ts,
|
||||
const c10::optional<at::Tensor>& attention_mask,
|
||||
const at::Tensor& position_ids,
|
||||
const at::Tensor& mrope_deltas,
|
||||
const at::Tensor& workspace,
|
||||
int spatial_merge_size,
|
||||
int image_token_id,
|
||||
int video_token_id,
|
||||
int vision_start_token_id,
|
||||
float tokens_per_second) {
|
||||
|
||||
ASSERT_CHECK(input_ids.has_value());
|
||||
|
||||
const at::Tensor& input_ids_tensor = input_ids.value();
|
||||
ASSERT_CHECK(input_ids_tensor.dim() == 2);
|
||||
ASSERT_CHECK(input_ids_tensor.is_contiguous());
|
||||
|
||||
const auto batch_size = input_ids_tensor.size(0);
|
||||
const auto seq_len = input_ids_tensor.size(1);
|
||||
const auto device = input_ids_tensor.device();
|
||||
|
||||
const int64_t *input_ids_ptr = static_cast<const int64_t*>(input_ids_tensor.data_ptr());
|
||||
|
||||
const int64_t *attention_mask_ptr = nullptr;
|
||||
|
||||
if (attention_mask.has_value()) {
|
||||
const at::Tensor& attention_mask_tensor = attention_mask.value();
|
||||
ASSERT_CHECK(attention_mask_tensor.is_contiguous());
|
||||
attention_mask_ptr = static_cast<const int64_t*>(attention_mask_tensor.data_ptr());
|
||||
}
|
||||
|
||||
const int64_t *image_grid_thw_ptr = nullptr;
|
||||
|
||||
if (image_grid_thw.has_value()) {
|
||||
const at::Tensor& image_grid_thw_tensor = image_grid_thw.value();
|
||||
ASSERT_CHECK(image_grid_thw_tensor.dim() == 2 && image_grid_thw_tensor.size(1) == 3);
|
||||
ASSERT_CHECK(image_grid_thw_tensor.is_contiguous());
|
||||
image_grid_thw_ptr = static_cast<const int64_t*>(image_grid_thw_tensor.data_ptr());
|
||||
}
|
||||
|
||||
const int64_t *video_grid_thw_ptr = nullptr;
|
||||
|
||||
if (video_grid_thw.has_value()) {
|
||||
const at::Tensor& video_grid_thw_tensor = video_grid_thw.value();
|
||||
ASSERT_CHECK(video_grid_thw_tensor.is_contiguous());
|
||||
ASSERT_CHECK(video_grid_thw_tensor.dim() == 2 && video_grid_thw_tensor.size(1) == 3);
|
||||
video_grid_thw_ptr = static_cast<const int64_t*>(video_grid_thw_tensor.data_ptr());
|
||||
}
|
||||
|
||||
const float *second_per_grid_ts_ptr = nullptr;
|
||||
|
||||
if (second_per_grid_ts.has_value()) {
|
||||
const at::Tensor& second_per_grid_ts_tensor = second_per_grid_ts.value();
|
||||
ASSERT_CHECK(second_per_grid_ts_tensor.is_contiguous());
|
||||
ASSERT_CHECK(second_per_grid_ts_tensor.dim() == 1);
|
||||
second_per_grid_ts_ptr = static_cast<const float*>(second_per_grid_ts_tensor.data_ptr());
|
||||
}
|
||||
|
||||
cudaStream_t stream = static_cast<cudaStream_t>(
|
||||
at::cuda::getCurrentCUDAStream().stream());
|
||||
|
||||
// Case: no vision tokens -> use simple positional encoding
|
||||
if (!image_grid_thw.has_value() && !video_grid_thw.has_value()) {
|
||||
if (attention_mask.has_value()) {
|
||||
// Use attention mask to compute valid positions
|
||||
int block_size = min(1024, (int)seq_len);
|
||||
dim3 grid(batch_size);
|
||||
dim3 block(block_size);
|
||||
size_t shared_mem_size = seq_len * sizeof(int64_t);
|
||||
|
||||
compute_3d_positions_mask_text<<<grid, block, shared_mem_size>>>(
|
||||
attention_mask_ptr, static_cast<int64_t*>(position_ids.data_ptr()), static_cast<int64_t*>(mrope_deltas.data_ptr()), batch_size, seq_len
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
// No mask, no vision -> use simple arange
|
||||
dim3 thread_num(256);
|
||||
dim3 grid(3, batch_size, (seq_len + 255) / 256);
|
||||
arange_3d<<<grid, thread_num, 0, stream>>>(seq_len, static_cast<int64_t*>(position_ids.data_ptr()));
|
||||
|
||||
int block_size = 256;
|
||||
int grid_size = (batch_size + block_size - 1) / block_size;
|
||||
fill_zeros_1d<<<grid_size, block_size, 0, stream>>>(batch_size, static_cast<int64_t*>(mrope_deltas.data_ptr()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate workspace buffers
|
||||
void* work_ptr = workspace.data_ptr();
|
||||
VisionDescriptor *d_vision_desc = reinterpret_cast<VisionDescriptor*>(work_ptr);
|
||||
|
||||
int64_t *d_vision_counts = reinterpret_cast<int64_t*>(reinterpret_cast<VisionDescriptor*>(work_ptr) + batch_size * MAX_VISION_TOKENS);
|
||||
int64_t *d_text_lengths = d_vision_counts + batch_size;
|
||||
int64_t *d_position_offsets = d_text_lengths + batch_size * (MAX_VISION_TOKENS + 1);
|
||||
int64_t *d_image_counts = d_position_offsets + batch_size * (MAX_VISION_TOKENS + 1);
|
||||
int64_t *d_video_counts = d_image_counts + batch_size;
|
||||
|
||||
// Stage 1: count vision tokens
|
||||
dim3 index_grid(static_cast<unsigned int>(batch_size));
|
||||
dim3 index_block(256);
|
||||
|
||||
compute_vision_counts<<<index_grid, index_block, 0, stream>>>(
|
||||
input_ids_ptr, attention_mask_ptr,
|
||||
d_image_counts, d_video_counts,
|
||||
batch_size, seq_len, image_token_id, video_token_id, vision_start_token_id);
|
||||
|
||||
// Stage 2: preprocess vision tokens
|
||||
int64_t threads_per_block = std::min(static_cast<int64_t>(batch_size), static_cast<int64_t>(256));
|
||||
int64_t num_blocks = (batch_size + threads_per_block - 1) / threads_per_block;
|
||||
|
||||
dim3 preprocess_grid(static_cast<unsigned int>(num_blocks));
|
||||
dim3 preprocess_block(static_cast<unsigned int>(threads_per_block));
|
||||
|
||||
preprocess_vision_tokens<<<preprocess_grid, preprocess_block, 0, stream>>>(
|
||||
input_ids_ptr, attention_mask_ptr, image_grid_thw_ptr, video_grid_thw_ptr,
|
||||
second_per_grid_ts_ptr, d_image_counts, d_video_counts,
|
||||
d_vision_desc, d_vision_counts, d_text_lengths, d_position_offsets,
|
||||
batch_size, seq_len, spatial_merge_size,
|
||||
image_token_id, video_token_id, vision_start_token_id, tokens_per_second);
|
||||
|
||||
// Stage 3: compute 3D positions
|
||||
threads_per_block = std::min(static_cast<int64_t>(seq_len), static_cast<int64_t>(MAX_THREADS_PER_BLOCK));
|
||||
if (threads_per_block < 32) threads_per_block = 32;
|
||||
|
||||
int64_t power_of_2 = 1;
|
||||
while (power_of_2 < threads_per_block) power_of_2 *= 2;
|
||||
if (power_of_2 > MAX_THREADS_PER_BLOCK) power_of_2 = MAX_THREADS_PER_BLOCK;
|
||||
threads_per_block = power_of_2;
|
||||
|
||||
dim3 compute_grid(static_cast<unsigned int>(batch_size));
|
||||
dim3 compute_block(static_cast<unsigned int>(threads_per_block));
|
||||
|
||||
compute_3d_positions<<<compute_grid, compute_block, 0, stream>>>(
|
||||
input_ids_ptr, attention_mask_ptr, d_vision_desc, d_vision_counts,
|
||||
d_text_lengths, d_position_offsets,
|
||||
static_cast<int64_t*>(position_ids.data_ptr()), static_cast<int64_t*>(mrope_deltas.data_ptr()),
|
||||
batch_size, seq_len, tokens_per_second);
|
||||
|
||||
sync_check_cuda_error();
|
||||
}
|
||||
|
||||
} // wallx_cuda_get_rope_index
|
||||
|
||||
// TVM FFI exports
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
@@ -0,0 +1,840 @@
|
||||
// Re-enable CUDA half operators (PyTorch disables them)
|
||||
#undef __CUDA_NO_HALF_OPERATORS__
|
||||
#undef __CUDA_NO_HALF_CONVERSIONS__
|
||||
#undef __CUDA_NO_HALF2_OPERATORS__
|
||||
#undef __CUDA_NO_BFLOAT16_CONVERSIONS__
|
||||
|
||||
#include <torch/extension.h>
|
||||
#include <c10/cuda/CUDAStream.h>
|
||||
|
||||
#include <cuda_fp16.h>
|
||||
#include <cassert>
|
||||
#include <cuda_runtime.h>
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
#include "../common/cuda_utils.h"
|
||||
|
||||
namespace wallx_cuda_m_rope {
|
||||
|
||||
// Helper: convert vector of T to float4
|
||||
template<typename T>
|
||||
__device__ __forceinline__ float4 to_float4(const T* ptr) {
|
||||
if constexpr (std::is_same_v<T, half>) {
|
||||
half2 h2_0 = reinterpret_cast<const half2*>(ptr)[0];
|
||||
half2 h2_1 = reinterpret_cast<const half2*>(ptr)[1];
|
||||
float2 f2_0 = __half22float2(h2_0);
|
||||
float2 f2_1 = __half22float2(h2_1);
|
||||
return make_float4(f2_0.x, f2_0.y, f2_1.x, f2_1.y);
|
||||
} else if constexpr (std::is_same_v<T, __nv_bfloat16>) {
|
||||
__nv_bfloat162 b2_0 = reinterpret_cast<const __nv_bfloat162*>(ptr)[0];
|
||||
__nv_bfloat162 b2_1 = reinterpret_cast<const __nv_bfloat162*>(ptr)[1];
|
||||
#if __CUDA_ARCH__ >= 800
|
||||
float2 f2_0 = __bfloat1622float2(b2_0);
|
||||
float2 f2_1 = __bfloat1622float2(b2_1);
|
||||
return make_float4(f2_0.x, f2_0.y, f2_1.x, f2_1.y);
|
||||
#else
|
||||
assert(false && "Unsupported on arch < 800");
|
||||
#endif
|
||||
} else if constexpr (std::is_same_v<T, float>) {
|
||||
return reinterpret_cast<const float4*>(ptr)[0];
|
||||
} else {
|
||||
static_assert(sizeof(T) == 0, "Unsupported type");
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: convert float4 back to vector of T
|
||||
template<typename T>
|
||||
__device__ __forceinline__ void from_float4(T* ptr, const float4& f4) {
|
||||
if constexpr (std::is_same_v<T, half>) {
|
||||
half2 h2_0 = __float22half2_rn(make_float2(f4.x, f4.y));
|
||||
half2 h2_1 = __float22half2_rn(make_float2(f4.z, f4.w));
|
||||
reinterpret_cast<half2*>(ptr)[0] = h2_0;
|
||||
reinterpret_cast<half2*>(ptr)[1] = h2_1;
|
||||
} else if constexpr (std::is_same_v<T, __nv_bfloat16>) {
|
||||
#if __CUDA_ARCH__ >= 800
|
||||
__nv_bfloat162 b2_0 = __float22bfloat162_rn(make_float2(f4.x, f4.y));
|
||||
__nv_bfloat162 b2_1 = __float22bfloat162_rn(make_float2(f4.z, f4.w));
|
||||
reinterpret_cast<__nv_bfloat162*>(ptr)[0] = b2_0;
|
||||
reinterpret_cast<__nv_bfloat162*>(ptr)[1] = b2_1;
|
||||
#else
|
||||
assert(false && "Unsupported on arch < 800");
|
||||
#endif
|
||||
} else if constexpr (std::is_same_v<T, float>) {
|
||||
reinterpret_cast<float4*>(ptr)[0] = f4;
|
||||
} else {
|
||||
static_assert(sizeof(T) == 0, "Unsupported type");
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
__global__ void MRopeKernel(const float* cos,
|
||||
const float* sin,
|
||||
const T* q,
|
||||
const int q_h,
|
||||
const T* k,
|
||||
const int k_h,
|
||||
T* q_embed,
|
||||
T* k_embed,
|
||||
const int first,
|
||||
const int second,
|
||||
const int d,
|
||||
const int qb_stride,
|
||||
const int qs_stride,
|
||||
const int qh_stride,
|
||||
const int kb_stride,
|
||||
const int ks_stride,
|
||||
const int kh_stride,
|
||||
const int qeb_stride,
|
||||
const int qes_stride,
|
||||
const int qeh_stride,
|
||||
const int keb_stride,
|
||||
const int kes_stride,
|
||||
const int keh_stride) {
|
||||
extern __shared__ char cos_sin[];
|
||||
const int half_dim = d / 2;
|
||||
float* cos_smem = reinterpret_cast<float*>(cos_sin);
|
||||
float* sin_smem = cos_smem + half_dim;
|
||||
int b = blockIdx.x;
|
||||
int s = blockIdx.y;
|
||||
|
||||
int64_t offset = gridDim.x * gridDim.y * half_dim;
|
||||
int64_t cos_sin_b_stride = gridDim.y * half_dim;
|
||||
int64_t cos_sin_s_stride = half_dim;
|
||||
#define SIN_GMEM(a, b, c, d) sin[(a) * offset + (b) * cos_sin_b_stride + (c) * cos_sin_s_stride + (d)]
|
||||
#define COS_GMEM(a, b, c, d) cos[(a) * offset + (b) * cos_sin_b_stride + (c) * cos_sin_s_stride + (d)]
|
||||
|
||||
for (int i = threadIdx.x; i < half_dim; i += blockDim.x) {
|
||||
if (i < first) {
|
||||
cos_smem[i] = COS_GMEM(0, b, s, i);
|
||||
sin_smem[i] = SIN_GMEM(0, b, s, i);
|
||||
} else if (i < (second + first)) {
|
||||
cos_smem[i] = COS_GMEM(1, b, s, i);
|
||||
sin_smem[i] = SIN_GMEM(1, b, s, i);
|
||||
} else {
|
||||
cos_smem[i] = COS_GMEM(2, b, s, i);
|
||||
sin_smem[i] = SIN_GMEM(2, b, s, i);
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
#define Q_GMEM(a, b, c, d) q[(a) * qb_stride + (b) * qs_stride + (c) * qh_stride + d]
|
||||
#define K_GMEM(a, b, c, d) k[(a) * kb_stride + (b) * ks_stride + (c) * kh_stride + d]
|
||||
#define Q_EMBED_GMEM(a, b, c, d) q_embed[(a) * qeb_stride + (b) * qes_stride + (c) * qeh_stride + d]
|
||||
#define K_EMBED_GMEM(a, b, c, d) k_embed[(a) * keb_stride + (b) * kes_stride + (c) * keh_stride + d]
|
||||
|
||||
for (int j = threadIdx.x * 4; j < q_h * half_dim; j += blockDim.x * 4) {
|
||||
int h_idx = j / half_dim;
|
||||
int base_d = j % half_dim;
|
||||
if (base_d + 3 >= half_dim) continue;
|
||||
|
||||
float4 q_x0 = to_float4<T>(&Q_GMEM(b, s, h_idx, base_d));
|
||||
float4 q_x1 = to_float4<T>(&Q_GMEM(b, s, h_idx, base_d + half_dim));
|
||||
|
||||
float4 cos_vec = to_float4<float>(&cos_smem[base_d]);
|
||||
float4 sin_vec = to_float4<float>(&sin_smem[base_d]);
|
||||
|
||||
float4 q0_rot = make_float4(
|
||||
q_x0.x * cos_vec.x - q_x1.x * sin_vec.x,
|
||||
q_x0.y * cos_vec.y - q_x1.y * sin_vec.y,
|
||||
q_x0.z * cos_vec.z - q_x1.z * sin_vec.z,
|
||||
q_x0.w * cos_vec.w - q_x1.w * sin_vec.w
|
||||
);
|
||||
|
||||
float4 q1_rot = make_float4(
|
||||
q_x1.x * cos_vec.x + q_x0.x * sin_vec.x,
|
||||
q_x1.y * cos_vec.y + q_x0.y * sin_vec.y,
|
||||
q_x1.z * cos_vec.z + q_x0.z * sin_vec.z,
|
||||
q_x1.w * cos_vec.w + q_x0.w * sin_vec.w
|
||||
);
|
||||
|
||||
from_float4<T>(&Q_EMBED_GMEM(b, s, h_idx, base_d), q0_rot);
|
||||
from_float4<T>(&Q_EMBED_GMEM(b, s, h_idx, base_d + half_dim), q1_rot);
|
||||
}
|
||||
|
||||
for (int j = threadIdx.x * 4; j < k_h * half_dim; j += blockDim.x * 4) {
|
||||
int h_idx = j / half_dim;
|
||||
int base_d = j % half_dim;
|
||||
if (base_d + 3 >= half_dim) continue;
|
||||
|
||||
float4 k_x0 = to_float4<T>(&K_GMEM(b, s, h_idx, base_d));
|
||||
float4 k_x1 = to_float4<T>(&K_GMEM(b, s, h_idx, base_d + half_dim));
|
||||
|
||||
float4 cos_vec = to_float4<float>(&cos_smem[base_d]);
|
||||
float4 sin_vec = to_float4<float>(&sin_smem[base_d]);
|
||||
|
||||
float4 k0_rot = make_float4(
|
||||
k_x0.x * cos_vec.x - k_x1.x * sin_vec.x,
|
||||
k_x0.y * cos_vec.y - k_x1.y * sin_vec.y,
|
||||
k_x0.z * cos_vec.z - k_x1.z * sin_vec.z,
|
||||
k_x0.w * cos_vec.w - k_x1.w * sin_vec.w
|
||||
);
|
||||
|
||||
float4 k1_rot = make_float4(
|
||||
k_x1.x * cos_vec.x + k_x0.x * sin_vec.x,
|
||||
k_x1.y * cos_vec.y + k_x0.y * sin_vec.y,
|
||||
k_x1.z * cos_vec.z + k_x0.z * sin_vec.z,
|
||||
k_x1.w * cos_vec.w + k_x0.w * sin_vec.w
|
||||
);
|
||||
|
||||
from_float4<T>(&K_EMBED_GMEM(b, s, h_idx, base_d), k0_rot);
|
||||
from_float4<T>(&K_EMBED_GMEM(b, s, h_idx, base_d + half_dim), k1_rot);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
__global__ void MRopeInplaceKernel(float* cos, float* sin,
|
||||
T* q, const int q_h, T* k, const int k_h,
|
||||
const int first, const int second,
|
||||
const int d, const int qb_stride, const int qs_stride, const int qh_stride,
|
||||
const int kb_stride, const int ks_stride, const int kh_stride) {
|
||||
extern __shared__ char cos_sin[];
|
||||
const int half_dim = d / 2;
|
||||
float* cos_smem = reinterpret_cast<float*>(cos_sin);
|
||||
float* sin_smem = cos_smem + half_dim;
|
||||
int b = blockIdx.x;
|
||||
int s = blockIdx.y;
|
||||
|
||||
int64_t offset = gridDim.x * gridDim.y * half_dim;
|
||||
int64_t cos_sin_b_stride = gridDim.y * half_dim;
|
||||
int64_t cos_sin_s_stride = half_dim;
|
||||
#define SIN_GMEM(a, b, c, d) sin[(a) * offset + (b) * cos_sin_b_stride + (c) * cos_sin_s_stride + (d)]
|
||||
#define COS_GMEM(a, b, c, d) cos[(a) * offset + (b) * cos_sin_b_stride + (c) * cos_sin_s_stride + (d)]
|
||||
|
||||
for (int i = threadIdx.x; i < half_dim; i += blockDim.x) {
|
||||
if (i < first) {
|
||||
cos_smem[i] = COS_GMEM(0, b, s, i);
|
||||
sin_smem[i] = SIN_GMEM(0, b, s, i);
|
||||
} else if (i < (second + first)) {
|
||||
cos_smem[i] = COS_GMEM(1, b, s, i);
|
||||
sin_smem[i] = SIN_GMEM(1, b, s, i);
|
||||
} else {
|
||||
cos_smem[i] = COS_GMEM(2, b, s, i);
|
||||
sin_smem[i] = SIN_GMEM(2, b, s, i);
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
#define Q_GMEM(a, b, c, d) q[(a) * qb_stride + (b) * qs_stride + (c) * qh_stride + d]
|
||||
#define K_GMEM(a, b, c, d) k[(a) * kb_stride + (b) * ks_stride + (c) * kh_stride + d]
|
||||
|
||||
for (int j = threadIdx.x * 4; j < q_h * half_dim; j += blockDim.x * 4) {
|
||||
int h_idx = j / half_dim;
|
||||
int base_d = j % half_dim;
|
||||
if (base_d + 3 >= half_dim) continue;
|
||||
|
||||
float4 q_x0 = to_float4<T>(&Q_GMEM(b, s, h_idx, base_d));
|
||||
float4 q_x1 = to_float4<T>(&Q_GMEM(b, s, h_idx, base_d + half_dim));
|
||||
|
||||
float4 cos_vec = to_float4<float>(&cos_smem[base_d]);
|
||||
float4 sin_vec = to_float4<float>(&sin_smem[base_d]);
|
||||
|
||||
float4 q0_rot = make_float4(
|
||||
q_x0.x * cos_vec.x - q_x1.x * sin_vec.x,
|
||||
q_x0.y * cos_vec.y - q_x1.y * sin_vec.y,
|
||||
q_x0.z * cos_vec.z - q_x1.z * sin_vec.z,
|
||||
q_x0.w * cos_vec.w - q_x1.w * sin_vec.w
|
||||
);
|
||||
|
||||
float4 q1_rot = make_float4(
|
||||
q_x1.x * cos_vec.x + q_x0.x * sin_vec.x,
|
||||
q_x1.y * cos_vec.y + q_x0.y * sin_vec.y,
|
||||
q_x1.z * cos_vec.z + q_x0.z * sin_vec.z,
|
||||
q_x1.w * cos_vec.w + q_x0.w * sin_vec.w
|
||||
);
|
||||
|
||||
from_float4<T>(&Q_GMEM(b, s, h_idx, base_d), q0_rot);
|
||||
from_float4<T>(&Q_GMEM(b, s, h_idx, base_d + half_dim), q1_rot);
|
||||
}
|
||||
|
||||
for (int j = threadIdx.x * 4; j < k_h * half_dim; j += blockDim.x * 4) {
|
||||
int h_idx = j / half_dim;
|
||||
int base_d = j % half_dim;
|
||||
if (base_d + 3 >= half_dim) continue;
|
||||
|
||||
float4 k_x0 = to_float4<T>(&K_GMEM(b, s, h_idx, base_d));
|
||||
float4 k_x1 = to_float4<T>(&K_GMEM(b, s, h_idx, base_d + half_dim));
|
||||
|
||||
float4 cos_vec = to_float4<float>(&cos_smem[base_d]);
|
||||
float4 sin_vec = to_float4<float>(&sin_smem[base_d]);
|
||||
|
||||
float4 k0_rot = make_float4(
|
||||
k_x0.x * cos_vec.x - k_x1.x * sin_vec.x,
|
||||
k_x0.y * cos_vec.y - k_x1.y * sin_vec.y,
|
||||
k_x0.z * cos_vec.z - k_x1.z * sin_vec.z,
|
||||
k_x0.w * cos_vec.w - k_x1.w * sin_vec.w
|
||||
);
|
||||
|
||||
float4 k1_rot = make_float4(
|
||||
k_x1.x * cos_vec.x + k_x0.x * sin_vec.x,
|
||||
k_x1.y * cos_vec.y + k_x0.y * sin_vec.y,
|
||||
k_x1.z * cos_vec.z + k_x0.z * sin_vec.z,
|
||||
k_x1.w * cos_vec.w + k_x0.w * sin_vec.w
|
||||
);
|
||||
|
||||
from_float4<T>(&K_GMEM(b, s, h_idx, base_d), k0_rot);
|
||||
from_float4<T>(&K_GMEM(b, s, h_idx, base_d + half_dim), k1_rot);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
__global__ void MRopeKernelBackward(
|
||||
const float* cos,
|
||||
const float* sin,
|
||||
const T* grad_q_embed,
|
||||
const T* grad_k_embed,
|
||||
T* grad_q,
|
||||
T* grad_k,
|
||||
const int first, const int second,
|
||||
const int q_h,
|
||||
const int k_h,
|
||||
const int d,
|
||||
const int qb_stride, const int qs_stride, const int qh_stride,
|
||||
const int kb_stride, const int ks_stride, const int kh_stride) {
|
||||
|
||||
extern __shared__ char cos_sin[];
|
||||
const int half_dim = d / 2;
|
||||
float* cos_smem = reinterpret_cast<float*>(cos_sin);
|
||||
float* sin_smem = cos_smem + half_dim;
|
||||
|
||||
int b = blockIdx.x;
|
||||
int s = blockIdx.y;
|
||||
|
||||
int64_t offset = gridDim.x * gridDim.y * half_dim;
|
||||
int64_t cos_sin_b_stride = gridDim.y * half_dim;
|
||||
int64_t cos_sin_s_stride = half_dim;
|
||||
|
||||
#define SIN_GMEM(a, b, c, d) sin[(a) * offset + (b) * cos_sin_b_stride + (c) * cos_sin_s_stride + (d)]
|
||||
#define COS_GMEM(a, b, c, d) cos[(a) * offset + (b) * cos_sin_b_stride + (c) * cos_sin_s_stride + (d)]
|
||||
|
||||
for (int i = threadIdx.x; i < half_dim; i += blockDim.x) {
|
||||
if (i < first) {
|
||||
cos_smem[i] = COS_GMEM(0, b, s, i);
|
||||
sin_smem[i] = SIN_GMEM(0, b, s, i);
|
||||
} else if (i < (second + first)) {
|
||||
cos_smem[i] = COS_GMEM(1, b, s, i);
|
||||
sin_smem[i] = SIN_GMEM(1, b, s, i);
|
||||
} else {
|
||||
cos_smem[i] = COS_GMEM(2, b, s, i);
|
||||
sin_smem[i] = SIN_GMEM(2, b, s, i);
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
#define GQ_EMBED(a, b, c, d) grad_q_embed[(a) * qb_stride + (b) * qs_stride + (c) * qh_stride + (d)]
|
||||
#define GK_EMBED(a, b, c, d) grad_k_embed[(a) * kb_stride + (b) * ks_stride + (c) * kh_stride + (d)]
|
||||
#define GQ(a, b, c, d) grad_q[(a) * qb_stride + (b) * qs_stride + (c) * qh_stride + (d)]
|
||||
#define GK(a, b, c, d) grad_k[(a) * kb_stride + (b) * ks_stride + (c) * kh_stride + (d)]
|
||||
|
||||
// Process grad_q
|
||||
for (int j = threadIdx.x * 4; j < q_h * half_dim; j += blockDim.x * 4) {
|
||||
int h_idx = j / half_dim;
|
||||
int base_d = j % half_dim;
|
||||
if (base_d + 3 >= half_dim) continue;
|
||||
|
||||
// float path
|
||||
float4 gq0_rot = to_float4<T>(&GQ_EMBED(b, s, h_idx, base_d));
|
||||
float4 gq1_rot = to_float4<T>(&GQ_EMBED(b, s, h_idx, base_d + half_dim));
|
||||
|
||||
float4 cos_vec = to_float4<float>(&cos_smem[base_d]);
|
||||
float4 sin_vec = to_float4<float>(&sin_smem[base_d]);
|
||||
|
||||
float4 gq0 = make_float4(
|
||||
gq0_rot.x * cos_vec.x + gq1_rot.x * sin_vec.x,
|
||||
gq0_rot.y * cos_vec.y + gq1_rot.y * sin_vec.y,
|
||||
gq0_rot.z * cos_vec.z + gq1_rot.z * sin_vec.z,
|
||||
gq0_rot.w * cos_vec.w + gq1_rot.w * sin_vec.w
|
||||
);
|
||||
float4 gq1 = make_float4(
|
||||
gq1_rot.x * cos_vec.x - gq0_rot.x * sin_vec.x,
|
||||
gq1_rot.y * cos_vec.y - gq0_rot.y * sin_vec.y,
|
||||
gq1_rot.z * cos_vec.z - gq0_rot.z * sin_vec.z,
|
||||
gq1_rot.w * cos_vec.w - gq0_rot.w * sin_vec.w
|
||||
);
|
||||
|
||||
from_float4<T>(&GQ(b, s, h_idx, base_d), gq0);
|
||||
from_float4<T>(&GQ(b, s, h_idx, base_d + half_dim), gq1);
|
||||
}
|
||||
|
||||
// Process grad_k
|
||||
for (int j = threadIdx.x * 4; j < k_h * half_dim; j += blockDim.x * 4) {
|
||||
int h_idx = j / half_dim;
|
||||
int base_d = j % half_dim;
|
||||
if (base_d + 3 >= half_dim) continue;
|
||||
|
||||
// float path
|
||||
float4 gk0_rot = to_float4<T>(&GK_EMBED(b, s, h_idx, base_d));
|
||||
float4 gk1_rot = to_float4<T>(&GK_EMBED(b, s, h_idx, base_d + half_dim));
|
||||
|
||||
float4 cos_vec = to_float4<float>(&cos_smem[base_d]);
|
||||
float4 sin_vec = to_float4<float>(&sin_smem[base_d]);
|
||||
|
||||
float4 gk0 = make_float4(
|
||||
gk0_rot.x * cos_vec.x + gk1_rot.x * sin_vec.x,
|
||||
gk0_rot.y * cos_vec.y + gk1_rot.y * sin_vec.y,
|
||||
gk0_rot.z * cos_vec.z + gk1_rot.z * sin_vec.z,
|
||||
gk0_rot.w * cos_vec.w + gk1_rot.w * sin_vec.w
|
||||
);
|
||||
float4 gk1 = make_float4(
|
||||
gk1_rot.x * cos_vec.x - gk0_rot.x * sin_vec.x,
|
||||
gk1_rot.y * cos_vec.y - gk0_rot.y * sin_vec.y,
|
||||
gk1_rot.z * cos_vec.z - gk0_rot.z * sin_vec.z,
|
||||
gk1_rot.w * cos_vec.w - gk0_rot.w * sin_vec.w
|
||||
);
|
||||
|
||||
from_float4<T>(&GK(b, s, h_idx, base_d), gk0);
|
||||
from_float4<T>(&GK(b, s, h_idx, base_d + half_dim), gk1);
|
||||
}
|
||||
}
|
||||
|
||||
void MRope(const at::Tensor& q, // [b, s, h, d]
|
||||
const at::Tensor& k, // [b, s, h_k, d]
|
||||
const at::Tensor& q_embed, // [b, s, h, d]
|
||||
const at::Tensor& k_embed, // [b, s, h_k, d]
|
||||
const at::Tensor& cos, // [3, b, s, d / 2]
|
||||
const at::Tensor& sin, // [3, b, s, d / 2]
|
||||
const int first,
|
||||
const int second
|
||||
) {
|
||||
int Nthreads = 256;
|
||||
|
||||
cudaStream_t stream = static_cast<cudaStream_t>(
|
||||
at::cuda::getCurrentCUDAStream().stream());
|
||||
|
||||
ASSERT_CHECK(q.scalar_type() == k.scalar_type());
|
||||
ASSERT_CHECK(q.scalar_type() == q_embed.scalar_type());
|
||||
ASSERT_CHECK(k.scalar_type() == k_embed.scalar_type());
|
||||
ASSERT_CHECK(q_embed.size(0) == q.size(0));
|
||||
ASSERT_CHECK(q_embed.size(1) == q.size(1));
|
||||
ASSERT_CHECK(q_embed.size(2) == q.size(2));
|
||||
ASSERT_CHECK(q_embed.size(3) == q.size(3));
|
||||
ASSERT_CHECK(k_embed.size(0) == k.size(0));
|
||||
ASSERT_CHECK(k_embed.size(1) == k.size(1));
|
||||
ASSERT_CHECK(k_embed.size(2) == k.size(2));
|
||||
ASSERT_CHECK(k_embed.size(3) == k.size(3));
|
||||
ASSERT_CHECK(q.size(3) % 8 == 0);
|
||||
ASSERT_CHECK(cos.size(0) == 3);
|
||||
ASSERT_CHECK(cos.size(3) == q.size(3) / 2);
|
||||
ASSERT_CHECK(cos.scalar_type() == at::kFloat);
|
||||
ASSERT_CHECK(sin.scalar_type() == at::kFloat);
|
||||
ASSERT_CHECK(sin.size(0) == 3);
|
||||
ASSERT_CHECK(q.stride(3) == 1);
|
||||
ASSERT_CHECK(k.stride(3) == 1);
|
||||
ASSERT_CHECK(q_embed.stride(3) == 1);
|
||||
ASSERT_CHECK(k_embed.stride(3) == 1);
|
||||
ASSERT_CHECK(cos.stride(3) == 1);
|
||||
ASSERT_CHECK(sin.stride(3) == 1);
|
||||
ASSERT_CHECK(cos.is_contiguous());
|
||||
ASSERT_CHECK(sin.is_contiguous());
|
||||
|
||||
int64_t batch = q.size(0);
|
||||
int64_t q_head_num = q.size(2);
|
||||
int64_t k_head_num = k.size(2);
|
||||
int64_t seq_len = q.size(1);
|
||||
int64_t dim = q.size(3);
|
||||
int64_t qb_stride = q.stride(0);
|
||||
int64_t kb_stride = k.stride(0);
|
||||
int64_t qs_stride = q.stride(1);
|
||||
int64_t ks_stride = k.stride(1);
|
||||
int64_t qh_stride = q.stride(2);
|
||||
int64_t kh_stride = k.stride(2);
|
||||
int64_t qeb_stride = q_embed.stride(0);
|
||||
int64_t keb_stride = k_embed.stride(0);
|
||||
int64_t qes_stride = q_embed.stride(1);
|
||||
int64_t kes_stride = k_embed.stride(1);
|
||||
int64_t qeh_stride = q_embed.stride(2);
|
||||
int64_t keh_stride = k_embed.stride(2);
|
||||
|
||||
dim3 grid(batch, seq_len);
|
||||
|
||||
if (q.scalar_type() == at::kHalf) {
|
||||
const half* q_data = static_cast<const half*>(q.data_ptr());
|
||||
const half* k_data = static_cast<const half*>(k.data_ptr());
|
||||
half* q_embed_data = static_cast<half*>(q_embed.data_ptr());
|
||||
half* k_embed_data = static_cast<half*>(k_embed.data_ptr());
|
||||
const float* cos_data = static_cast<const float*>(cos.data_ptr());
|
||||
const float* sin_data = static_cast<const float*>(sin.data_ptr());
|
||||
MRopeKernel<half><<<grid, Nthreads, dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
q_data, q_head_num,
|
||||
k_data, k_head_num,
|
||||
q_embed_data, k_embed_data,
|
||||
first, second, dim,
|
||||
qb_stride, qs_stride, qh_stride, kb_stride, ks_stride, kh_stride,
|
||||
qeb_stride, qes_stride, qeh_stride, keb_stride, kes_stride, keh_stride);
|
||||
} else if (q.scalar_type() == at::kBFloat16) {
|
||||
const __nv_bfloat16* q_data = static_cast<const __nv_bfloat16*>(q.data_ptr());
|
||||
const __nv_bfloat16* k_data = static_cast<const __nv_bfloat16*>(k.data_ptr());
|
||||
__nv_bfloat16* q_embed_data = static_cast<__nv_bfloat16*>(q_embed.data_ptr());
|
||||
__nv_bfloat16* k_embed_data = static_cast<__nv_bfloat16*>(k_embed.data_ptr());
|
||||
const float* cos_data = static_cast<const float*>(cos.data_ptr());
|
||||
const float* sin_data = static_cast<const float*>(sin.data_ptr());
|
||||
MRopeKernel<__nv_bfloat16><<<grid, Nthreads, dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
q_data, q_head_num,
|
||||
k_data, k_head_num,
|
||||
q_embed_data, k_embed_data,
|
||||
first, second, dim,
|
||||
qb_stride, qs_stride, qh_stride, kb_stride, ks_stride, kh_stride,
|
||||
qeb_stride, qes_stride, qeh_stride, keb_stride, kes_stride, keh_stride);
|
||||
} else if (q.scalar_type() == at::kFloat) {
|
||||
const float* q_data = static_cast<const float*>(q.data_ptr());
|
||||
const float* k_data = static_cast<const float*>(k.data_ptr());
|
||||
float* q_embed_data = static_cast<float*>(q_embed.data_ptr());
|
||||
float* k_embed_data = static_cast<float*>(k_embed.data_ptr());
|
||||
const float* cos_data = static_cast<const float*>(cos.data_ptr());
|
||||
const float* sin_data = static_cast<const float*>(sin.data_ptr());
|
||||
MRopeKernel<float><<<grid, Nthreads, dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
q_data, q_head_num,
|
||||
k_data, k_head_num,
|
||||
q_embed_data, k_embed_data,
|
||||
first, second, dim,
|
||||
qb_stride, qs_stride, qh_stride, kb_stride, ks_stride, kh_stride,
|
||||
qeb_stride, qes_stride, qeh_stride, keb_stride, kes_stride, keh_stride
|
||||
);
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported data type for m_rope");
|
||||
}
|
||||
|
||||
sync_check_cuda_error();
|
||||
}
|
||||
|
||||
void MRopeInplace(const at::Tensor& q, // [b, s, h, d]
|
||||
const at::Tensor& k, // [b, s, h_k, d]
|
||||
const at::Tensor& cos, // [3, b, s, d / 2]
|
||||
const at::Tensor& sin, // [3, b, s, d / 2]
|
||||
const int first,
|
||||
const int second
|
||||
) {
|
||||
int Nthreads = 256;
|
||||
|
||||
cudaStream_t stream = static_cast<cudaStream_t>(
|
||||
at::cuda::getCurrentCUDAStream().stream());
|
||||
|
||||
ASSERT_CHECK(q.scalar_type() == k.scalar_type());
|
||||
ASSERT_CHECK(q.size(3) % 8 == 0);
|
||||
ASSERT_CHECK(cos.size(0) == 3);
|
||||
ASSERT_CHECK(cos.size(3) == q.size(3) / 2);
|
||||
ASSERT_CHECK(cos.scalar_type() == at::kFloat);
|
||||
ASSERT_CHECK(sin.scalar_type() == at::kFloat);
|
||||
ASSERT_CHECK(sin.size(0) == 3);
|
||||
ASSERT_CHECK(q.stride(3) == 1);
|
||||
ASSERT_CHECK(k.stride(3) == 1);
|
||||
ASSERT_CHECK(cos.stride(3) == 1);
|
||||
ASSERT_CHECK(sin.stride(3) == 1);
|
||||
ASSERT_CHECK(cos.is_contiguous());
|
||||
ASSERT_CHECK(sin.is_contiguous());
|
||||
|
||||
int64_t batch = q.size(0);
|
||||
int64_t q_head_num = q.size(2);
|
||||
int64_t k_head_num = k.size(2);
|
||||
int64_t seq_len = q.size(1);
|
||||
int64_t dim = q.size(3);
|
||||
int64_t qb_stride = q.stride(0);
|
||||
int64_t kb_stride = k.stride(0);
|
||||
int64_t qs_stride = q.stride(1);
|
||||
int64_t ks_stride = k.stride(1);
|
||||
int64_t qh_stride = q.stride(2);
|
||||
int64_t kh_stride = k.stride(2);
|
||||
|
||||
dim3 grid(batch, seq_len);
|
||||
|
||||
if (q.scalar_type() == at::kHalf) {
|
||||
half* q_data = static_cast<half*>(q.data_ptr());
|
||||
half* k_data = static_cast<half*>(k.data_ptr());
|
||||
float* cos_data = static_cast<float*>(cos.data_ptr());
|
||||
float* sin_data = static_cast<float*>(sin.data_ptr());
|
||||
MRopeInplaceKernel<half><<<grid, Nthreads, dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
q_data, q_head_num,
|
||||
k_data, k_head_num,
|
||||
first, second, dim, qb_stride, qs_stride, qh_stride, kb_stride, ks_stride, kh_stride);
|
||||
} else if (q.scalar_type() == at::kBFloat16) {
|
||||
__nv_bfloat16* q_data = static_cast<__nv_bfloat16*>(q.data_ptr());
|
||||
__nv_bfloat16* k_data = static_cast<__nv_bfloat16*>(k.data_ptr());
|
||||
float* cos_data = static_cast<float*>(cos.data_ptr());
|
||||
float* sin_data = static_cast<float*>(sin.data_ptr());
|
||||
MRopeInplaceKernel<__nv_bfloat16><<<grid, Nthreads, dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
q_data, q_head_num,
|
||||
k_data, k_head_num,
|
||||
first, second, dim, qb_stride, qs_stride, qh_stride, kb_stride, ks_stride, kh_stride);
|
||||
} else if (q.scalar_type() == at::kFloat) {
|
||||
float* q_data = static_cast<float*>(q.data_ptr());
|
||||
float* k_data = static_cast<float*>(k.data_ptr());
|
||||
float* cos_data = static_cast<float*>(cos.data_ptr());
|
||||
float* sin_data = static_cast<float*>(sin.data_ptr());
|
||||
MRopeInplaceKernel<float><<<grid, Nthreads, dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
q_data, q_head_num,
|
||||
k_data, k_head_num,
|
||||
first, second, dim, qb_stride, qs_stride, qh_stride, kb_stride, ks_stride, kh_stride
|
||||
);
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported data type for m_rope");
|
||||
}
|
||||
|
||||
sync_check_cuda_error();
|
||||
}
|
||||
|
||||
void MRopeBackward(
|
||||
const at::Tensor& grad_q_embed,
|
||||
const at::Tensor& grad_k_embed,
|
||||
const at::Tensor& grad_q, // output: [b, s, q_h, d]
|
||||
const at::Tensor& grad_k, // output: [b, s, k_h, d]
|
||||
const at::Tensor& cos, // [3, b, s, d/2]
|
||||
const at::Tensor& sin, // [3, b, s, d/2]
|
||||
const int first,
|
||||
const int second
|
||||
) {
|
||||
int Nthreads = 256;
|
||||
|
||||
cudaStream_t stream = static_cast<cudaStream_t>(
|
||||
at::cuda::getCurrentCUDAStream().stream());
|
||||
|
||||
ASSERT_CHECK(grad_q_embed.scalar_type() == grad_k_embed.scalar_type());
|
||||
ASSERT_CHECK(grad_q.scalar_type() == grad_k.scalar_type());
|
||||
ASSERT_CHECK(grad_q_embed.size(3) == grad_q.size(3));
|
||||
ASSERT_CHECK(grad_k_embed.size(3) == grad_k.size(3));
|
||||
ASSERT_CHECK(grad_q_embed.size(3) % 8 == 0);
|
||||
ASSERT_CHECK(grad_q_embed.stride(3) == 1);
|
||||
ASSERT_CHECK(grad_k_embed.stride(3) == 1);
|
||||
ASSERT_CHECK(grad_q.stride(0) == grad_q_embed.stride(0));
|
||||
ASSERT_CHECK(grad_q.stride(1) == grad_q_embed.stride(1));
|
||||
ASSERT_CHECK(grad_q.stride(2) == grad_q_embed.stride(2));
|
||||
ASSERT_CHECK(grad_q.stride(3) == grad_q_embed.stride(3));
|
||||
ASSERT_CHECK(grad_k.stride(0) == grad_k_embed.stride(0));
|
||||
ASSERT_CHECK(grad_k.stride(1) == grad_k_embed.stride(1));
|
||||
ASSERT_CHECK(grad_k.stride(2) == grad_k_embed.stride(2));
|
||||
ASSERT_CHECK(grad_k.stride(3) == grad_k_embed.stride(3));
|
||||
|
||||
int64_t batch = grad_q_embed.size(0);
|
||||
int64_t seq_len = grad_q_embed.size(1);
|
||||
int64_t q_head_num = grad_q.size(2);
|
||||
int64_t k_head_num = grad_k.size(2);
|
||||
int64_t dim = grad_q_embed.size(3);
|
||||
int64_t qb_stride = grad_q_embed.stride(0);
|
||||
int64_t kb_stride = grad_k_embed.stride(0);
|
||||
int64_t qs_stride = grad_q_embed.stride(1);
|
||||
int64_t ks_stride = grad_k_embed.stride(1);
|
||||
int64_t qh_stride = grad_q_embed.stride(2);
|
||||
int64_t kh_stride = grad_k_embed.stride(2);
|
||||
|
||||
dim3 grid(batch, seq_len);
|
||||
|
||||
if (grad_q_embed.scalar_type() == at::kHalf) {
|
||||
const half* gq_embed = static_cast<const half*>(grad_q_embed.data_ptr());
|
||||
const half* gk_embed = static_cast<const half*>(grad_k_embed.data_ptr());
|
||||
half* gq = static_cast<half*>(grad_q.data_ptr());
|
||||
half* gk = static_cast<half*>(grad_k.data_ptr());
|
||||
const float* cos_data = static_cast<const float*>(cos.data_ptr());
|
||||
const float* sin_data = static_cast<const float*>(sin.data_ptr());
|
||||
MRopeKernelBackward<half><<<grid, Nthreads, dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
gq_embed, gk_embed,
|
||||
gq, gk,
|
||||
first, second,
|
||||
q_head_num, k_head_num, dim, qb_stride, qs_stride, qh_stride, kb_stride, ks_stride, kh_stride
|
||||
);
|
||||
} else if (grad_q_embed.scalar_type() == at::kBFloat16) {
|
||||
const __nv_bfloat16* gq_embed = static_cast<const __nv_bfloat16*>(grad_q_embed.data_ptr());
|
||||
const __nv_bfloat16* gk_embed = static_cast<const __nv_bfloat16*>(grad_k_embed.data_ptr());
|
||||
__nv_bfloat16* gq = static_cast<__nv_bfloat16*>(grad_q.data_ptr());
|
||||
__nv_bfloat16* gk = static_cast<__nv_bfloat16*>(grad_k.data_ptr());
|
||||
const float* cos_data = static_cast<const float*>(cos.data_ptr());
|
||||
const float* sin_data = static_cast<const float*>(sin.data_ptr());
|
||||
MRopeKernelBackward<__nv_bfloat16><<<grid, Nthreads, dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
gq_embed, gk_embed,
|
||||
gq, gk,
|
||||
first, second,
|
||||
q_head_num, k_head_num, dim, qb_stride, qs_stride, qh_stride, kb_stride, ks_stride, kh_stride
|
||||
);
|
||||
} else if (grad_q_embed.scalar_type() == at::kFloat) {
|
||||
const float* gq_embed = static_cast<const float*>(grad_q_embed.data_ptr());
|
||||
const float* gk_embed = static_cast<const float*>(grad_k_embed.data_ptr());
|
||||
float* gq = static_cast<float*>(grad_q.data_ptr());
|
||||
float* gk = static_cast<float*>(grad_k.data_ptr());
|
||||
const float* cos_data = static_cast<const float*>(cos.data_ptr());
|
||||
const float* sin_data = static_cast<const float*>(sin.data_ptr());
|
||||
MRopeKernelBackward<float><<<grid, Nthreads, dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
gq_embed, gk_embed,
|
||||
gq, gk,
|
||||
first, second,
|
||||
q_head_num, k_head_num, dim, qb_stride, qs_stride, qh_stride, kb_stride, ks_stride, kh_stride
|
||||
);
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported data type for m_rope backward");
|
||||
}
|
||||
|
||||
sync_check_cuda_error();
|
||||
}
|
||||
|
||||
// Pack interface: accepts qkv [bz, seq_len, q_dim + 2*kv_dim], applies mrope
|
||||
// inplace on q and k without Python-side split/view overhead.
|
||||
// Supports GQA (q_num_heads != kv_num_heads). CUDA kernels are not modified.
|
||||
void MRopeInplacePack(const at::Tensor& qkv, // [bz, seq_len, q_dim + 2*kv_dim]
|
||||
const at::Tensor& cos, // [3, bz, seq_len, head_dim/2]
|
||||
const at::Tensor& sin, // [3, bz, seq_len, head_dim/2]
|
||||
int64_t q_num_heads,
|
||||
int64_t kv_num_heads,
|
||||
const int first,
|
||||
const int second) {
|
||||
int Nthreads = 256;
|
||||
cudaStream_t stream = static_cast<cudaStream_t>(
|
||||
at::cuda::getCurrentCUDAStream().stream());
|
||||
|
||||
ASSERT_CHECK(qkv.dim() == 3);
|
||||
ASSERT_CHECK(qkv.stride(2) == 1);
|
||||
ASSERT_CHECK(cos.scalar_type() == at::kFloat);
|
||||
ASSERT_CHECK(sin.scalar_type() == at::kFloat);
|
||||
ASSERT_CHECK(cos.size(0) == 3);
|
||||
ASSERT_CHECK(sin.size(0) == 3);
|
||||
ASSERT_CHECK(cos.is_contiguous());
|
||||
ASSERT_CHECK(sin.is_contiguous());
|
||||
|
||||
int64_t bz = qkv.size(0);
|
||||
int64_t seq_len = qkv.size(1);
|
||||
int64_t head_dim = cos.size(3) * 2; // cos: [3, bz, seq, half_dim]
|
||||
int64_t q_dim = q_num_heads * head_dim;
|
||||
int64_t kv_dim = kv_num_heads * head_dim;
|
||||
ASSERT_CHECK(qkv.size(2) == q_dim + 2 * kv_dim);
|
||||
ASSERT_CHECK(head_dim % 8 == 0);
|
||||
|
||||
// q at offset 0, k at offset q_dim; logical [bz, seq, *_num_heads, head_dim]
|
||||
// with strides [qkv.stride(0), qkv.stride(1), head_dim, 1]
|
||||
int64_t qkv_b_stride = qkv.stride(0);
|
||||
int64_t qkv_s_stride = qkv.stride(1);
|
||||
|
||||
dim3 grid(bz, seq_len);
|
||||
|
||||
if (qkv.scalar_type() == at::kHalf) {
|
||||
half* q_data = static_cast<half*>(qkv.data_ptr());
|
||||
half* k_data = q_data + q_dim;
|
||||
float* cos_data = static_cast<float*>(cos.data_ptr());
|
||||
float* sin_data = static_cast<float*>(sin.data_ptr());
|
||||
MRopeInplaceKernel<half><<<grid, Nthreads, head_dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
q_data, q_num_heads, k_data, kv_num_heads,
|
||||
first, second, head_dim,
|
||||
qkv_b_stride, qkv_s_stride, head_dim,
|
||||
qkv_b_stride, qkv_s_stride, head_dim);
|
||||
} else if (qkv.scalar_type() == at::kBFloat16) {
|
||||
__nv_bfloat16* q_data = static_cast<__nv_bfloat16*>(qkv.data_ptr());
|
||||
__nv_bfloat16* k_data = q_data + q_dim;
|
||||
float* cos_data = static_cast<float*>(cos.data_ptr());
|
||||
float* sin_data = static_cast<float*>(sin.data_ptr());
|
||||
MRopeInplaceKernel<__nv_bfloat16><<<grid, Nthreads, head_dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
q_data, q_num_heads, k_data, kv_num_heads,
|
||||
first, second, head_dim,
|
||||
qkv_b_stride, qkv_s_stride, head_dim,
|
||||
qkv_b_stride, qkv_s_stride, head_dim);
|
||||
} else if (qkv.scalar_type() == at::kFloat) {
|
||||
float* q_data = static_cast<float*>(qkv.data_ptr());
|
||||
float* k_data = q_data + q_dim;
|
||||
float* cos_data = static_cast<float*>(cos.data_ptr());
|
||||
float* sin_data = static_cast<float*>(sin.data_ptr());
|
||||
MRopeInplaceKernel<float><<<grid, Nthreads, head_dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
q_data, q_num_heads, k_data, kv_num_heads,
|
||||
first, second, head_dim,
|
||||
qkv_b_stride, qkv_s_stride, head_dim,
|
||||
qkv_b_stride, qkv_s_stride, head_dim);
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported data type for m_rope_inplace_pack");
|
||||
}
|
||||
|
||||
sync_check_cuda_error();
|
||||
}
|
||||
|
||||
// Pack backward interface: accepts dqkv [bz, seq_len, q_dim + 2*kv_dim] and
|
||||
// applies the inverse mrope transform inplace on the dq and dk slices. The dv
|
||||
// slice is left unchanged.
|
||||
void MRopeInplacePackBackward(const at::Tensor& dqkv, // [bz, seq_len, q_dim + 2*kv_dim]
|
||||
const at::Tensor& cos, // [3, bz, seq_len, head_dim/2]
|
||||
const at::Tensor& sin, // [3, bz, seq_len, head_dim/2]
|
||||
int64_t q_num_heads,
|
||||
int64_t kv_num_heads,
|
||||
const int first,
|
||||
const int second) {
|
||||
int Nthreads = 256;
|
||||
cudaStream_t stream = static_cast<cudaStream_t>(
|
||||
at::cuda::getCurrentCUDAStream().stream());
|
||||
|
||||
ASSERT_CHECK(dqkv.dim() == 3);
|
||||
ASSERT_CHECK(dqkv.stride(2) == 1);
|
||||
ASSERT_CHECK(cos.scalar_type() == at::kFloat);
|
||||
ASSERT_CHECK(sin.scalar_type() == at::kFloat);
|
||||
ASSERT_CHECK(cos.size(0) == 3);
|
||||
ASSERT_CHECK(sin.size(0) == 3);
|
||||
ASSERT_CHECK(cos.is_contiguous());
|
||||
ASSERT_CHECK(sin.is_contiguous());
|
||||
|
||||
int64_t bz = dqkv.size(0);
|
||||
int64_t seq_len = dqkv.size(1);
|
||||
int64_t head_dim = cos.size(3) * 2; // cos: [3, bz, seq, half_dim]
|
||||
int64_t q_dim = q_num_heads * head_dim;
|
||||
int64_t kv_dim = kv_num_heads * head_dim;
|
||||
ASSERT_CHECK(dqkv.size(2) == q_dim + 2 * kv_dim);
|
||||
ASSERT_CHECK(head_dim % 8 == 0);
|
||||
|
||||
int64_t dqkv_b_stride = dqkv.stride(0);
|
||||
int64_t dqkv_s_stride = dqkv.stride(1);
|
||||
|
||||
dim3 grid(bz, seq_len);
|
||||
|
||||
if (dqkv.scalar_type() == at::kHalf) {
|
||||
half* dq_data = static_cast<half*>(dqkv.data_ptr());
|
||||
half* dk_data = dq_data + q_dim;
|
||||
const float* cos_data = static_cast<const float*>(cos.data_ptr());
|
||||
const float* sin_data = static_cast<const float*>(sin.data_ptr());
|
||||
MRopeKernelBackward<half><<<grid, Nthreads, head_dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
dq_data, dk_data,
|
||||
dq_data, dk_data,
|
||||
first, second,
|
||||
q_num_heads, kv_num_heads, head_dim,
|
||||
dqkv_b_stride, dqkv_s_stride, head_dim,
|
||||
dqkv_b_stride, dqkv_s_stride, head_dim);
|
||||
} else if (dqkv.scalar_type() == at::kBFloat16) {
|
||||
__nv_bfloat16* dq_data = static_cast<__nv_bfloat16*>(dqkv.data_ptr());
|
||||
__nv_bfloat16* dk_data = dq_data + q_dim;
|
||||
const float* cos_data = static_cast<const float*>(cos.data_ptr());
|
||||
const float* sin_data = static_cast<const float*>(sin.data_ptr());
|
||||
MRopeKernelBackward<__nv_bfloat16><<<grid, Nthreads, head_dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
dq_data, dk_data,
|
||||
dq_data, dk_data,
|
||||
first, second,
|
||||
q_num_heads, kv_num_heads, head_dim,
|
||||
dqkv_b_stride, dqkv_s_stride, head_dim,
|
||||
dqkv_b_stride, dqkv_s_stride, head_dim);
|
||||
} else if (dqkv.scalar_type() == at::kFloat) {
|
||||
float* dq_data = static_cast<float*>(dqkv.data_ptr());
|
||||
float* dk_data = dq_data + q_dim;
|
||||
const float* cos_data = static_cast<const float*>(cos.data_ptr());
|
||||
const float* sin_data = static_cast<const float*>(sin.data_ptr());
|
||||
MRopeKernelBackward<float><<<grid, Nthreads, head_dim * sizeof(float), stream>>>(
|
||||
cos_data, sin_data,
|
||||
dq_data, dk_data,
|
||||
dq_data, dk_data,
|
||||
first, second,
|
||||
q_num_heads, kv_num_heads, head_dim,
|
||||
dqkv_b_stride, dqkv_s_stride, head_dim,
|
||||
dqkv_b_stride, dqkv_s_stride, head_dim);
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported data type for m_rope_inplace_pack_bwd");
|
||||
}
|
||||
|
||||
sync_check_cuda_error();
|
||||
}
|
||||
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
|
||||
} // wallx_cuda_m_rope
|
||||
@@ -0,0 +1,581 @@
|
||||
// Re-enable CUDA half operators (PyTorch disables them)
|
||||
#undef __CUDA_NO_HALF_OPERATORS__
|
||||
#undef __CUDA_NO_HALF_CONVERSIONS__
|
||||
#undef __CUDA_NO_HALF2_OPERATORS__
|
||||
#undef __CUDA_NO_BFLOAT16_CONVERSIONS__
|
||||
|
||||
#include <torch/extension.h>
|
||||
#include <c10/cuda/CUDAStream.h>
|
||||
|
||||
#include <cuda_fp16.h>
|
||||
#include <cuda_bf16.h>
|
||||
#include <cub/cub.cuh>
|
||||
#include "../common/cuda_utils.h"
|
||||
#include "cuda_runtime.h"
|
||||
#include "device_launch_parameters.h"
|
||||
|
||||
// Global FP8 guard: use CUDA_VERSION (defined in both host and device passes),
|
||||
// not __CUDA_ARCH__ (device-pass only - leaves host dispatchers unable to see
|
||||
// FP8 typedefs/branches). FP8 storage types arrived in CUDA 11.8.
|
||||
#if defined(CUDA_VERSION) && (CUDA_VERSION >= 11800)
|
||||
#include <cuda_fp8.h>
|
||||
#define XCOMPUTE_HAS_FP8 1
|
||||
#else
|
||||
#define XCOMPUTE_HAS_FP8 0
|
||||
#endif
|
||||
|
||||
namespace wallx_cuda_permute {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CUTLASS-free helpers: replace cutlass::Array, NumericArrayConverter,
|
||||
// arch::global_load, and type aliases with standard CUDA equivalents.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Vectorized array: replaces cutlass::Array<T, N>
|
||||
// __align__(16) ensures safe reinterpret_cast<float4*> in vec_load/store.
|
||||
template <typename T, int N>
|
||||
struct __align__(16) VecArray {
|
||||
T data[N];
|
||||
__device__ __forceinline__ T& at(int i) { return data[i]; }
|
||||
__device__ __forceinline__ const T& at(int i) const { return data[i]; }
|
||||
__device__ __forceinline__ T* raw() { return data; }
|
||||
__device__ __forceinline__ const T* raw() const { return data; }
|
||||
__device__ __forceinline__ void clear() {
|
||||
#pragma unroll
|
||||
for (int i = 0; i < N; i++) data[i] = T(0);
|
||||
}
|
||||
};
|
||||
|
||||
// Scalar multiply: VecArray * scalar
|
||||
template <typename T, int N>
|
||||
__device__ __forceinline__ VecArray<T, N> operator*(const VecArray<T, N>& a, T s) {
|
||||
VecArray<T, N> r;
|
||||
#pragma unroll
|
||||
for (int i = 0; i < N; i++) r.data[i] = a.data[i] * s;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Element-wise convert: replaces cutlass::NumericArrayConverter
|
||||
template <typename To, typename From, int N>
|
||||
struct ArrayConverter {
|
||||
__device__ __forceinline__ VecArray<To, N> operator()(const VecArray<From, N>& src) const {
|
||||
VecArray<To, N> dst;
|
||||
#pragma unroll
|
||||
for (int i = 0; i < N; i++) dst.data[i] = To(src.data[i]);
|
||||
return dst;
|
||||
}
|
||||
};
|
||||
|
||||
// Vectorized global load: replaces cutlass::arch::global_load
|
||||
template <typename Fragment>
|
||||
__device__ __forceinline__ void vec_load(Fragment& frag, const void* ptr) {
|
||||
static_assert(sizeof(Fragment) == sizeof(float4) || sizeof(Fragment) == 16,
|
||||
"vec_load expects 16-byte fragment");
|
||||
*reinterpret_cast<float4*>(&frag) = *reinterpret_cast<const float4*>(ptr);
|
||||
}
|
||||
|
||||
// Type aliases: replace cutlass types with standard CUDA types
|
||||
using half_t = __half;
|
||||
using bfloat16_t = __nv_bfloat16;
|
||||
#if XCOMPUTE_HAS_FP8
|
||||
using float_e5m2_t = __nv_fp8_e5m2;
|
||||
using float_e4m3_t = __nv_fp8_e4m3; // fn variant; CUDA's __nv_fp8_e4m3 IS the "fn" form
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Kernels (unchanged logic, only CUTLASS types/calls replaced)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static __global__ void moe_permute_topK_row_map(
|
||||
const int *sorted_row_id,
|
||||
int *row_id_map,
|
||||
const int num_rows,
|
||||
const int num_topK,
|
||||
const int num_out_tokens)
|
||||
{
|
||||
const int bid = blockIdx.x;
|
||||
const int tid = threadIdx.x;
|
||||
const int idx = bid * blockDim.x + tid;
|
||||
|
||||
if (idx >= num_rows * num_topK)
|
||||
return;
|
||||
|
||||
int source_row = sorted_row_id[idx];
|
||||
int source_token_id = source_row / num_topK;
|
||||
int source_topK_id = source_row % num_topK;
|
||||
|
||||
if (idx >= num_out_tokens)
|
||||
{
|
||||
row_id_map[source_topK_id * num_rows + source_token_id] = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
row_id_map[source_topK_id * num_rows + source_token_id] = idx;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename TCompute, int kElementsPerAccess, bool hasProb>
|
||||
__global__ void moe_recover_topK_kernel(const T *input,
|
||||
T *unpermuted_output,
|
||||
const int *row_id_map,
|
||||
const float *prob,
|
||||
const int num_rows,
|
||||
const int num_topK,
|
||||
const int num_cols)
|
||||
{
|
||||
extern __shared__ int8_t s_mem[];
|
||||
TCompute *s_prob = reinterpret_cast<TCompute *>(s_mem);
|
||||
|
||||
using FragLS = VecArray<T, kElementsPerAccess>;
|
||||
using FragC = VecArray<TCompute, kElementsPerAccess>;
|
||||
|
||||
ArrayConverter<TCompute, T, kElementsPerAccess> src_converter;
|
||||
ArrayConverter<T, TCompute, kElementsPerAccess> dst_converter;
|
||||
|
||||
const int source_token = blockIdx.x;
|
||||
const int tid = threadIdx.x;
|
||||
|
||||
if (hasProb)
|
||||
{
|
||||
for (int i = tid; i < num_topK; i += blockDim.x * blockDim.y)
|
||||
{
|
||||
s_prob[i] = TCompute(prob[source_token * num_topK + i]);
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
for (int i = tid * kElementsPerAccess; i < num_cols; i += blockDim.x * kElementsPerAccess)
|
||||
{
|
||||
FragLS frag_load_store;
|
||||
FragC frag_elem;
|
||||
FragC frag_sum;
|
||||
|
||||
int source_row = row_id_map[source_token];
|
||||
|
||||
if (source_row != -1)
|
||||
{
|
||||
const T *source_row_ptr = input + source_row * num_cols;
|
||||
|
||||
vec_load(frag_load_store, source_row_ptr + i);
|
||||
frag_sum = src_converter(frag_load_store);
|
||||
|
||||
if (hasProb)
|
||||
{
|
||||
frag_sum = frag_sum * s_prob[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
frag_sum.clear();
|
||||
}
|
||||
|
||||
for (int k = 1; k < num_topK; k++)
|
||||
{
|
||||
source_row = row_id_map[k * num_rows + source_token];
|
||||
|
||||
if (source_row == -1)
|
||||
continue;
|
||||
|
||||
const T *source_row_ptr = input + source_row * num_cols;
|
||||
|
||||
vec_load(frag_load_store, source_row_ptr + i);
|
||||
frag_elem = src_converter(frag_load_store);
|
||||
|
||||
if (hasProb)
|
||||
{
|
||||
frag_elem = frag_elem * s_prob[k];
|
||||
}
|
||||
|
||||
for (int e = 0; e < kElementsPerAccess; e++)
|
||||
{
|
||||
frag_sum.at(e) = frag_sum.at(e) + frag_elem.at(e);
|
||||
}
|
||||
}
|
||||
|
||||
T *dest_row_ptr = unpermuted_output + source_token * num_cols;
|
||||
frag_load_store = dst_converter(frag_sum);
|
||||
*(float4 *)(dest_row_ptr + i) = *(float4 *)(frag_load_store.raw());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename TCompute,
|
||||
int kElementsPerAccess,
|
||||
int topKTile,
|
||||
bool hasProb>
|
||||
__global__ void moe_permute_topK_kernel(const T *input_bwd,
|
||||
const T *input_fwd,
|
||||
T *act_grad,
|
||||
const float *prob,
|
||||
float *prob_grad,
|
||||
const int *row_id_map,
|
||||
const int num_rows,
|
||||
const int num_topK,
|
||||
const int num_cols)
|
||||
{
|
||||
extern __shared__ int8_t s_mem[];
|
||||
TCompute *s_prob = reinterpret_cast<TCompute *>(s_mem);
|
||||
|
||||
using FragLS = VecArray<T, kElementsPerAccess>;
|
||||
using FragC = VecArray<TCompute, kElementsPerAccess>;
|
||||
|
||||
ArrayConverter<TCompute, T, kElementsPerAccess> src_converter;
|
||||
ArrayConverter<T, TCompute, kElementsPerAccess> dst_converter;
|
||||
|
||||
const int source_token = blockIdx.x;
|
||||
const int tid = threadIdx.x;
|
||||
|
||||
if (hasProb)
|
||||
{
|
||||
for (int i = tid; i < num_topK; i += blockDim.x)
|
||||
{
|
||||
s_prob[i] = TCompute(prob[source_token * num_topK + i]);
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
float accum[topKTile] = {0.0f};
|
||||
FragLS frag_load_store;
|
||||
|
||||
const T *source_row_ptr = input_bwd + source_token * num_cols;
|
||||
for (int i = tid * kElementsPerAccess; i < num_cols; i += blockDim.x * kElementsPerAccess)
|
||||
{
|
||||
vec_load(frag_load_store, source_row_ptr + i);
|
||||
FragC frag_src = src_converter(frag_load_store);
|
||||
|
||||
int index = source_token;
|
||||
|
||||
for (int k = 0; k < topKTile; k++)
|
||||
{
|
||||
if (k == num_topK)
|
||||
break;
|
||||
|
||||
int dest_row = row_id_map[index];
|
||||
index += num_rows;
|
||||
|
||||
if (dest_row == -1)
|
||||
continue;
|
||||
|
||||
if (hasProb)
|
||||
{
|
||||
frag_load_store = dst_converter(frag_src * s_prob[k]);
|
||||
}
|
||||
else
|
||||
{
|
||||
frag_load_store = dst_converter(frag_src);
|
||||
}
|
||||
|
||||
T *dest_row_ptr = act_grad + dest_row * num_cols;
|
||||
*(float4 *)(dest_row_ptr + i) = *(float4 *)(frag_load_store.raw());
|
||||
|
||||
if (hasProb)
|
||||
{
|
||||
const T *input_fwd_ptr = input_fwd + dest_row * num_cols;
|
||||
vec_load(frag_load_store, input_fwd_ptr + i);
|
||||
FragC frag_input_fwd = src_converter(frag_load_store);
|
||||
|
||||
for (int e = 0; e < kElementsPerAccess; e++)
|
||||
{
|
||||
accum[k] += float(frag_src.at(e) * frag_input_fwd.at(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasProb)
|
||||
{
|
||||
for (int k = 0; k < topKTile; k++)
|
||||
{
|
||||
if (k == num_topK)
|
||||
break;
|
||||
|
||||
for (int mask = 16; mask > 0; mask /= 2)
|
||||
{
|
||||
accum[k] = accum[k] + __shfl_xor_sync(0xffffffff, accum[k], mask, 32);
|
||||
}
|
||||
}
|
||||
|
||||
if (tid == 0)
|
||||
{
|
||||
for (int k = 0; k < topKTile; k++)
|
||||
{
|
||||
if (k == num_topK)
|
||||
break;
|
||||
prob_grad[source_token * num_topK + k] = accum[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Launcher (unchanged)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template <typename T, typename TCompute, bool FWD, int kElementsPerAccess>
|
||||
void moe_permute_topK_kernel_launcher(
|
||||
const T *input,
|
||||
T *output,
|
||||
const int *sorted_row_id,
|
||||
int *row_id_map,
|
||||
const float *prob,
|
||||
const int num_rows,
|
||||
const int num_topK,
|
||||
const int num_cols,
|
||||
const int num_out_tokens,
|
||||
cudaStream_t stream,
|
||||
float *prob_grad = nullptr,
|
||||
const T *input_fwd = nullptr)
|
||||
{
|
||||
if (FWD)
|
||||
{
|
||||
if (prob_grad == nullptr)
|
||||
{
|
||||
int threads = 64;
|
||||
int blocks = (num_rows * num_topK + threads - 1) / threads;
|
||||
moe_permute_topK_row_map<<<blocks, threads, 0, stream>>>(
|
||||
sorted_row_id, row_id_map, num_rows, num_topK, num_out_tokens);
|
||||
|
||||
blocks = num_rows;
|
||||
threads = std::min(num_cols / kElementsPerAccess, 1024);
|
||||
moe_permute_topK_kernel<T, TCompute, kElementsPerAccess, 128, false><<<blocks, threads, 0, stream>>>(
|
||||
input, nullptr, output, nullptr, nullptr, row_id_map,
|
||||
num_rows, num_topK, num_cols);
|
||||
}
|
||||
else
|
||||
{
|
||||
int blocks = num_rows;
|
||||
int threads = 32;
|
||||
size_t smem_bytes = num_topK * sizeof(TCompute);
|
||||
|
||||
if (num_topK == 1)
|
||||
moe_permute_topK_kernel<T, TCompute, kElementsPerAccess, 1, false><<<blocks, threads, 0, stream>>>(
|
||||
input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, num_topK, num_cols);
|
||||
else if (num_topK <= 8)
|
||||
moe_permute_topK_kernel<T, TCompute, kElementsPerAccess, 8, true><<<blocks, threads, smem_bytes, stream>>>(
|
||||
input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, num_topK, num_cols);
|
||||
else if (num_topK <= 16)
|
||||
moe_permute_topK_kernel<T, TCompute, kElementsPerAccess, 16, true><<<blocks, threads, smem_bytes, stream>>>(
|
||||
input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, num_topK, num_cols);
|
||||
else if (num_topK <= 32)
|
||||
moe_permute_topK_kernel<T, TCompute, kElementsPerAccess, 32, true><<<blocks, threads, smem_bytes, stream>>>(
|
||||
input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, num_topK, num_cols);
|
||||
else if (num_topK <= 64)
|
||||
moe_permute_topK_kernel<T, TCompute, kElementsPerAccess, 64, true><<<blocks, threads, smem_bytes, stream>>>(
|
||||
input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, num_topK, num_cols);
|
||||
else if (num_topK <= 128)
|
||||
moe_permute_topK_kernel<T, TCompute, kElementsPerAccess, 128, true><<<blocks, threads, smem_bytes, stream>>>(
|
||||
input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, num_topK, num_cols);
|
||||
else
|
||||
throw std::runtime_error("num_topK cannot exceed 128.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int blocks = num_rows;
|
||||
int threads = std::min(num_cols / kElementsPerAccess, 1024);
|
||||
size_t smem_bytes = num_topK * sizeof(TCompute);
|
||||
|
||||
if (num_topK == 1)
|
||||
moe_recover_topK_kernel<T, TCompute, kElementsPerAccess, false><<<blocks, threads, smem_bytes, stream>>>(
|
||||
input, output, row_id_map, prob, num_rows, num_topK, num_cols);
|
||||
else if (prob == nullptr)
|
||||
moe_recover_topK_kernel<T, TCompute, kElementsPerAccess, false><<<blocks, threads, smem_bytes, stream>>>(
|
||||
input, output, row_id_map, prob, num_rows, num_topK, num_cols);
|
||||
else
|
||||
moe_recover_topK_kernel<T, TCompute, kElementsPerAccess, true><<<blocks, threads, smem_bytes, stream>>>(
|
||||
input, output, row_id_map, prob, num_rows, num_topK, num_cols);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Host-side ops
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void MoePermuteTopKOp(
|
||||
const at::Tensor& input,
|
||||
const at::Tensor& indices,
|
||||
const at::Tensor& sorted_indices,
|
||||
const at::Tensor& row_id,
|
||||
const at::Tensor& sorted_row_id,
|
||||
const at::Tensor& temp_storage,
|
||||
const at::Tensor& permuted_output,
|
||||
const at::Tensor& row_id_map,
|
||||
int64_t num_out_tokens,
|
||||
int64_t max_expanded_token_num
|
||||
) {
|
||||
ASSERT_CHECK(input.size(0) == indices.size(0));
|
||||
const int num_tokens = input.size(0);
|
||||
const int num_cols = input.size(1);
|
||||
const int num_topK = indices.size(1);
|
||||
|
||||
int *indices_ptr = static_cast<int*>(indices.data_ptr());
|
||||
int *sorted_indices_ptr = static_cast<int*>(sorted_indices.data_ptr());
|
||||
int *row_id_ptr = static_cast<int*>(row_id.data_ptr());
|
||||
int *sorted_row_id_ptr = static_cast<int*>(sorted_row_id.data_ptr());
|
||||
|
||||
void *d_temp_storage = static_cast<void*>(temp_storage.data_ptr());
|
||||
size_t temp_storage_bytes = std::numeric_limits<size_t>::max();
|
||||
|
||||
cub::DeviceRadixSort::SortPairs(d_temp_storage, temp_storage_bytes,
|
||||
indices_ptr, sorted_indices_ptr,
|
||||
row_id_ptr, sorted_row_id_ptr, num_tokens * num_topK);
|
||||
|
||||
num_out_tokens = (num_out_tokens > 0) ? num_out_tokens : num_tokens * num_topK;
|
||||
int *row_id_map_ptr = static_cast<int*>(row_id_map.data_ptr());
|
||||
cudaStream_t stream = static_cast<cudaStream_t>(
|
||||
at::cuda::getCurrentCUDAStream().stream());
|
||||
|
||||
if (input.scalar_type() == at::kFloat) {
|
||||
float *input_ptr = static_cast<float*>(input.data_ptr());
|
||||
float *out_ptr = static_cast<float*>(permuted_output.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<float, float, true, 4>(
|
||||
input_ptr, out_ptr, sorted_row_id_ptr, row_id_map_ptr,
|
||||
nullptr, num_tokens, num_topK, num_cols, num_out_tokens, stream);
|
||||
} else if (input.scalar_type() == at::kHalf) {
|
||||
half_t *input_ptr = static_cast<half_t*>(input.data_ptr());
|
||||
half_t *out_ptr = static_cast<half_t*>(permuted_output.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<half_t, half_t, true, 8>(
|
||||
input_ptr, out_ptr, sorted_row_id_ptr, row_id_map_ptr,
|
||||
nullptr, num_tokens, num_topK, num_cols, num_out_tokens, stream);
|
||||
} else if (input.scalar_type() == at::kBFloat16) {
|
||||
bfloat16_t *input_ptr = static_cast<bfloat16_t*>(input.data_ptr());
|
||||
bfloat16_t *out_ptr = static_cast<bfloat16_t*>(permuted_output.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<bfloat16_t, bfloat16_t, true, 8>(
|
||||
input_ptr, out_ptr, sorted_row_id_ptr, row_id_map_ptr,
|
||||
nullptr, num_tokens, num_topK, num_cols, num_out_tokens, stream);
|
||||
#if XCOMPUTE_HAS_FP8
|
||||
} else if (input.scalar_type() == at::kFloat8_e5m2) {
|
||||
float_e5m2_t *input_ptr = static_cast<float_e5m2_t*>(input.data_ptr());
|
||||
float_e5m2_t *out_ptr = static_cast<float_e5m2_t*>(permuted_output.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<float_e5m2_t, half_t, true, 16>(
|
||||
input_ptr, out_ptr, sorted_row_id_ptr, row_id_map_ptr,
|
||||
nullptr, num_tokens, num_topK, num_cols, num_out_tokens, stream);
|
||||
} else if (input.scalar_type() == at::kFloat8_e4m3fn) {
|
||||
float_e4m3_t *input_ptr = static_cast<float_e4m3_t*>(input.data_ptr());
|
||||
float_e4m3_t *out_ptr = static_cast<float_e4m3_t*>(permuted_output.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<float_e4m3_t, half_t, true, 16>(
|
||||
input_ptr, out_ptr, sorted_row_id_ptr, row_id_map_ptr,
|
||||
nullptr, num_tokens, num_topK, num_cols, num_out_tokens, stream);
|
||||
#endif
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported data type for MoePermuteTopKOp");
|
||||
}
|
||||
}
|
||||
|
||||
void MoeRecoverTopKOp(
|
||||
const at::Tensor& input,
|
||||
const at::Tensor& row_id_map,
|
||||
const c10::optional<at::Tensor>& prob,
|
||||
const at::Tensor& unpermuted_output,
|
||||
int64_t num_tokens,
|
||||
int64_t num_topK
|
||||
) {
|
||||
const int num_cols = input.size(1);
|
||||
int *row_id_map_ptr = static_cast<int*>(row_id_map.data_ptr());
|
||||
float *prob_ptr = (prob.has_value()) ? static_cast<float*>(prob.value().data_ptr()) : nullptr;
|
||||
cudaStream_t stream = static_cast<cudaStream_t>(
|
||||
at::cuda::getCurrentCUDAStream().stream());
|
||||
|
||||
if (input.scalar_type() == at::kFloat) {
|
||||
float *in_ptr = static_cast<float*>(input.data_ptr());
|
||||
float *out_ptr = static_cast<float*>(unpermuted_output.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<float, float, false, 4>(
|
||||
in_ptr, out_ptr, nullptr, row_id_map_ptr, prob_ptr, num_tokens, num_topK, num_cols, 0, stream);
|
||||
} else if (input.scalar_type() == at::kHalf) {
|
||||
half_t *in_ptr = static_cast<half_t*>(input.data_ptr());
|
||||
half_t *out_ptr = static_cast<half_t*>(unpermuted_output.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<half_t, half_t, false, 8>(
|
||||
in_ptr, out_ptr, nullptr, row_id_map_ptr, prob_ptr, num_tokens, num_topK, num_cols, 0, stream);
|
||||
} else if (input.scalar_type() == at::kBFloat16) {
|
||||
bfloat16_t *in_ptr = static_cast<bfloat16_t*>(input.data_ptr());
|
||||
bfloat16_t *out_ptr = static_cast<bfloat16_t*>(unpermuted_output.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<bfloat16_t, bfloat16_t, false, 8>(
|
||||
in_ptr, out_ptr, nullptr, row_id_map_ptr, prob_ptr, num_tokens, num_topK, num_cols, 0, stream);
|
||||
#if XCOMPUTE_HAS_FP8
|
||||
} else if (input.scalar_type() == at::kFloat8_e5m2) {
|
||||
float_e5m2_t *in_ptr = static_cast<float_e5m2_t*>(input.data_ptr());
|
||||
float_e5m2_t *out_ptr = static_cast<float_e5m2_t*>(unpermuted_output.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<float_e5m2_t, half_t, false, 16>(
|
||||
in_ptr, out_ptr, nullptr, row_id_map_ptr, prob_ptr, num_tokens, num_topK, num_cols, 0, stream);
|
||||
} else if (input.scalar_type() == at::kFloat8_e4m3fn) {
|
||||
float_e4m3_t *in_ptr = static_cast<float_e4m3_t*>(input.data_ptr());
|
||||
float_e4m3_t *out_ptr = static_cast<float_e4m3_t*>(unpermuted_output.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<float_e4m3_t, half_t, false, 16>(
|
||||
in_ptr, out_ptr, nullptr, row_id_map_ptr, prob_ptr, num_tokens, num_topK, num_cols, 0, stream);
|
||||
#endif
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported data type for MoeRecoverTopKOp");
|
||||
}
|
||||
sync_check_cuda_error();
|
||||
}
|
||||
|
||||
void MoeRecoverTopKBwdOp(
|
||||
const at::Tensor& input_bwd,
|
||||
const at::Tensor& input_fwd,
|
||||
const at::Tensor& row_id_map,
|
||||
const at::Tensor& prob,
|
||||
const at::Tensor& act_grad,
|
||||
const at::Tensor& prob_grad
|
||||
) {
|
||||
const int num_tokens = prob.size(0);
|
||||
const int num_topK = prob.size(1);
|
||||
const int num_cols = input_bwd.size(1);
|
||||
int *row_id_map_ptr = static_cast<int*>(row_id_map.data_ptr());
|
||||
float *prob_ptr = static_cast<float*>(prob.data_ptr());
|
||||
float *prob_grad_ptr = static_cast<float*>(prob_grad.data_ptr());
|
||||
cudaStream_t stream = static_cast<cudaStream_t>(
|
||||
at::cuda::getCurrentCUDAStream().stream());
|
||||
|
||||
if (input_bwd.scalar_type() == at::kFloat) {
|
||||
float *bwd = static_cast<float*>(input_bwd.data_ptr());
|
||||
float *fwd = static_cast<float*>(input_fwd.data_ptr());
|
||||
float *grad = static_cast<float*>(act_grad.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<float, float, true, 4>(
|
||||
bwd, grad, nullptr, row_id_map_ptr, prob_ptr, num_tokens, num_topK, num_cols, 0, stream, prob_grad_ptr, fwd);
|
||||
} else if (input_bwd.scalar_type() == at::kHalf) {
|
||||
half_t *bwd = static_cast<half_t*>(input_bwd.data_ptr());
|
||||
half_t *fwd = static_cast<half_t*>(input_fwd.data_ptr());
|
||||
half_t *grad = static_cast<half_t*>(act_grad.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<half_t, half_t, true, 8>(
|
||||
bwd, grad, nullptr, row_id_map_ptr, prob_ptr, num_tokens, num_topK, num_cols, 0, stream, prob_grad_ptr, fwd);
|
||||
} else if (input_bwd.scalar_type() == at::kBFloat16) {
|
||||
bfloat16_t *bwd = static_cast<bfloat16_t*>(input_bwd.data_ptr());
|
||||
bfloat16_t *fwd = static_cast<bfloat16_t*>(input_fwd.data_ptr());
|
||||
bfloat16_t *grad = static_cast<bfloat16_t*>(act_grad.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<bfloat16_t, bfloat16_t, true, 8>(
|
||||
bwd, grad, nullptr, row_id_map_ptr, prob_ptr, num_tokens, num_topK, num_cols, 0, stream, prob_grad_ptr, fwd);
|
||||
#if XCOMPUTE_HAS_FP8
|
||||
} else if (input_bwd.scalar_type() == at::kFloat8_e5m2) {
|
||||
float_e5m2_t *bwd = static_cast<float_e5m2_t*>(input_bwd.data_ptr());
|
||||
float_e5m2_t *fwd = static_cast<float_e5m2_t*>(input_fwd.data_ptr());
|
||||
float_e5m2_t *grad = static_cast<float_e5m2_t*>(act_grad.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<float_e5m2_t, half_t, true, 16>(
|
||||
bwd, grad, nullptr, row_id_map_ptr, prob_ptr, num_tokens, num_topK, num_cols, 0, stream, prob_grad_ptr, fwd);
|
||||
} else if (input_bwd.scalar_type() == at::kFloat8_e4m3fn) {
|
||||
float_e4m3_t *bwd = static_cast<float_e4m3_t*>(input_bwd.data_ptr());
|
||||
float_e4m3_t *fwd = static_cast<float_e4m3_t*>(input_fwd.data_ptr());
|
||||
float_e4m3_t *grad = static_cast<float_e4m3_t*>(act_grad.data_ptr());
|
||||
moe_permute_topK_kernel_launcher<float_e4m3_t, half_t, true, 16>(
|
||||
bwd, grad, nullptr, row_id_map_ptr, prob_ptr, num_tokens, num_topK, num_cols, 0, stream, prob_grad_ptr, fwd);
|
||||
#endif
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported data type for MoeRecoverTopKBwdOp");
|
||||
}
|
||||
sync_check_cuda_error();
|
||||
}
|
||||
|
||||
size_t CubSortPairGetStorageBytes(int64_t num_items){
|
||||
size_t temp_storage_bytes = 0;
|
||||
int *temp_ptr = nullptr;
|
||||
cub::DeviceRadixSort::SortPairs(nullptr, temp_storage_bytes,
|
||||
temp_ptr, temp_ptr,
|
||||
temp_ptr, temp_ptr, num_items);
|
||||
return temp_storage_bytes;
|
||||
}
|
||||
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
|
||||
} // wallx_cuda_permute
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
||||
#include <torch/extension.h>
|
||||
#include <c10/cuda/CUDAStream.h>
|
||||
|
||||
#include <cuda_fp16.h>
|
||||
#include <cub/cub.cuh>
|
||||
#include <cassert>
|
||||
#include "../common/cuda_utils.h"
|
||||
|
||||
namespace wallx_cuda_rot_pos {
|
||||
|
||||
__global__ void fused_rot_pos_emb_kernel_int32(
|
||||
const float *__restrict__ inv_freq, // [dim/2] - precomputed inverse frequencies
|
||||
const int32_t *__restrict__ grid_thw, // [num_grids, 3] - (t, h, w) for each grid
|
||||
float *__restrict__ output, // [total_tokens, dim] - output rotary embeddings
|
||||
const int32_t *__restrict__ cumsum_tokens, // [num_grids+1] - cumulative sum of tokens per grid
|
||||
const int dim_half, // dim/2 (size of inv_freq)
|
||||
const int spatial_merge_size, // spatial merge size
|
||||
const int num_grids // number of grids
|
||||
)
|
||||
{
|
||||
const int32_t tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const int32_t total_tokens = cumsum_tokens[num_grids];
|
||||
|
||||
extern __shared__ float freq_smem[];
|
||||
for (int i = threadIdx.x; i < dim_half; i += blockDim.x) {
|
||||
freq_smem[i] = inv_freq[i];
|
||||
}
|
||||
if (tid >= total_tokens * dim_half)
|
||||
return;
|
||||
|
||||
const int32_t token_idx = tid / dim_half;
|
||||
const int freq_idx = tid % dim_half;
|
||||
|
||||
// Find which grid this token belongs to
|
||||
int grid_idx = 0;
|
||||
int32_t local_token_idx = token_idx;
|
||||
for (int g = 0; g < num_grids; g++)
|
||||
{
|
||||
if (token_idx < cumsum_tokens[g + 1])
|
||||
{
|
||||
grid_idx = g;
|
||||
local_token_idx = token_idx - cumsum_tokens[g];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get grid dimensions
|
||||
const int32_t h = grid_thw[grid_idx * 3 + 1];
|
||||
const int32_t w = grid_thw[grid_idx * 3 + 2];
|
||||
|
||||
// Calculate spatial dimensions after merging
|
||||
const int32_t h_merged = h / spatial_merge_size;
|
||||
const int32_t w_merged = w / spatial_merge_size;
|
||||
const int32_t spatial_tokens = h_merged * w_merged * spatial_merge_size * spatial_merge_size;
|
||||
|
||||
// Get spatial index
|
||||
const int32_t spatial_idx = local_token_idx % spatial_tokens;
|
||||
|
||||
// Decompose spatial index to get merged block and position within block
|
||||
const int32_t tokens_per_block = spatial_merge_size * spatial_merge_size;
|
||||
const int32_t block_idx = spatial_idx / tokens_per_block;
|
||||
const int32_t within_block_idx = spatial_idx % tokens_per_block;
|
||||
|
||||
// Get block coordinates in merged grid
|
||||
const int32_t block_h = block_idx / w_merged;
|
||||
const int32_t block_w = block_idx % w_merged;
|
||||
|
||||
// Get position within block
|
||||
const int32_t within_h = within_block_idx / spatial_merge_size;
|
||||
const int32_t within_w = within_block_idx % spatial_merge_size;
|
||||
|
||||
// Calculate actual h and w positions
|
||||
const int32_t h_pos = block_h * spatial_merge_size + within_h;
|
||||
const int32_t w_pos = block_w * spatial_merge_size + within_w;
|
||||
|
||||
// Compute rotary embedding
|
||||
float freq_val = freq_smem[freq_idx];
|
||||
|
||||
// Output has shape [total_tokens, dim] where dim = 2 * dim_half
|
||||
int32_t out_idx = token_idx * dim_half * 2 + freq_idx;
|
||||
output[out_idx] = h_pos * freq_val; // h_pos frequencies
|
||||
output[out_idx + dim_half] = w_pos * freq_val; // w_pos frequencies
|
||||
}
|
||||
|
||||
__global__ void fused_rot_pos_emb_kernel_int64(
|
||||
const float *__restrict__ inv_freq, // [dim/2] - precomputed inverse frequencies
|
||||
const int64_t *__restrict__ grid_thw, // [num_grids, 3] - (t, h, w) for each grid
|
||||
float *__restrict__ output, // [total_tokens, dim] - output rotary embeddings
|
||||
const int64_t *__restrict__ cumsum_tokens, // [num_grids+1] - cumulative sum of tokens per grid
|
||||
const int dim_half, // dim/2 (size of inv_freq)
|
||||
const int spatial_merge_size, // spatial merge size
|
||||
const int num_grids // number of grids
|
||||
)
|
||||
{
|
||||
const int64_t tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const int64_t total_tokens = cumsum_tokens[num_grids];
|
||||
|
||||
extern __shared__ float freq_smem[];
|
||||
for (int i = threadIdx.x; i < dim_half; i += blockDim.x) {
|
||||
freq_smem[i] = inv_freq[i];
|
||||
}
|
||||
|
||||
if (tid >= total_tokens * dim_half)
|
||||
return;
|
||||
|
||||
const int64_t token_idx = tid / dim_half;
|
||||
const int freq_idx = tid % dim_half;
|
||||
|
||||
// Find which grid this token belongs to
|
||||
int grid_idx = 0;
|
||||
int64_t local_token_idx = token_idx;
|
||||
for (int g = 0; g < num_grids; g++)
|
||||
{
|
||||
if (token_idx < cumsum_tokens[g + 1])
|
||||
{
|
||||
grid_idx = g;
|
||||
local_token_idx = token_idx - cumsum_tokens[g];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get grid dimensions
|
||||
const int64_t h = grid_thw[grid_idx * 3 + 1];
|
||||
const int64_t w = grid_thw[grid_idx * 3 + 2];
|
||||
|
||||
// Calculate spatial dimensions after merging
|
||||
const int64_t h_merged = h / spatial_merge_size;
|
||||
const int64_t w_merged = w / spatial_merge_size;
|
||||
const int64_t spatial_tokens = h_merged * w_merged * spatial_merge_size * spatial_merge_size;
|
||||
|
||||
// Get spatial index
|
||||
const int64_t spatial_idx = local_token_idx % spatial_tokens;
|
||||
|
||||
// Decompose spatial index to get merged block and position within block
|
||||
const int64_t tokens_per_block = spatial_merge_size * spatial_merge_size;
|
||||
const int64_t block_idx = spatial_idx / tokens_per_block;
|
||||
const int64_t within_block_idx = spatial_idx % tokens_per_block;
|
||||
|
||||
// Get block coordinates in merged grid
|
||||
const int64_t block_h = block_idx / w_merged;
|
||||
const int64_t block_w = block_idx % w_merged;
|
||||
|
||||
// Get position within block
|
||||
const int64_t within_h = within_block_idx / spatial_merge_size;
|
||||
const int64_t within_w = within_block_idx % spatial_merge_size;
|
||||
|
||||
// Calculate actual h and w positions
|
||||
const int64_t h_pos = block_h * spatial_merge_size + within_h;
|
||||
const int64_t w_pos = block_w * spatial_merge_size + within_w;
|
||||
|
||||
// Compute rotary embedding
|
||||
float freq_val = freq_smem[freq_idx];
|
||||
|
||||
// Output has shape [total_tokens, dim] where dim = 2 * dim_half
|
||||
int64_t out_idx = token_idx * dim_half * 2 + freq_idx;
|
||||
output[out_idx] = h_pos * freq_val; // h_pos frequencies
|
||||
output[out_idx + dim_half] = w_pos * freq_val; // w_pos frequencies
|
||||
}
|
||||
|
||||
__global__ void compute_token_counts_kernel_int32(
|
||||
const int32_t *__restrict__ grid_thw,
|
||||
int32_t *__restrict__ token_counts,
|
||||
const int spatial_merge_size,
|
||||
const int num_grids)
|
||||
{
|
||||
const int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (idx >= num_grids)
|
||||
return;
|
||||
|
||||
int32_t t = grid_thw[idx * 3 + 0];
|
||||
int32_t h = grid_thw[idx * 3 + 1];
|
||||
int32_t w = grid_thw[idx * 3 + 2];
|
||||
int32_t h_merged = h / spatial_merge_size;
|
||||
int32_t w_merged = w / spatial_merge_size;
|
||||
token_counts[idx] = t * h_merged * w_merged * spatial_merge_size * spatial_merge_size;
|
||||
}
|
||||
|
||||
__global__ void compute_token_counts_kernel_int64(
|
||||
const int64_t *__restrict__ grid_thw,
|
||||
int64_t *__restrict__ token_counts,
|
||||
const int spatial_merge_size,
|
||||
const int num_grids)
|
||||
{
|
||||
const int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (idx >= num_grids)
|
||||
return;
|
||||
|
||||
int64_t t = grid_thw[idx * 3 + 0];
|
||||
int64_t h = grid_thw[idx * 3 + 1];
|
||||
int64_t w = grid_thw[idx * 3 + 2];
|
||||
int64_t h_merged = h / spatial_merge_size;
|
||||
int64_t w_merged = w / spatial_merge_size;
|
||||
token_counts[idx] = t * h_merged * w_merged * spatial_merge_size * spatial_merge_size;
|
||||
}
|
||||
|
||||
void GetTokenCounts(
|
||||
const at::Tensor& grid_thw, // [num_grids, 3]
|
||||
const at::Tensor& token_counts, // [num_grids]
|
||||
int spatial_merge_size
|
||||
) {
|
||||
ASSERT_CHECK(grid_thw.dim() == 2);
|
||||
ASSERT_CHECK(grid_thw.size(1) == 3);
|
||||
ASSERT_CHECK(grid_thw.size(0) == token_counts.size(0));
|
||||
|
||||
ASSERT_CHECK(spatial_merge_size > 0);
|
||||
|
||||
const int num_grids = grid_thw.size(0);
|
||||
|
||||
const int threads = 256;
|
||||
const int blocks = (num_grids + threads - 1) / threads;
|
||||
|
||||
cudaStream_t stream = static_cast<cudaStream_t>(
|
||||
at::cuda::getCurrentCUDAStream().stream());
|
||||
|
||||
if (grid_thw.scalar_type() == at::kInt) {
|
||||
compute_token_counts_kernel_int32<<<blocks, threads, 0, stream>>>(
|
||||
static_cast<int32_t*>(grid_thw.data_ptr()),
|
||||
static_cast<int32_t*>(token_counts.data_ptr()),
|
||||
spatial_merge_size,
|
||||
num_grids);
|
||||
} else if (grid_thw.scalar_type() == at::kLong) {
|
||||
compute_token_counts_kernel_int64<<<blocks, threads, 0, stream>>>(
|
||||
static_cast<int64_t*>(grid_thw.data_ptr()),
|
||||
static_cast<int64_t*>(token_counts.data_ptr()),
|
||||
spatial_merge_size,
|
||||
num_grids);
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported data type for GetTokenCounts");
|
||||
}
|
||||
|
||||
sync_check_cuda_error();
|
||||
}
|
||||
|
||||
void RotPosEmb(
|
||||
const at::Tensor& inv_freq, // [dim/2]
|
||||
const at::Tensor& grid_thw, // [num_grids, 3]
|
||||
const at::Tensor& output,
|
||||
const at::Tensor& cumsum_tokens,
|
||||
int spatial_merge_size
|
||||
) {
|
||||
ASSERT_CHECK(output.size(0) > 0);
|
||||
ASSERT_CHECK(inv_freq.dim() == 1);
|
||||
ASSERT_CHECK(inv_freq.scalar_type() == at::kFloat);
|
||||
|
||||
const int dim_half = inv_freq.size(0);
|
||||
const int num_grids = grid_thw.size(0);
|
||||
const int total_tokens = output.size(0);
|
||||
|
||||
const int64_t num_elements = total_tokens * dim_half;
|
||||
|
||||
const int threads_per_block = 256;
|
||||
const int num_blocks = static_cast<int>((num_elements + threads_per_block - 1) / threads_per_block);
|
||||
cudaStream_t stream = static_cast<cudaStream_t>(
|
||||
at::cuda::getCurrentCUDAStream().stream());
|
||||
|
||||
if (grid_thw.scalar_type() == at::kInt) {
|
||||
fused_rot_pos_emb_kernel_int32<<<num_blocks, threads_per_block, dim_half * sizeof(float), stream>>>(
|
||||
static_cast<float*>(inv_freq.data_ptr()),
|
||||
static_cast<int32_t*>(grid_thw.data_ptr()),
|
||||
static_cast<float*>(output.data_ptr()),
|
||||
static_cast<int32_t*>(cumsum_tokens.data_ptr()),
|
||||
dim_half,
|
||||
spatial_merge_size,
|
||||
num_grids);
|
||||
} else if (grid_thw.scalar_type() == at::kLong) {
|
||||
fused_rot_pos_emb_kernel_int64<<<num_blocks, threads_per_block, dim_half * sizeof(float), stream>>>(
|
||||
static_cast<float*>(inv_freq.data_ptr()),
|
||||
static_cast<int64_t*>(grid_thw.data_ptr()),
|
||||
static_cast<float*>(output.data_ptr()),
|
||||
static_cast<int64_t*>(cumsum_tokens.data_ptr()),
|
||||
dim_half,
|
||||
spatial_merge_size,
|
||||
num_grids);
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported data type for RotPosEmb");
|
||||
}
|
||||
|
||||
sync_check_cuda_error();
|
||||
}
|
||||
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
|
||||
} // wallx_cuda_rot_pos
|
||||
@@ -0,0 +1,284 @@
|
||||
#include <torch/extension.h>
|
||||
|
||||
#include <cuda_fp16.h>
|
||||
#include <cassert>
|
||||
#include "../common/cuda_utils.h"
|
||||
|
||||
namespace wallx_cuda_window_index {
|
||||
|
||||
__global__ void compute_metadata(
|
||||
const int *grid_thw, // [num_grids, 3]
|
||||
int *grid_info, // [num_grids, 6]: [grid_elements, grid_windows, llm_h, llm_w, num_windows_h, num_windows_w]
|
||||
int *global_totals, // [total_elements, total_windows]
|
||||
int num_grids,
|
||||
int spatial_merge_size,
|
||||
int vit_merger_window_size)
|
||||
{
|
||||
int grid_idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (grid_idx >= num_grids)
|
||||
return;
|
||||
|
||||
int grid_t = grid_thw[grid_idx * 3 + 0];
|
||||
int grid_h = grid_thw[grid_idx * 3 + 1];
|
||||
int grid_w = grid_thw[grid_idx * 3 + 2];
|
||||
|
||||
int llm_h = grid_h / spatial_merge_size;
|
||||
int llm_w = grid_w / spatial_merge_size;
|
||||
|
||||
int pad_h = (vit_merger_window_size - llm_h % vit_merger_window_size) % vit_merger_window_size;
|
||||
int pad_w = (vit_merger_window_size - llm_w % vit_merger_window_size) % vit_merger_window_size;
|
||||
|
||||
int num_windows_h = (llm_h + pad_h) / vit_merger_window_size;
|
||||
int num_windows_w = (llm_w + pad_w) / vit_merger_window_size;
|
||||
|
||||
int grid_elements = grid_t * llm_h * llm_w;
|
||||
int grid_windows = grid_t * num_windows_h * num_windows_w;
|
||||
|
||||
grid_info[grid_idx * 6 + 0] = grid_elements;
|
||||
grid_info[grid_idx * 6 + 1] = grid_windows;
|
||||
grid_info[grid_idx * 6 + 2] = llm_h;
|
||||
grid_info[grid_idx * 6 + 3] = llm_w;
|
||||
grid_info[grid_idx * 6 + 4] = num_windows_h;
|
||||
grid_info[grid_idx * 6 + 5] = num_windows_w;
|
||||
|
||||
atomicAdd(&global_totals[0], grid_elements);
|
||||
atomicAdd(&global_totals[1], grid_windows);
|
||||
}
|
||||
|
||||
__global__ void compute_window_counts(
|
||||
const int *grid_thw,
|
||||
const int *grid_info,
|
||||
int *window_counts,
|
||||
int vit_merger_window_size,
|
||||
int spatial_merge_unit,
|
||||
int num_grids)
|
||||
{
|
||||
int grid_idx = blockIdx.y;
|
||||
int t_idx = blockIdx.x;
|
||||
|
||||
if (grid_idx >= num_grids)
|
||||
return;
|
||||
|
||||
int grid_t = grid_thw[grid_idx * 3 + 0];
|
||||
if (t_idx >= grid_t)
|
||||
return;
|
||||
|
||||
int llm_h = grid_info[grid_idx * 6 + 2];
|
||||
int llm_w = grid_info[grid_idx * 6 + 3];
|
||||
int num_windows_h = grid_info[grid_idx * 6 + 4];
|
||||
int num_windows_w = grid_info[grid_idx * 6 + 5];
|
||||
|
||||
int window_base = 0;
|
||||
for (int g = 0; g < grid_idx; g++)
|
||||
{
|
||||
window_base += grid_info[g * 6 + 1];
|
||||
}
|
||||
|
||||
int t_window_base = window_base + t_idx * num_windows_h * num_windows_w;
|
||||
|
||||
int thread_id = threadIdx.x;
|
||||
int warp_id = thread_id / 32;
|
||||
int lane_id = thread_id % 32;
|
||||
|
||||
int windows_per_t = num_windows_h * num_windows_w;
|
||||
int warps_per_block = blockDim.x / 32;
|
||||
|
||||
if (lane_id == 0)
|
||||
{
|
||||
for (int window_idx = warp_id; window_idx < windows_per_t; window_idx += warps_per_block)
|
||||
{
|
||||
int win_h = window_idx / num_windows_w;
|
||||
int win_w = window_idx % num_windows_w;
|
||||
|
||||
int start_h = win_h * vit_merger_window_size;
|
||||
int start_w = win_w * vit_merger_window_size;
|
||||
|
||||
int valid_h = min(vit_merger_window_size, llm_h - start_h);
|
||||
int valid_w = min(vit_merger_window_size, llm_w - start_w);
|
||||
|
||||
int valid_count = (valid_h > 0 && valid_w > 0) ? valid_h * valid_w : 0;
|
||||
|
||||
window_counts[t_window_base + window_idx] = valid_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void compute_cu_window_seqlens(
|
||||
const int *window_counts,
|
||||
int *cu_window_seqlens, // [total_windows + 1]
|
||||
int total_windows,
|
||||
int spatial_merge_unit
|
||||
)
|
||||
{
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (tid == 0)
|
||||
{
|
||||
cu_window_seqlens[0] = 0;
|
||||
}
|
||||
|
||||
if (tid < total_windows)
|
||||
{
|
||||
cu_window_seqlens[tid + 1] = window_counts[tid] * spatial_merge_unit;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (tid == 0)
|
||||
{
|
||||
for (int i = 1; i <= total_windows; i++)
|
||||
{
|
||||
cu_window_seqlens[i] += cu_window_seqlens[i - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void generate_window_indices(
|
||||
const int *grid_thw,
|
||||
const int *grid_info,
|
||||
const int *cu_window_seqlens,
|
||||
int *window_indices,
|
||||
int vit_merger_window_size,
|
||||
int spatial_merge_unit,
|
||||
int num_grids)
|
||||
{
|
||||
int grid_idx = blockIdx.y;
|
||||
int t_idx = blockIdx.x;
|
||||
|
||||
if (grid_idx >= num_grids)
|
||||
return;
|
||||
|
||||
int grid_t = grid_thw[grid_idx * 3 + 0];
|
||||
if (t_idx >= grid_t)
|
||||
return;
|
||||
|
||||
int llm_h = grid_info[grid_idx * 6 + 2];
|
||||
int llm_w = grid_info[grid_idx * 6 + 3];
|
||||
int num_windows_h = grid_info[grid_idx * 6 + 4];
|
||||
int num_windows_w = grid_info[grid_idx * 6 + 5];
|
||||
|
||||
int element_base = 0;
|
||||
for (int g = 0; g < grid_idx; g++)
|
||||
{
|
||||
element_base += grid_info[g * 6 + 0];
|
||||
}
|
||||
int t_element_base = element_base + t_idx * llm_h * llm_w;
|
||||
|
||||
int window_base = 0;
|
||||
for (int g = 0; g < grid_idx; g++)
|
||||
{
|
||||
window_base += grid_info[g * 6 + 1];
|
||||
}
|
||||
int t_window_base = window_base + t_idx * num_windows_h * num_windows_w;
|
||||
|
||||
int thread_id = threadIdx.x;
|
||||
int warp_id = thread_id / 32;
|
||||
int lane_id = thread_id % 32;
|
||||
|
||||
int windows_per_t = num_windows_h * num_windows_w;
|
||||
int warps_per_block = blockDim.x / 32;
|
||||
|
||||
for (int window_idx = warp_id; window_idx < windows_per_t; window_idx += warps_per_block)
|
||||
{
|
||||
int win_h = window_idx / num_windows_w;
|
||||
int win_w = window_idx % num_windows_w;
|
||||
|
||||
int global_window_idx = t_window_base + window_idx;
|
||||
int output_offset = cu_window_seqlens[global_window_idx] / spatial_merge_unit;
|
||||
|
||||
int start_h = win_h * vit_merger_window_size;
|
||||
int start_w = win_w * vit_merger_window_size;
|
||||
|
||||
int valid_h = min(vit_merger_window_size, llm_h - start_h);
|
||||
int valid_w = min(vit_merger_window_size, llm_w - start_w);
|
||||
|
||||
for (int elem_idx = lane_id; elem_idx < valid_h * valid_w; elem_idx += 32)
|
||||
{
|
||||
int local_h = elem_idx / valid_w;
|
||||
int local_w = elem_idx % valid_w;
|
||||
|
||||
int abs_h = start_h + local_h;
|
||||
int abs_w = start_w + local_w;
|
||||
|
||||
int value = t_element_base + abs_h * llm_w + abs_w;
|
||||
|
||||
int base_offset = output_offset + elem_idx;
|
||||
window_indices[base_offset] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetTotals(
|
||||
const at::Tensor& grid_thw,
|
||||
const at::Tensor& grid_info_tensor,
|
||||
const at::Tensor& global_totals_tensor,
|
||||
int spatial_merge_size,
|
||||
int vit_merger_window_size
|
||||
) {
|
||||
ASSERT_CHECK(grid_thw.dim() == 2 && grid_thw.size(1) == 3);
|
||||
ASSERT_CHECK(grid_thw.scalar_type() == at::kInt);
|
||||
|
||||
int num_grids = grid_thw.size(0);
|
||||
const int *d_grid_thw = static_cast<int32_t*>(grid_thw.data_ptr());
|
||||
|
||||
int *d_grid_info = static_cast<int32_t*>(grid_info_tensor.data_ptr());
|
||||
int *d_global_totals = static_cast<int32_t*>(global_totals_tensor.data_ptr());
|
||||
|
||||
int threads1 = 256;
|
||||
int blocks1 = (num_grids + threads1 - 1) / threads1;
|
||||
|
||||
compute_metadata<<<blocks1, threads1>>>(
|
||||
d_grid_thw, d_grid_info, d_global_totals,
|
||||
num_grids, spatial_merge_size, vit_merger_window_size);
|
||||
|
||||
sync_check_cuda_error();
|
||||
}
|
||||
|
||||
void GetWindowIndex(
|
||||
const at::Tensor& grid_thw,
|
||||
const at::Tensor& grid_info_tensor,
|
||||
const at::Tensor& window_indices,
|
||||
const at::Tensor& cu_window_seqlens,
|
||||
const at::Tensor& window_counts_tensor,
|
||||
int max_grid_t,
|
||||
int spatial_merge_size,
|
||||
int vit_merger_window_size,
|
||||
int patch_size,
|
||||
int spatial_merge_unit) {
|
||||
|
||||
int num_grids = grid_thw.size(0);
|
||||
|
||||
dim3 blocks2(max_grid_t, num_grids);
|
||||
dim3 threads2(256);
|
||||
|
||||
const int *d_grid_thw = static_cast<int32_t*>(grid_thw.data_ptr());
|
||||
|
||||
int *d_grid_info = static_cast<int32_t*>(grid_info_tensor.data_ptr());
|
||||
|
||||
int *d_window_indices = static_cast<int32_t*>(window_indices.data_ptr());
|
||||
int *d_cu_window_seqlens = static_cast<int32_t*>(cu_window_seqlens.data_ptr());
|
||||
|
||||
int *d_window_counts = static_cast<int32_t*>(window_counts_tensor.data_ptr());
|
||||
|
||||
compute_window_counts<<<blocks2, threads2>>>(
|
||||
d_grid_thw, d_grid_info, d_window_counts,
|
||||
vit_merger_window_size, spatial_merge_unit, num_grids);
|
||||
|
||||
int total_windows = window_counts_tensor.size(0);
|
||||
|
||||
int threads4 = 256;
|
||||
int blocks4 = (total_windows + threads4 - 1) / threads4;
|
||||
compute_cu_window_seqlens<<<blocks4, threads4>>>(
|
||||
d_window_counts, d_cu_window_seqlens, total_windows, spatial_merge_unit);
|
||||
|
||||
generate_window_indices<<<blocks2, threads2>>>(
|
||||
d_grid_thw, d_grid_info, d_cu_window_seqlens, d_window_indices,
|
||||
vit_merger_window_size, spatial_merge_unit, num_grids);
|
||||
|
||||
sync_check_cuda_error();
|
||||
}
|
||||
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
// Removed TVM FFI export (see pybind11 registration)
|
||||
|
||||
} // wallx_cuda_window_index
|
||||
Reference in New Issue
Block a user