175 lines
7.3 KiB
Plaintext
175 lines
7.3 KiB
Plaintext
#include "turbo-quant.cuh"
|
|
#include "turbo-wht.cuh"
|
|
|
|
// ─── CUDA kernel ──────────────────────────────────────────────────────────────
|
|
//
|
|
// Templated on direction and group_size (128 or 64).
|
|
// One block per group, group_size threads per block.
|
|
// direction: 0 = forward (signs1 → WHT → signs2), 1 = inverse (signs2 → WHT → signs1)
|
|
//
|
|
// When head_dim is not a multiple of group_size, only the full groups
|
|
// within each head are processed. Tail elements are left unchanged (identity).
|
|
//
|
|
// Algorithm mirrors the CPU implementation in ggml-cpu/ops.cpp:
|
|
// 1. Apply s_first elementwise
|
|
// 2. Radix-2 Hadamard butterfly (log2(group_size) stages, in-place)
|
|
// 3. Normalize by 1/sqrt(group_size) and apply s_second elementwise
|
|
//
|
|
// InnerQ scale_inv: when non-null, applies per-channel inverse scaling for
|
|
// Q/V equalization. For forward (Q rotation): multiply BEFORE signs+WHT.
|
|
// For inverse (V un-rotation): multiply AFTER WHT+signs.
|
|
|
|
template <int direction, int group_size>
|
|
static __global__ void k_turbo_wht_f32(const float * __restrict__ src,
|
|
float * __restrict__ dst,
|
|
const float * __restrict__ scale_inv,
|
|
int64_t n_groups,
|
|
int64_t head_dim,
|
|
int64_t groups_per_head) {
|
|
static_assert(group_size == 128 || group_size == 64, "group_size must be 128 or 64");
|
|
|
|
const int64_t g = blockIdx.x;
|
|
if (g >= n_groups) return;
|
|
|
|
const int t = threadIdx.x; // 0 .. group_size-1
|
|
|
|
// Map group index to position in the tensor:
|
|
// each head has groups_per_head full groups, then a gap of tail elements.
|
|
const int64_t head_idx = g / groups_per_head;
|
|
const int64_t grp_in_head = g % groups_per_head;
|
|
const int64_t base = head_idx * head_dim + grp_in_head * group_size;
|
|
|
|
__shared__ float x[group_size];
|
|
|
|
// Load from global memory
|
|
x[t] = src[base + t];
|
|
__syncthreads();
|
|
|
|
// InnerQ forward: apply scale_inv BEFORE signs+WHT (for Q pre-rotation)
|
|
if (direction == 0 && scale_inv != nullptr) {
|
|
x[t] *= scale_inv[t % group_size];
|
|
__syncthreads();
|
|
}
|
|
|
|
// Apply first sign array
|
|
if (group_size == 128) {
|
|
x[t] *= (direction == 0) ? TURBO_WHT_SIGNS1[t] : TURBO_WHT_SIGNS2[t];
|
|
} else {
|
|
x[t] *= (direction == 0) ? TURBO_WHT_SIGNS1_64[t] : TURBO_WHT_SIGNS2_64[t];
|
|
}
|
|
__syncthreads();
|
|
|
|
// WHT butterfly — log2(group_size) stages.
|
|
// In stage h, threads where (t % (2h)) < h read x[t] and x[t+h],
|
|
// then write x[t] = a+b and x[t+h] = a-b. Each active thread
|
|
// owns a disjoint pair, so no intra-stage conflicts exist.
|
|
#define WHT_STAGE(h) \
|
|
if (t % (2*(h)) < (h)) { float a = x[t], b = x[t+(h)]; x[t] = a+b; x[t+(h)] = a-b; } \
|
|
__syncthreads();
|
|
|
|
WHT_STAGE(1)
|
|
WHT_STAGE(2)
|
|
WHT_STAGE(4)
|
|
WHT_STAGE(8)
|
|
WHT_STAGE(16)
|
|
WHT_STAGE(32)
|
|
if (group_size == 128) { WHT_STAGE(64) }
|
|
#undef WHT_STAGE
|
|
|
|
// Normalize and apply second sign array, write to output
|
|
constexpr float inv_sqrt = (group_size == 128) ? 0.08838834764831845f : 0.125f;
|
|
float result;
|
|
if (group_size == 128) {
|
|
result = x[t] * inv_sqrt *
|
|
((direction == 0) ? TURBO_WHT_SIGNS2[t] : TURBO_WHT_SIGNS1[t]);
|
|
} else {
|
|
result = x[t] * inv_sqrt *
|
|
((direction == 0) ? TURBO_WHT_SIGNS2_64[t] : TURBO_WHT_SIGNS1_64[t]);
|
|
}
|
|
|
|
// InnerQ inverse: apply scale_inv AFTER WHT+signs (for V un-rotation)
|
|
if (direction == 1 && scale_inv != nullptr) {
|
|
result *= scale_inv[t % group_size];
|
|
}
|
|
|
|
dst[base + t] = result;
|
|
}
|
|
|
|
// ─── Simple copy kernel for tail elements (identity pass-through) ────────────
|
|
|
|
static __global__ void k_turbo_wht_copy_tail(const float * __restrict__ src,
|
|
float * __restrict__ dst,
|
|
int64_t n_heads,
|
|
int64_t head_dim,
|
|
int64_t tail_offset,
|
|
int tail_size) {
|
|
const int64_t i = (int64_t)blockIdx.x * blockDim.x + threadIdx.x;
|
|
if (i >= n_heads * tail_size) return;
|
|
|
|
const int64_t head_idx = i / tail_size;
|
|
const int64_t tail_elem = i % tail_size;
|
|
const int64_t offset = head_idx * head_dim + tail_offset + tail_elem;
|
|
dst[offset] = src[offset];
|
|
}
|
|
|
|
// ─── Dispatch ─────────────────────────────────────────────────────────────────
|
|
|
|
void ggml_cuda_turbo_wht(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
|
const ggml_tensor * src = dst->src[0];
|
|
const ggml_tensor * scale_tensor = dst->src[1]; // InnerQ scale_inv (may be NULL)
|
|
|
|
GGML_ASSERT(src->type == GGML_TYPE_F32);
|
|
GGML_ASSERT(dst->type == GGML_TYPE_F32);
|
|
GGML_ASSERT(ggml_is_contiguous(src));
|
|
GGML_ASSERT(ggml_is_contiguous(dst));
|
|
|
|
int direction;
|
|
int group_size;
|
|
memcpy(&direction, dst->op_params + 0, sizeof(int));
|
|
memcpy(&group_size, dst->op_params + sizeof(int), sizeof(int));
|
|
|
|
const int64_t head_dim = src->ne[0];
|
|
const int64_t n_heads = ggml_nelements(src) / head_dim;
|
|
|
|
GGML_ASSERT(group_size == 64 || group_size == 128);
|
|
const int64_t groups_per_head = head_dim / group_size;
|
|
const int tail_size = (int)(head_dim % group_size);
|
|
const int64_t n_groups = groups_per_head * n_heads;
|
|
|
|
const float * src_ptr = (const float *) src->data;
|
|
float * dst_ptr = (float *) dst->data;
|
|
const float * scale_inv_ptr = scale_tensor ? (const float *) scale_tensor->data : nullptr;
|
|
|
|
cudaStream_t stream = ctx.stream();
|
|
|
|
// Process full groups
|
|
if (n_groups > 0) {
|
|
dim3 blocks(n_groups);
|
|
if (group_size == 128) {
|
|
dim3 threads(128);
|
|
if (direction == 0) {
|
|
k_turbo_wht_f32<0, 128><<<blocks, threads, 0, stream>>>(src_ptr, dst_ptr, scale_inv_ptr, n_groups, head_dim, groups_per_head);
|
|
} else {
|
|
k_turbo_wht_f32<1, 128><<<blocks, threads, 0, stream>>>(src_ptr, dst_ptr, scale_inv_ptr, n_groups, head_dim, groups_per_head);
|
|
}
|
|
} else {
|
|
dim3 threads(64);
|
|
if (direction == 0) {
|
|
k_turbo_wht_f32<0, 64><<<blocks, threads, 0, stream>>>(src_ptr, dst_ptr, scale_inv_ptr, n_groups, head_dim, groups_per_head);
|
|
} else {
|
|
k_turbo_wht_f32<1, 64><<<blocks, threads, 0, stream>>>(src_ptr, dst_ptr, scale_inv_ptr, n_groups, head_dim, groups_per_head);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pass through tail elements unchanged (no rotation)
|
|
// Not needed for 64-aligned dims but kept for completeness
|
|
if (tail_size > 0) {
|
|
const int64_t total_tail = n_heads * tail_size;
|
|
const int block_sz = 256;
|
|
const int n_blocks = (int)((total_tail + block_sz - 1) / block_sz);
|
|
k_turbo_wht_copy_tail<<<n_blocks, block_sz, 0, stream>>>(
|
|
src_ptr, dst_ptr, n_heads, head_dim, groups_per_head * group_size, tail_size);
|
|
}
|
|
}
|