Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
292 changes: 265 additions & 27 deletions src/kernels.cu
Original file line number Diff line number Diff line change
@@ -1,8 +1,81 @@
#include <vector>
#include <cstddef>
#include <cuda_fp16.h>
#include <vector>

#include "../tester/utils.h"

// =====================================================================
// CUDA 核函数辅助类型转换工具 (确保同时完美兼容 float 和 half)
// =====================================================================
template <typename T> __device__ __forceinline__ float to_float(T val) {
return static_cast<float>(val);
}

template <> __device__ __forceinline__ float to_float<half>(half val) {
return __half2float(val);
}

template <typename T> __device__ __forceinline__ T from_float(float val) {
return static_cast<T>(val);
}

template <> __device__ __forceinline__ half from_float<half>(float val) {
return __float2half(val);
}

// =====================================================================
// RMSNorm CUDA Kernel 实现
// =====================================================================
template <typename T>
__global__ void rmsNormKernel(const T *input, const T *weight, T *output,
size_t rows, size_t hidden_dim, float eps) {
// 每个 Block 负责处理矩阵中的一个 Token (一行)
size_t i = blockIdx.x;
if (i >= rows)
return;

// 定位当前行的起始指针
const T *row_input = input + i * hidden_dim;
T *row_output = output + i * hidden_dim;

// 动态共享内存,用于 Block 内部线程协同求和 (大小由启动时的第三个参数决定)
extern __shared__ float sdata[];
size_t tid = threadIdx.x;

// 1. 每个线程并行计算自己分到的那一批元素的平方和
float thread_sum = 0.0f;
for (size_t j = tid; j < hidden_dim; j += blockDim.x) {
float val = to_float(row_input[j]);
thread_sum += val * val;
}
sdata[tid] = thread_sum;
__syncthreads(); // 等待全块线程完成局部平方和写入

// 2. 块内折半规约 (Block Reduction):将所有线程的和累加到 sdata[0]
// 保证 blockDim.x 是 2 的幂次(这里固定为 256),此逻辑绝对安全
for (size_t s = blockDim.x / 2; s > 0; s >>= 1) {
if (tid < s) {
sdata[tid] += sdata[tid + s];
}
__syncthreads();
}

// 3. 由 0 号线程算出这一行的 rsqrt 值,并共享给全块
__shared__ float rsqrt_val;
if (tid == 0) {
float mean_square = sdata[0] / hidden_dim;
rsqrt_val = rsqrtf(mean_square + eps); // 使用 CUDA 硬件加速的 rsqrtf 指令
}
__syncthreads(); // 等待 rsqrt_val 计算并同步完毕

// 4. 所有线程再次并行,计算当前行每个元素的最终缩放值并写回
for (size_t j = tid; j < hidden_dim; j += blockDim.x) {
float val = to_float(row_input[j]);
float w = to_float(weight[j]);
row_output[j] = from_float<T>(val * rsqrt_val * w);
}
}

/**
* @brief Computes RMSNorm over the last dimension of a 2D tensor.
*
Expand All @@ -22,47 +95,212 @@
* @param[in] eps Numerical stability epsilon.
*/
template <typename T>
void rmsNorm(const std::vector<T>& h_input, const std::vector<T>& h_weight,
std::vector<T>& h_output, size_t rows, size_t hidden_dim,
float eps) {
// TODO: Implement the rmsNorm function
void rmsNorm(const std::vector<T> &h_input, const std::vector<T> &h_weight,
std::vector<T> &h_output, size_t rows, size_t hidden_dim,
float eps) {
// 1. 定义 Device 端的裸指针
T *d_input = nullptr;
T *d_weight = nullptr;
T *d_output = nullptr;

size_t input_size = rows * hidden_dim * sizeof(T);
size_t weight_size = hidden_dim * sizeof(T);

// 2. 分配 GPU 显存
cudaMalloc(&d_input, input_size);
cudaMalloc(&d_weight, weight_size);
cudaMalloc(&d_output, input_size);

// 3. 将数据从 Host (CPU) 拷贝到 Device (GPU)
cudaMemcpy(d_input, h_input.data(), input_size, cudaMemcpyHostToDevice);
cudaMemcpy(d_weight, h_weight.data(), weight_size, cudaMemcpyHostToDevice);

// 4. 配置配置网格和线程块尺寸
// 固定使用 256 线程,它是 2 的幂次,能完美支持 Kernel 内部的折半规约
unsigned int threads_per_block = 256;
unsigned int blocks_per_grid = rows; // 有多少行就启动多少个 Block
size_t shared_mem_size = threads_per_block * sizeof(float);

// 5. 启动 CUDA Kernel
rmsNormKernel<T><<<blocks_per_grid, threads_per_block, shared_mem_size>>>(
d_input, d_weight, d_output, rows, hidden_dim, eps);

// 6. 将计算结果从 GPU 捞回预先分配好的 h_output 中
cudaMemcpy(h_output.data(), d_output, input_size, cudaMemcpyDeviceToHost);

// 7. 善后处理:释放显存防止内存泄漏
cudaFree(d_input);
cudaFree(d_weight);
cudaFree(d_output);
}

// =====================================================================
// Falsh Attention CUDA Kernel 实现
// =====================================================================
template <typename T>
__global__ void flashAttentionKernel(const T *q, const T *k, const T *v, T *o,
int tgt_len, int src_len, int q_heads,
int kv_heads, int d, bool is_causal) {
extern __shared__ float smem[]; // size = src_len + blockDim.x
float *s_score = smem;
float *red = smem + src_len;

int b = blockIdx.x;
int t = blockIdx.y;
int h = blockIdx.z;
int hkv = h / (q_heads / kv_heads); // GQA 分组查询
float scale = 1.0f / sqrtf((float)d);
int tid = threadIdx.x, nthreads = blockDim.x;

const T *q_row = q + (((size_t)b * tgt_len + t) * q_heads + h) * d;
T *o_row = o + (((size_t)b * tgt_len + t) * q_heads + h) * d;

// ---- 阶段 A: 计算 s_j = dot(q, k_j) * scale 并存 Shared Memory ----
for (int j = tid; j < src_len; j += nthreads) {
if (is_causal && j > t) {
s_score[j] = -INFINITY;
continue;
}
const T *k_row = k + (((size_t)b * src_len + j) * kv_heads + hkv) * d;
float dot = 0.f;
for (int dd = 0; dd < d; dd++)
dot += to_float(q_row[dd]) * to_float(k_row[dd]);
s_score[j] = dot * scale;
}
__syncthreads();

// ---- 阶段 B: 求 max(s_j) ----
float local_max = -INFINITY;
for (int j = tid; j < src_len; j += nthreads)
local_max = fmaxf(local_max, s_score[j]);
red[tid] = local_max;
__syncthreads();

for (int s = nthreads / 2; s > 0; s >>= 1) {
if (tid < s)
red[tid] = fmaxf(red[tid], red[tid + s]);
__syncthreads();
}
float m = red[0];
__syncthreads();

// ---- 阶段 C: 重新计算 dot,保证 expf(dot * scale - m) 的 FMA 指令融合精度
// ----
for (int j = tid; j < src_len; j += nthreads) {
if (is_causal && j > t) {
s_score[j] = 0.f;
continue;
}
const T *k_row = k + (((size_t)b * src_len + j) * kv_heads + hkv) * d;
float dot = 0.f;
for (int dd = 0; dd < d; dd++)
dot += to_float(q_row[dd]) * to_float(k_row[dd]);
s_score[j] = expf(dot * scale - m);
}
__syncthreads();

// ---- 阶段 C.2: 由 0 号线程按 j 升序单线程串行求和 l (严格与 CPU
// 参考实现的结合树一致) ----
if (tid == 0) {
float l_seq = 0.f;
for (int j = 0; j < src_len; j++)
l_seq += s_score[j];
red[0] = l_seq;
}
__syncthreads();
float l = red[0];
__syncthreads();

// ---- 阶段 D: 线程按 d 通道分工, o[d] = Σ_j p_j * v[j][d] / l ----
for (int dd = threadIdx.x; dd < d; dd += blockDim.x) {
float acc = 0.f;
for (int j = 0; j < src_len; j++) {
const T *v_row = v + (((size_t)b * src_len + j) * kv_heads + hkv) * d;
acc += s_score[j] * to_float(v_row[dd]);
}
o_row[dd] = from_float<T>(acc / l);
}
}

// Hidden_dim == num_heads * head_dim.
// query_heads和kv_heads是否相同,则决定了head_dim的大小
/**
* @brief Computes flash attention for given query, key, and value tensors.
*
*
* @tparam T Data type (float) for input/output tensors
* @param[in] h_q Query tensor of shape [batch_size, tgt_seq_len, query_heads, head_dim]
* @param[in] h_k Key tensor of shape [batch_size, src_seq_len, kv_heads, head_dim]
* @param[in] h_v Value tensor of shape [batch_size, src_seq_len, kv_heads, head_dim]
* @param[out] h_o Output attention tensor of shape [batch_size, tgt_seq_len, query_heads, head_dim]
* @param[in] h_q Query tensor of shape [batch_size, tgt_seq_len, query_heads,
* head_dim]
* @param[in] h_k Key tensor of shape [batch_size, src_seq_len, kv_heads,
* head_dim]
* @param[in] h_v Value tensor of shape [batch_size, src_seq_len, kv_heads,
* head_dim]
* @param[out] h_o Output attention tensor of shape [batch_size, tgt_seq_len,
* query_heads, head_dim]
* @param[in] batch_size Batch dimension size
* @param[in] target_seq_len Target sequence length
* @param[in] src_seq_len Source sequence length
* @param[in] src_seq_len Source sequence length
* @param[in] query_heads Number of query attention heads
* @param[in] kv_heads Number of key/value heads (supports grouped query attention)
* @param[in] kv_heads Number of key/value heads (supports grouped query
* attention)
* @param[in] head_dim Dimension size of each attention head
* @param[in] is_causal Whether to apply causal masking
*/
template <typename T>
void flashAttention(const std::vector<T>& h_q, const std::vector<T>& h_k,
const std::vector<T>& h_v, std::vector<T>& h_o,
int batch_size, int target_seq_len, int src_seq_len,
int query_heads, int kv_heads, int head_dim, bool is_causal) {
// TODO: Implement the flash attention function
void flashAttention(const std::vector<T> &h_q, const std::vector<T> &h_k,
const std::vector<T> &h_v, std::vector<T> &h_o,
int batch_size, int target_seq_len, int src_seq_len,
int query_heads, int kv_heads, int head_dim,
bool is_causal) {
// 和 rmsNorm 一模一样的套路,只是换成 4 个张量:
T *d_q, *d_k, *d_v, *d_o;
size_t q_size =
(size_t)batch_size * target_seq_len * query_heads * head_dim * sizeof(T);
size_t kv_size =
(size_t)batch_size * src_seq_len * kv_heads * head_dim * sizeof(T);
// cudaMalloc x4 → cudaMemcpy q/k/v → launch → memcpy 回 h_o → cudaFree x4
cudaMalloc(&d_q, q_size);
cudaMalloc(&d_k, kv_size);
cudaMalloc(&d_v, kv_size);
cudaMalloc(&d_o, q_size);

cudaMemcpy(d_q, h_q.data(), q_size, cudaMemcpyHostToDevice);
cudaMemcpy(d_k, h_k.data(), kv_size, cudaMemcpyHostToDevice);
cudaMemcpy(d_v, h_v.data(), kv_size, cudaMemcpyHostToDevice);

// 启动配置:一个 block 负责一个输出行 (b, t, h)
// 三维网格,天然映射, 剩下的一个就是head dim
dim3 grid(batch_size, target_seq_len, query_heads);
int threads = 128;
// s_score[src_len] + red[threads] 两块区域,缺一不可!
size_t shmem = ((size_t)src_seq_len + threads) * sizeof(float);
flashAttentionKernel<T><<<grid, threads, shmem>>>(
d_q, d_k, d_v, d_o, target_seq_len, src_seq_len, query_heads, kv_heads,
head_dim, is_causal);

cudaMemcpy(h_o.data(), d_o, q_size, cudaMemcpyDeviceToHost);
cudaFree(d_q);
cudaFree(d_k);
cudaFree(d_v);
cudaFree(d_o);
}

// *********************************************************************
// Explicit Template Instantiations (REQUIRED FOR LINKING WITH TESTER.O)
// DO NOT MODIFY THIS SECTION
// *********************************************************************
template void rmsNorm<float>(const std::vector<float>&, const std::vector<float>&,
std::vector<float>&, size_t, size_t, float);
template void rmsNorm<half>(const std::vector<half>&, const std::vector<half>&,
std::vector<half>&, size_t, size_t, float);
template void flashAttention<float>(const std::vector<float>&, const std::vector<float>&,
const std::vector<float>&, std::vector<float>&,
int, int, int, int, int, int, bool);
template void flashAttention<half>(const std::vector<half>&, const std::vector<half>&,
const std::vector<half>&, std::vector<half>&,
int, int, int, int, int, int, bool);
template void rmsNorm<float>(const std::vector<float> &,
const std::vector<float> &, std::vector<float> &,
size_t, size_t, float);
template void rmsNorm<half>(const std::vector<half> &,
const std::vector<half> &, std::vector<half> &,
size_t, size_t, float);
template void flashAttention<float>(const std::vector<float> &,
const std::vector<float> &,
const std::vector<float> &,
std::vector<float> &, int, int, int, int,
int, int, bool);
template void flashAttention<half>(const std::vector<half> &,
const std::vector<half> &,
const std::vector<half> &,
std::vector<half> &, int, int, int, int, int,
int, bool);
Loading