151 lines
5.5 KiB
Plaintext
151 lines
5.5 KiB
Plaintext
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
|
/* Copyright (C) 2026 David (Corey) Herrera */
|
|
/*
|
|
* rope.cu — partial NEOX rotary position embedding for Foundry
|
|
*
|
|
* Target: sm_37 (K80, Kepler)
|
|
* Compile: nvcc -ptx -arch=sm_37 rope.cu -o rope.ptx
|
|
*
|
|
* Applies partial NEOX RoPE in place to a packed [n_heads * head_dim] tensor:
|
|
* only the first rope_dim (=64) dims of each head_dim (=256) head are rotated;
|
|
* dims [rope_dim, head_dim) pass through untouched.
|
|
*
|
|
* Mirrors, value-for-value, the CPU reference:
|
|
* Pantheon/src/models/qwen35moe_attn.c
|
|
* qwen35moe_rope_partial (lines 57-76)
|
|
*
|
|
* NEOX pairing: dim j pairs with dim j+half (half = rope_dim/2), angle =
|
|
* pos * theta_base^(-2j/rope_dim). This is the single-section collapse the
|
|
* CPU path documents: for this text-only GGUF every section shares the one
|
|
* linear token position, so multi-section / interleaved head logic does NOT
|
|
* apply here. (RoPE pairs dims within a head; it does no GQA head broadcast,
|
|
* so the interleaved-vs-blocked head-tiling trap is not in play in this file.)
|
|
*
|
|
* PTX symbol emitted (for embedding into ../cuda/fc_cuda_ptx.h):
|
|
* FC_PTX_ROPE -> kernel fc_rope_neox_partial
|
|
*
|
|
* Grid/block layout:
|
|
* grid = n_heads (one block per head)
|
|
* block = half threads, capped at 256 (rope_dim/2 = 32 for this model, so
|
|
* one warp's worth — well within Kepler limits). One thread per
|
|
* rotation pair; idle threads (tid >= half) early-out.
|
|
*
|
|
* Kepler notes:
|
|
* - Pure fp32. Uses cosf/sinf/powf — same libm intrinsics the CPU path uses,
|
|
* so the only divergence source is fp32 vs the CPU's fp32 (identical type).
|
|
* - No reductions, no shared memory, no __shfl — embarrassingly parallel.
|
|
*/
|
|
|
|
extern "C" {
|
|
|
|
/*
|
|
* Partial NEOX RoPE, in place.
|
|
*
|
|
* data: [n_heads * head_dim] fp32, rotated in place
|
|
* n_heads: number of heads in this tensor (Q: 16, K: 2)
|
|
* head_dim: per-head dimension (256)
|
|
* rope_dim: number of leading dims to rotate (64); rest pass through
|
|
* pos: token position (0-based)
|
|
* theta_base: rope.freq_base (e.g. 1e7)
|
|
*
|
|
* Launched with `n_heads` blocks; blockDim.x >= rope_dim/2 (use 32 or 256).
|
|
* No shared memory.
|
|
*
|
|
* Matches CPU qwen35moe_rope_partial: rot = (rope_dim in (0,head_dim)) ?
|
|
* rope_dim : head_dim, half = rot/2; freq = 1/theta_base^(2j/rot).
|
|
*/
|
|
__global__ void fc_rope_neox_partial(float *data, int n_heads, int head_dim,
|
|
int rope_dim, int pos, float theta_base) {
|
|
int head = blockIdx.x;
|
|
if (head >= n_heads) return;
|
|
|
|
int rot = (rope_dim > 0 && rope_dim < head_dim) ? rope_dim : head_dim;
|
|
int half = rot / 2;
|
|
|
|
int j = threadIdx.x;
|
|
if (j >= half) return; /* one thread per rotation pair */
|
|
|
|
float *h = data + (size_t)head * head_dim;
|
|
|
|
/* freq = theta_base^(-2j/rot); angle = pos * freq. Computed per-thread to
|
|
* stay bit-faithful to the CPU per-j powf/cosf/sinf sequence. */
|
|
float freq = 1.0f / powf(theta_base, (float)(2 * j) / (float)rot);
|
|
float angle = (float)pos * freq;
|
|
float cos_a = cosf(angle);
|
|
float sin_a = sinf(angle);
|
|
|
|
float x0 = h[j];
|
|
float x1 = h[j + half];
|
|
h[j] = x0 * cos_a - x1 * sin_a;
|
|
h[j + half] = x0 * sin_a + x1 * cos_a;
|
|
}
|
|
|
|
/*
|
|
* Batched (prefill) variant: n consecutive positions in one launch.
|
|
* data: [n][n_heads * head_dim], position of row t is pos0 + t.
|
|
* Grid (n_heads, n); per (head, position) the math is identical to
|
|
* fc_rope_neox_partial at pos = pos0 + t -> bit-exact.
|
|
*/
|
|
__global__ void fc_rope_neox_partial_nx(float *data, int n_heads, int head_dim,
|
|
int rope_dim, int pos0,
|
|
float theta_base) {
|
|
int head = blockIdx.x;
|
|
int t = blockIdx.y;
|
|
if (head >= n_heads) return;
|
|
|
|
int rot = (rope_dim > 0 && rope_dim < head_dim) ? rope_dim : head_dim;
|
|
int half = rot / 2;
|
|
|
|
int j = threadIdx.x;
|
|
if (j >= half) return;
|
|
|
|
float *h = data + ((size_t)t * n_heads + head) * head_dim;
|
|
int pos = pos0 + t;
|
|
|
|
float freq = 1.0f / powf(theta_base, (float)(2 * j) / (float)rot);
|
|
float angle = (float)pos * freq;
|
|
float cos_a = cosf(angle);
|
|
float sin_a = sinf(angle);
|
|
|
|
float x0 = h[j];
|
|
float x1 = h[j + half];
|
|
h[j] = x0 * cos_a - x1 * sin_a;
|
|
h[j + half] = x0 * sin_a + x1 * cos_a;
|
|
}
|
|
|
|
/*
|
|
* Multi-user decode variant: n rows, each at its OWN arbitrary position.
|
|
* data: [n][n_heads * head_dim], pos: [n] int32 device array — row t is
|
|
* rotated at position pos[t]. Grid (n_heads, n); per (head, row) the math
|
|
* is identical to fc_rope_neox_partial at pos = pos[t] -> bit-exact per
|
|
* user vs decoding alone.
|
|
*/
|
|
__global__ void fc_rope_neox_partial_pos(float *data, int n_heads,
|
|
int head_dim, int rope_dim,
|
|
const int *pos, float theta_base) {
|
|
int head = blockIdx.x;
|
|
int t = blockIdx.y;
|
|
if (head >= n_heads) return;
|
|
|
|
int rot = (rope_dim > 0 && rope_dim < head_dim) ? rope_dim : head_dim;
|
|
int half = rot / 2;
|
|
|
|
int j = threadIdx.x;
|
|
if (j >= half) return;
|
|
|
|
float *h = data + ((size_t)t * n_heads + head) * head_dim;
|
|
int p = pos[t];
|
|
|
|
float freq = 1.0f / powf(theta_base, (float)(2 * j) / (float)rot);
|
|
float angle = (float)p * freq;
|
|
float cos_a = cosf(angle);
|
|
float sin_a = sinf(angle);
|
|
|
|
float x0 = h[j];
|
|
float x1 = h[j + half];
|
|
h[j] = x0 * cos_a - x1 * sin_a;
|
|
h[j + half] = x0 * sin_a + x1 * cos_a;
|
|
}
|
|
|
|
} /* extern "C" */
|