char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
d9b6be07d
char/aliceai-t5-moe
1#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA) && CUDART_VERSION >= 11070 2#define USE_CUB 3#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA) && CUDART_VERSION >= 11070 4 5#ifdef USE_CUB 6#include <cub/cub.cuh> 7using namespace cub; 8#endif // USE_CUB 9 10#include "ssm-scan.cuh" 11 12 13// Minimum number of tokens to use SSD (State Space Duality) matmul path instead of scan path. 14// For n_tok <= this threshold, the scan kernel is used (lower overhead for short sequences). 15#define SSM_SSD_MIN_TOKENS 128 16 17// prepare_dt kernel dimensions: one block per (head, seq), each block handles DT_MAX_ITEMS items. 18#define SSM_SSD_DT_BLOCK 256 19#define SSM_SSD_DT_MAX_ITEMS 32 20 21// Maximum tokens the SSD path supports, derived from the prepare_dt kernel block capacity. 22#define SSM_SSD_MAX_TOKENS (SSM_SSD_DT_BLOCK * SSM_SSD_DT_MAX_ITEMS) 23 24// Chunk size for chunked SSD. Caps matmul cost at O(chunk^2) per chunk. 25#define SSM_SSD_CHUNK_SIZE 256 26 27// We would like to keep pragma unroll for cases where L_template is not 0, 28// so we suppress the clang transformation warning. 29#ifdef __clang__ 30#pragma clang diagnostic push 31#pragma clang diagnostic ignored "-Wpass-failed" 32#endif // __clang__ 33template <size_t splitD, size_t N, size_t L_template> 34__global__ void __launch_bounds__(splitD, 1) 35 ssm_scan_f32(const float * src0_ptr, const float * src1_ptr, const float * src2_ptr, 36 const float * src3_ptr, const float * src4_ptr, const float * src5_ptr, 37 const int32_t * src6_ptr, float * dst_ptr, 38 const int src0_nb2, const int src0_nb3, const int src1_nb2, const int src1_nb3, 39 const int src2_nb1, const int src2_nb2, const int src3_nb1, 40 const int src4_nb2, const int src4_nb3, const int src5_nb2, const int src5_nb3, 41 const int64_t s_off, const int64_t d_inner, const int64_t L_param) 42{ 43 const float * GGML_CUDA_RESTRICT src0 = src0_ptr; 44 const float * GGML_CUDA_RESTRICT src1 = src1_ptr; 45 const float * GGML_CUDA_RESTRICT src2 = src2_ptr; 46 const float * GGML_CUDA_RESTRICT src3 = src3_ptr; 47 const float * GGML_CUDA_RESTRICT src4 = src4_ptr; 48 const float * GGML_CUDA_RESTRICT src5 = src5_ptr; 49 const int32_t * GGML_CUDA_RESTRICT src6 = src6_ptr; 50 float * GGML_CUDA_RESTRICT dst = dst_ptr; 51 const size_t L = L_template == 0 ? L_param : L_template; 52 ggml_cuda_pdl_sync(); 53 const float *s0_block = (const float *)((const char *)src0 + src6[blockIdx.x] * src0_nb3 + blockIdx.y * splitD * src0_nb2); 54 const float *x_block = (const float *)((const char *)src1 + (blockIdx.x * src1_nb3) + blockIdx.y * splitD * sizeof(float)); 55 const float *dt_block = (const float *)((const char *)src2 + (blockIdx.x * src2_nb2) + blockIdx.y * splitD * sizeof(float)); 56 const float *A_block = (const float *)((const char *)src3 + blockIdx.y * splitD * src3_nb1); 57 const float *B_block = (const float *)((const char *)src4 + (blockIdx.x * src4_nb3)); 58 const float *C_block = (const float *)((const char *)src5 + (blockIdx.x * src5_nb3)); 59 float *y_block = (float *)((char *)dst + (blockIdx.x * d_inner * L * sizeof(float)) + blockIdx.y * splitD * sizeof(float)); 60 float *s_block = (float *)((char *)dst + s_off + blockIdx.x * src0_nb3 + blockIdx.y * splitD * src0_nb2); 61 62 const int stride_x = src1_nb2 / sizeof(float); 63 const int stride_dt = src2_nb1 / sizeof(float); 64 const int stride_B = src4_nb2 / sizeof(float); 65 const int stride_C = src5_nb2 / sizeof(float); 66 const int stride_y = d_inner; 67 68 float regA[N]; 69 float regs0[N]; 70 71 __shared__ float smemB[N]; 72 __shared__ float smemC[N]; 73 74#ifdef USE_CUB 75 using BlockLoad = cub::BlockLoad<float, splitD, N, cub::BLOCK_LOAD_WARP_TRANSPOSE>; 76 using BlockStore = cub::BlockStore<float, splitD, N, cub::BLOCK_STORE_WARP_TRANSPOSE>; 77 78 union CubTempStorage { 79 typename BlockLoad::TempStorage load_temp; 80 typename BlockStore::TempStorage store_temp; 81 }; 82 __shared__ CubTempStorage cub_temp_storage; 83 84 BlockLoad(cub_temp_storage.load_temp).Load(A_block, regA); 85 __syncthreads(); 86 BlockLoad(cub_temp_storage.load_temp).Load(s0_block, regs0); 87#else 88 const int stride_s0 = src0_nb2 / sizeof(float); 89 const int stride_A = src3_nb1 / sizeof(float); 90#pragma unroll 91 for (size_t n = 0; n < N; ++n) 92 { 93 regA[n] = A_block[threadIdx.x * stride_A + n]; 94 regs0[n] = s0_block[threadIdx.x * stride_s0 + n]; 95 } 96#endif 97 98#pragma unroll 99 for (size_t i = 0; i < L; i++) 100 { 101 if (threadIdx.x < N) 102 { 103 smemB[threadIdx.x] = B_block[i * stride_B + threadIdx.x]; 104 smemC[threadIdx.x] = C_block[i * stride_C + threadIdx.x]; 105 } 106 __syncthreads(); 107 108 float dt_soft_plus = dt_block[i * stride_dt + threadIdx.x]; 109 if (dt_soft_plus <= 20.0f) 110 { 111 dt_soft_plus = log1pf(expf(dt_soft_plus)); 112 } 113 float x_dt = x_block[i * stride_x + threadIdx.x] * dt_soft_plus; 114 115 float sumf = 0.0f; 116#pragma unroll 117 for (size_t n = 0; n < N; n++) 118 { 119 float state = regs0[n] * expf(dt_soft_plus * regA[n]) + smemB[n] * x_dt; 120 sumf += state * smemC[n]; 121 regs0[n] = state; 122 } 123 y_block[i * stride_y + threadIdx.x] = sumf; 124 __syncthreads(); 125 } 126 127#ifdef USE_CUB 128 BlockStore(cub_temp_storage.store_temp).Store(s_block, regs0); 129#else 130 const int stride_s = stride_s0; 131#pragma unroll 132 for (size_t n = 0; n < N; ++n) 133 { 134 s_block[threadIdx.x * stride_s + n] = regs0[n]; 135 } 136#endif 137} 138#ifdef __clang__ 139#pragma clang diagnostic pop 140#endif // __clang__ 141 142// assumes as many threads as d_state 143template <int c_factor, int d_state> 144__global__ void __launch_bounds__(d_state, 1) 145 ssm_scan_f32_group( 146 const float * src0_ptr, const float * src1_ptr, const float * src2_ptr, 147 const float * src3_ptr, const float * src4_ptr, const float * src5_ptr, 148 const int32_t * src6_ptr, float * dst_ptr, 149 const int src0_nb2, const int src0_nb3, const int src1_nb2, const int src1_nb3, 150 const int src2_nb1, const int src2_nb2, const int src3_nb1, 151 const int src4_nb2, const int src4_nb3, const int src5_nb2, const int src5_nb3, 152 const int64_t s_off, const int64_t n_head, const int64_t d_head, const int64_t n_group, const int64_t n_tok, const int64_t K) { 153 const float * GGML_CUDA_RESTRICT src0 = src0_ptr; 154 const float * GGML_CUDA_RESTRICT src1 = src1_ptr; 155 const float * GGML_CUDA_RESTRICT src2 = src2_ptr; 156 const float * GGML_CUDA_RESTRICT src3 = src3_ptr; 157 const float * GGML_CUDA_RESTRICT src4 = src4_ptr; 158 const float * GGML_CUDA_RESTRICT src5 = src5_ptr; 159 const int32_t * GGML_CUDA_RESTRICT src6 = src6_ptr; 160 float * GGML_CUDA_RESTRICT dst = dst_ptr; 161 162 const int warp = threadIdx.x / WARP_SIZE; 163 const int lane = threadIdx.x % WARP_SIZE; 164 const int warp_idx = blockIdx.x * c_factor + warp; 165 166 const int head_idx = warp_idx / d_head; 167 const int head_off = (warp_idx % d_head) * sizeof(float); 168 const int seq_idx = blockIdx.y; 169 170 const int group_off = (head_idx / (n_head / n_group)) * d_state * sizeof(float); 171 172 ggml_cuda_pdl_sync(); 173 // TODO: refactor strides to be in elements/floats instead of bytes to be cleaner and consistent with the rest of the codebase 174 const float * s0_warp = (const float *) ((const char *) src0 + src6[seq_idx] * src0_nb3 + head_idx * src0_nb2 + head_off * d_state); 175 const float * x_warp = (const float *) ((const char *) src1 + (seq_idx * src1_nb3) + (warp_idx * sizeof(float))); 176 const float * dt_warp = (const float *) ((const char *) src2 + (seq_idx * src2_nb2) + head_idx * sizeof(float)); 177 const float * A_warp = (const float *) ((const char *) src3 + head_idx * src3_nb1); 178 const float * B_warp = (const float *) ((const char *) src4 + (seq_idx * src4_nb3) + (group_off)); 179 const float * C_warp = (const float *) ((const char *) src5 + (seq_idx * src5_nb3) + (group_off)); 180 float * y_warp = dst + (seq_idx * n_tok * n_head * d_head) + warp_idx; 181 float * s_warp = (float *) ((char *) dst + s_off + seq_idx * src0_nb3 + head_idx * src0_nb2 + head_off * d_state); 182 183 // strides across n_seq_tokens 184 const int stride_x = src1_nb2 / sizeof(float); 185 const int stride_dt = src2_nb1 / sizeof(float); 186 const int stride_B = src4_nb2 / sizeof(float); 187 const int stride_C = src5_nb2 / sizeof(float); 188 const int stride_y = n_head * d_head; 189 190 float state[c_factor]; 191 float state_sum = 0.0f; 192 193#pragma unroll 194 for (int j = 0; j < c_factor; j++) { 195 state[j] = s0_warp[WARP_SIZE * j + lane]; 196 } 197 198 for (int64_t i = 0; i < n_tok; i++) { 199 // NOTE: dt_soft_plus, dA and x_dt have the same value for a warp here. 200 // Recalculation is intentional; sharing via shuffles/smem proved slower due to sync overhead. 201 const float dt_soft_plus = (dt_warp[i * stride_dt] <= 20.0f ? log1pf(expf(dt_warp[i * stride_dt])) : dt_warp[i * stride_dt]); 202 203 state_sum = 0.0f; 204 const float dA = expf(dt_soft_plus * A_warp[0]); 205 const float x_dt = x_warp[i * stride_x] * dt_soft_plus; 206#pragma unroll 207 for (int j = 0; j < c_factor; j++) { 208 const float B_val = B_warp[i * stride_B + WARP_SIZE * j + lane]; 209 const float C_val = C_warp[i * stride_C + WARP_SIZE * j + lane]; 210 state[j] = (state[j] * dA) + (B_val * x_dt); 211 state_sum += state[j] * C_val; 212 } 213 214 // parallel accumulation for output 215 state_sum = warp_reduce_sum(state_sum); 216 217 if (lane == 0) { 218 y_warp[i * stride_y] = state_sum; 219 } 220 221 // Slot 0 is the final state written below; slots 1..K-1 are rollback snapshots. 222 const int64_t slot = n_tok - 1 - i; 223 if (K > 1 && slot > 0 && slot < K) { 224 float * s_snapshot_warp = (float *) ((char *) dst + s_off + (slot * gridDim.y + seq_idx) * src0_nb3 + head_idx * src0_nb2 + head_off * d_state); 225#pragma unroll 226 for (int j = 0; j < c_factor; j++) { 227 s_snapshot_warp[WARP_SIZE * j + lane] = state[j]; 228 } 229 } 230 } 231 232 // write back the state 233#pragma unroll 234 for (int j = 0; j < c_factor; j++) { 235 s_warp[WARP_SIZE * j + lane] = state[j]; 236 } 237} 238 239static void ssm_scan_f32_cuda(const float * src0, const float * src1, const float * src2, const float * src3, 240 const float * src4, const float * src5, const int32_t * src6, float * dst, 241 const int src0_nb2, const int src0_nb3, const int src1_nb2, const int src1_nb3, const int src2_nb1, 242 const int src2_nb2, const int src3_nb1, const int src4_nb2, const int src4_nb3, const int src5_nb2, 243 const int src5_nb3, const int64_t s_off, const int64_t d_state, const int64_t head_dim, 244 const int64_t n_head, const int64_t n_group, const int64_t n_tok, const int64_t n_seq, 245 const int64_t K, cudaStream_t stream) { 246 // NOTE: if you change conditions here, be sure to update the corresponding supports_op condition! 247 if (src3_nb1 == sizeof(float)) { 248 // Mamba-2 249 if (d_state == 128) { 250 constexpr int threads = 128; 251 constexpr int num_warps = threads/WARP_SIZE; 252 253 const dim3 blocks((n_head * head_dim + (num_warps - 1)) / num_warps, n_seq, 1); 254 const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(blocks, threads, 0, stream); 255 ggml_cuda_kernel_launch(ssm_scan_f32_group<128/WARP_SIZE, 128>, launch_params, 256 src0, src1, src2, src3, src4, src5, src6, dst, 257 src0_nb2, src0_nb3, src1_nb2, src1_nb3, src2_nb1, src2_nb2, src3_nb1, 258 src4_nb2, src4_nb3, src5_nb2, src5_nb3, s_off, n_head, head_dim, n_group, n_tok, K); 259 } else if (d_state == 256) { // Falcon-H1 260 constexpr int threads = 256; 261 constexpr int num_warps = threads/WARP_SIZE; 262 263 const dim3 blocks((n_head * head_dim + (num_warps - 1)) / num_warps, n_seq, 1); 264 const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(blocks, threads, 0, stream); 265 ggml_cuda_kernel_launch(ssm_scan_f32_group<256/WARP_SIZE, 256>, launch_params, 266 src0, src1, src2, src3, src4, src5, src6, dst, 267 src0_nb2, src0_nb3, src1_nb2, src1_nb3, src2_nb1, src2_nb2, src3_nb1, 268 src4_nb2, src4_nb3, src5_nb2, src5_nb3, s_off, n_head, head_dim, n_group, n_tok, K); 269 } else { 270 GGML_ABORT("doesn't support d_state!=(128 or 256)."); 271 } 272 } else { 273 // Mamba-1 274 GGML_ASSERT(K == 1); 275 constexpr int threads = 128; 276 GGML_ASSERT(n_head % threads == 0); 277 GGML_ASSERT(head_dim == 1); 278 GGML_ASSERT(n_group == 1); 279 const dim3 blocks(n_seq, (n_head + threads - 1) / threads, 1); 280 if (d_state == 16) { 281 const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(blocks, threads, 0, stream); 282 switch (n_tok) 283 { 284 case 1: 285 ggml_cuda_kernel_launch(ssm_scan_f32<threads, 16, 1>, launch_params, 286 src0, src1, src2, src3, src4, src5, src6, dst, 287 src0_nb2, src0_nb3, src1_nb2, src1_nb3, src2_nb1, src2_nb2, 288 src3_nb1, src4_nb2, src4_nb3, src5_nb2, src5_nb3, s_off, n_head, n_tok); 289 break; 290 case 2: 291 ggml_cuda_kernel_launch(ssm_scan_f32<threads, 16, 2>, launch_params, 292 src0, src1, src2, src3, src4, src5, src6, dst, 293 src0_nb2, src0_nb3, src1_nb2, src1_nb3, src2_nb1, src2_nb2, 294 src3_nb1, src4_nb2, src4_nb3, src5_nb2, src5_nb3, s_off, n_head, n_tok); 295 break; 296 case 3: 297 ggml_cuda_kernel_launch(ssm_scan_f32<threads, 16, 3>, launch_params, 298 src0, src1, src2, src3, src4, src5, src6, dst, 299 src0_nb2, src0_nb3, src1_nb2, src1_nb3, src2_nb1, src2_nb2, 300 src3_nb1, src4_nb2, src4_nb3, src5_nb2, src5_nb3, s_off, n_head, n_tok); 301 break; 302 case 4: 303 ggml_cuda_kernel_launch(ssm_scan_f32<threads, 16, 4>, launch_params, 304 src0, src1, src2, src3, src4, src5, src6, dst, 305 src0_nb2, src0_nb3, src1_nb2, src1_nb3, src2_nb1, src2_nb2, 306 src3_nb1, src4_nb2, src4_nb3, src5_nb2, src5_nb3, s_off, n_head, n_tok); 307 break; 308 case 5: 309 ggml_cuda_kernel_launch(ssm_scan_f32<threads, 16, 5>, launch_params, 310 src0, src1, src2, src3, src4, src5, src6, dst, 311 src0_nb2, src0_nb3, src1_nb2, src1_nb3, src2_nb1, src2_nb2, 312 src3_nb1, src4_nb2, src4_nb3, src5_nb2, src5_nb3, s_off, n_head, n_tok); 313 break; 314 case 6: 315 ggml_cuda_kernel_launch(ssm_scan_f32<threads, 16, 6>, launch_params, 316 src0, src1, src2, src3, src4, src5, src6, dst, 317 src0_nb2, src0_nb3, src1_nb2, src1_nb3, src2_nb1, src2_nb2, 318 src3_nb1, src4_nb2, src4_nb3, src5_nb2, src5_nb3, s_off, n_head, n_tok); 319 break; 320 case 7: 321 ggml_cuda_kernel_launch(ssm_scan_f32<threads, 16, 7>, launch_params, 322 src0, src1, src2, src3, src4, src5, src6, dst, 323 src0_nb2, src0_nb3, src1_nb2, src1_nb3, src2_nb1, src2_nb2, 324 src3_nb1, src4_nb2, src4_nb3, src5_nb2, src5_nb3, s_off, n_head, n_tok); 325 break; 326 case 8: 327 ggml_cuda_kernel_launch(ssm_scan_f32<threads, 16, 8>, launch_params, 328 src0, src1, src2, src3, src4, src5, src6, dst, 329 src0_nb2, src0_nb3, src1_nb2, src1_nb3, src2_nb1, src2_nb2, 330 src3_nb1, src4_nb2, src4_nb3, src5_nb2, src5_nb3, s_off, n_head, n_tok); 331 break; 332 default: 333 ggml_cuda_kernel_launch(ssm_scan_f32<threads, 16, 0>, launch_params, 334 src0, src1, src2, src3, src4, src5, src6, dst, 335 src0_nb2, src0_nb3, src1_nb2, src1_nb3, src2_nb1, src2_nb2, 336 src3_nb1, src4_nb2, src4_nb3, src5_nb2, src5_nb3, s_off, n_head, n_tok); 337 break; 338 } 339 } else { 340 GGML_ABORT("doesn't support d_state!=16."); 341 } 342 } 343} 344 345#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA) 346// ============================================================================ 347// SSD (State Space Duality) kernels for Mamba-2 prefill (n_tok > SSM_SSD_MIN_TOKENS) 348// 349// Instead of a sequential scan, SSD reformulates the output as: 350// Y = (L (.) (C @ B^T)) @ (X * dt) + decay * C @ s_init 351// where L is a causal decay mask derived from A and dt. 352// 353// This converts the O(T*N) sequential scan into parallel matmuls. 354// ============================================================================ 355// Softplus(dt) and inclusive prefix sum per head using CUB BlockScan. 356// Grid: (n_head, n_seqs) 357template <int BLOCK_SIZE, int MAX_ITEMS> 358__global__ void ssm_ssd_prepare_dt_kernel( 359 const float * __restrict__ dt_raw, 360 float * __restrict__ dt_sp_out, 361 float * __restrict__ cs_out, 362 const int n_head, const int n_tok, 363 const int dt_stride_tok, // elements between tokens in dt 364 const int dt_stride_seq) { // elements between sequences in dt 365 366 const int h = blockIdx.x; 367 const int s = blockIdx.y; 368 369 const float * dt_seq = dt_raw + s * dt_stride_seq; 370 371 float * dt_sp_seq = dt_sp_out + s * n_tok * n_head; 372 float * cs_seq = cs_out + s * n_tok * n_head; 373 374 const int items_per_thread = (n_tok + BLOCK_SIZE - 1) / BLOCK_SIZE; 375 376 // Phase 1: softplus with interleaved distribution (t = i*BLOCK_SIZE + threadIdx.x). 377 // Each warp reads BLOCK_SIZE consecutive tokens, giving coalesced dt_raw loads 378 // (stride n_head between threads vs. items_per_thread*n_head in blocked layout). 379 float local_vals[MAX_ITEMS]; 380 for (int i = 0; i < items_per_thread; i++) { 381 const int t = i * BLOCK_SIZE + threadIdx.x; 382 if (t < n_tok) { 383 float val = dt_seq[h + t * dt_stride_tok]; 384 float sp = (val <= 20.0f) ? log1pf(expf(val)) : val; 385 local_vals[i] = sp; 386 dt_sp_seq[t * n_head + h] = sp; 387 } else { 388 local_vals[i] = 0.0f; 389 } 390 } 391 392 // Phase 2+3: per-step inclusive scan to build cs[] in token order. 393 // With interleaved distribution the per-thread total scan would not give token-order 394 // prefix sums, so we scan one BLOCK_SIZE slab at a time and carry a running total. 395#ifdef USE_CUB 396 using BlockScan = cub::BlockScan<float, BLOCK_SIZE>; 397 __shared__ typename BlockScan::TempStorage scan_temp; 398 __shared__ float step_total; 399 400 float running = 0.0f; 401 for (int i = 0; i < items_per_thread; i++) { 402 float inclusive; 403 BlockScan(scan_temp).InclusiveSum(local_vals[i], inclusive); 404 const int t = i * BLOCK_SIZE + threadIdx.x; 405 if (t < n_tok) { 406 cs_seq[t * n_head + h] = running + inclusive; 407 } 408 if (threadIdx.x == BLOCK_SIZE - 1) { 409 step_total = inclusive; 410 } 411 __syncthreads(); 412 running += step_total; 413 } 414#else 415 // Fallback: sequential prefix scan in shared memory, one slab at a time. 416 __shared__ float sdata[BLOCK_SIZE]; 417 float running = 0.0f; 418 for (int i = 0; i < items_per_thread; i++) { 419 const int t = i * BLOCK_SIZE + threadIdx.x; 420 sdata[threadIdx.x] = local_vals[i]; 421 __syncthreads(); 422 if (threadIdx.x == 0) { 423 for (int j = 1; j < BLOCK_SIZE; j++) { 424 sdata[j] += sdata[j - 1]; 425 } 426 } 427 __syncthreads(); 428 if (t < n_tok) { 429 cs_seq[t * n_head + h] = running + sdata[threadIdx.x]; 430 } 431 running += sdata[BLOCK_SIZE - 1]; 432 __syncthreads(); 433 } 434#endif 435} 436 437// Prepare SSD matmul inputs for one chunk: X_dt, B_weighted, C_scaled. 438// T_matmul controls precision for X_dt, B_weighted (float or half). 439// C_scaled is always float (pairs with float s_cur in step 3c). 440// Computation is always FP32; only the final store converts to T_matmul. 441// Also materializes the causal M matrix = exp(A*(cs_out - cs_in)) * CB (fused with prep to save a launch). 442// Grid: (ceil(max(C*head_dim, d_state*C, chunk_len^2) / BLOCK), n_head, n_seqs) 443template <int BLOCK_SIZE, typename T_matmul> 444__global__ void ssm_ssd_pre_matmul_kernel( 445 const float * __restrict__ cs, // {n_tok, n_head} cumulative dt sums 446 const float * __restrict__ dt_sp, // {n_tok, n_head} softplus(dt) 447 const float * __restrict__ A, // {1, n_head} 448 const float * __restrict__ x, // {head_dim, n_head, n_tok, n_seqs} 449 const float * __restrict__ B, // {d_state, n_group, n_tok, n_seqs} 450 const float * __restrict__ C_src, // {d_state, n_group, n_tok, n_seqs} 451 T_matmul * __restrict__ X_dt, // {head_dim, C, n_head} x * dt, d-fastest 452 T_matmul * __restrict__ B_weighted, // {d_state, C, n_head} B * decay_from_end 453 float * __restrict__ C_scaled, // {d_state, C, n_head} C * decay_to_pos (always float) 454 const float * __restrict__ CB, // {chunk_len, chunk_len, n_group, n_seqs} 455 half * __restrict__ M_out, // {chunk_len, chunk_len, n_head, n_seqs} 456 const int chunk_len, const int head_dim, const int n_head, const int n_group, 457 const int d_state, const int A_stride, 458 const int x_stride_tok, const int x_stride_seq, 459 const int B_stride_tok, const int B_stride_seq, 460 const int C_stride_tok, const int C_stride_seq, 461 const int chunk_offset, 462 const int n_tok_total) { 463 464 const int h = blockIdx.y; 465 const int s = blockIdx.z; 466 const int g = h / (n_head / n_group); 467 468 const float A_h = A[h * A_stride]; 469 const int idx = blockIdx.x * BLOCK_SIZE + threadIdx.x; 470 471 const int cs_seq_off = s * n_tok_total * n_head; 472 const float cs_base = (chunk_offset > 0) ? cs[cs_seq_off + (chunk_offset - 1) * n_head + h] : 0.0f; 473 const float cs_last = cs[cs_seq_off + (chunk_offset + chunk_len - 1) * n_head + h] - cs_base; 474 475 // Prepare X_dt = x * dt, stored d-fastest for coalesced reads and writes. 476 const int n_xdt = chunk_len * head_dim; 477 if (idx < n_xdt) { 478 const int d = idx % head_dim; 479 const int t = idx / head_dim; 480 481 const float x_val = x[s * x_stride_seq + (chunk_offset + t) * x_stride_tok + d + h * head_dim]; 482 const float dt_val = dt_sp[cs_seq_off + (chunk_offset + t) * n_head + h]; 483 484 X_dt[d + t * head_dim + h * n_xdt + s * n_xdt * n_head] = (T_matmul)(x_val * dt_val); 485 } 486 487 // Prepare B_weighted and C_scaled together: both share the same index space (d_state * chunk_len) 488 // and the same cs_t load, so merging halves the cs[] global memory traffic. 489 const int n_bw = d_state * chunk_len; 490 if (idx < n_bw) { 491 const int n = idx % d_state; 492 const int t = idx / d_state; 493 494 const float cs_t = cs[cs_seq_off + (chunk_offset + t) * n_head + h] - cs_base; 495 496 const float B_val = B[s * B_stride_seq + (chunk_offset + t) * B_stride_tok + g * d_state + n]; 497 B_weighted[n + t * d_state + h * n_bw + s * n_bw * n_head] = (T_matmul)(B_val * __expf(A_h * (cs_last - cs_t))); 498 499 const float C_val = C_src[s * C_stride_seq + (chunk_offset + t) * C_stride_tok + g * d_state + n]; 500 C_scaled[n + t * d_state + h * n_bw + s * n_bw * n_head] = C_val * __expf(A_h * cs_t); 501 } 502 503 // Materialize M = exp(A*(cs_out - cs_in)) * CB with causal mask. 504 const int n_M = chunk_len * chunk_len; 505 if (idx < n_M) { 506 const int t_out = idx % chunk_len; 507 const int t_in = idx / chunk_len; 508 509 half val; 510 if (t_in <= t_out) { 511 const float cs_out = cs[cs_seq_off + (chunk_offset + t_out) * n_head + h] - cs_base; 512 const float cs_in = cs[cs_seq_off + (chunk_offset + t_in) * n_head + h] - cs_base; 513 const float decay = __expf(A_h * (cs_out - cs_in)); 514 const float * CB_g = CB + (int64_t)s * chunk_len * chunk_len * n_group 515 + (int64_t)g * chunk_len * chunk_len; 516 const float cb_val = CB_g[t_out + t_in * chunk_len]; 517 val = __float2half(decay * cb_val); 518 } else { 519 val = __float2half(0.0f); 520 } 521 522 M_out[(int64_t)s * n_M * n_head + (int64_t)h * n_M + t_in * chunk_len + t_out] = val; 523 } 524} 525 526// Scale running state in-place: s_cur *= decay_total(chunk). 527// Called BEFORE cuBLAS state update (beta=1) to fuse inter-chunk decay. 528// Eliminates the s_old buffer and D2D memcpy vs the old approach of: 529// memcpy(s_old, s_cur) -> cuBLAS(beta=0) -> s_cur += decay * s_old 530// Grid: (ceil(d_state * head_dim / BLOCK), n_head, n_seqs) 531template <int BLOCK_SIZE> 532__global__ void ssm_ssd_scale_state_kernel( 533 float * __restrict__ s_cur, // {d_state, head_dim, n_head, n_seqs} 534 const float * __restrict__ cs, // {n_tok, n_head} cumulative dt sums 535 const float * __restrict__ A, // {1, n_head} 536 const int d_state, const int head_dim, const int n_head, 537 const int chunk_offset, const int chunk_len, 538 const int n_tok_total, const int A_stride) { 539 540 const int h = blockIdx.y; 541 const int s = blockIdx.z; 542 const int idx = blockIdx.x * BLOCK_SIZE + threadIdx.x; 543 const int state_per_head = d_state * head_dim; 544 if (idx >= state_per_head) return; 545 546 const float A_h = A[h * A_stride]; 547 const int cs_seq_off = s * n_tok_total * n_head; 548 const float cs_base = (chunk_offset > 0) ? cs[cs_seq_off + (chunk_offset - 1) * n_head + h] : 0.0f; 549 const float cs_last = cs[cs_seq_off + (chunk_offset + chunk_len - 1) * n_head + h] - cs_base; 550 const float decay_total = __expf(A_h * cs_last); 551 552 const int off = s * state_per_head * n_head + h * state_per_head + idx; 553 s_cur[off] *= decay_total; 554} 555 556// Copy initial state from src0[ids[s]] into s_cur for each sequence. 557// Grid: (ceil(d_state * head_dim * n_head / BLOCK), n_seqs) 558template <int BLOCK_SIZE> 559__global__ void ssm_ssd_init_state_kernel( 560 const float * __restrict__ src0, // {d_state, head_dim, n_head, n_rs} 561 const int32_t * __restrict__ ids, // {n_seqs} 562 float * __restrict__ s_cur, // {d_state, head_dim, n_head, n_seqs} 563 const int state_size, // d_state * head_dim * n_head 564 const int64_t s0_stride_seq) { // elements between state rows 565 const int s = blockIdx.y; 566 const int idx = blockIdx.x * BLOCK_SIZE + threadIdx.x; 567 if (idx >= state_size) return; 568 569 const float * s_src = src0 + (int64_t)ids[s] * s0_stride_seq; 570 s_cur[s * state_size + idx] = s_src[idx]; 571} 572 573// SSD (State Space Duality) dispatch for Mamba-2 prefill. 574// Chunked matmuls: CB, materialize M + cuBLAS Y, S@C, B@X_dt. 575// All strides are in elements (floats), not bytes. 576static void ssm_scan_ssd_f32_cuda( 577 ggml_backend_cuda_context & ctx, 578 const float * src0_d, const float * src1_d, const float * src2_d, const float * src3_d, 579 const float * src4_d, const float * src5_d, const int32_t * src6_d, float * dst_d, 580 const int64_t s0_stride_seq, // state (src0) stride between seqs 581 const int x_stride_tok, const int x_stride_seq, // x (src1) strides 582 const int dt_stride_tok, const int dt_stride_seq, // dt (src2) strides 583 const int A_stride, // A (src3) stride between heads 584 const int B_stride_tok, const int B_stride_seq, // B (src4) strides 585 const int C_stride_tok, const int C_stride_seq, // C (src5) strides 586 const int64_t s_off, const int64_t d_state, const int64_t head_dim, 587 const int64_t n_head, const int64_t n_group, const int64_t n_tok, const int64_t n_seq) { 588 589 cudaStream_t stream = ctx.stream(); 590 const int64_t d_inner = head_dim * n_head; 591 592 const int64_t chunk_size = SSM_SSD_CHUNK_SIZE; 593 const int64_t n_chunks = (n_tok + chunk_size - 1) / chunk_size; 594 595 const int64_t state_per_head = d_state * head_dim; 596 597 using matmul_t = half; 598 static constexpr cudaDataType_t matmul_dtype = CUDA_R_16F; 599 600 ggml_cuda_pool_alloc<float> dt_sp_buf(ctx.pool(), n_tok * n_head * n_seq); 601 ggml_cuda_pool_alloc<float> cs_buf(ctx.pool(), n_tok * n_head * n_seq); 602 ggml_cuda_pool_alloc<float> CB_buf(ctx.pool(), chunk_size * chunk_size * n_group * n_seq); 603 ggml_cuda_pool_alloc<matmul_t> X_dt_buf(ctx.pool(), chunk_size * head_dim * n_head * n_seq); 604 ggml_cuda_pool_alloc<matmul_t> B_w_buf(ctx.pool(), d_state * chunk_size * n_head * n_seq); 605 ggml_cuda_pool_alloc<float> C_s_buf(ctx.pool(), d_state * chunk_size * n_head * n_seq); 606 float * dt_sp = dt_sp_buf.get(); 607 float * cs = cs_buf.get(); 608 float * CB = CB_buf.get(); 609 matmul_t * X_dt = X_dt_buf.get(); 610 matmul_t * B_weighted = B_w_buf.get(); 611 float * C_scaled = C_s_buf.get(); 612 float * s_cur = (float *)((char *)dst_d + s_off); // write state directly to dst 613 614 // Step 1: softplus(dt) and parallel prefix sum over full sequence 615 { 616 dim3 grid(n_head, n_seq); 617 ssm_ssd_prepare_dt_kernel<SSM_SSD_DT_BLOCK, SSM_SSD_DT_MAX_ITEMS><<<grid, SSM_SSD_DT_BLOCK, 0, stream>>>( 618 src2_d, dt_sp, cs, n_head, n_tok, dt_stride_tok, dt_stride_seq); 619 CUDA_CHECK(cudaGetLastError()); 620 } 621 622 // Step 2: initialize running state from src0[ids[s]] 623 { 624 constexpr int BLOCK = 256; 625 const int64_t state_size = d_state * head_dim * n_head; 626 dim3 grid((state_size + BLOCK - 1) / BLOCK, n_seq); 627 ssm_ssd_init_state_kernel<BLOCK><<<grid, BLOCK, 0, stream>>>( 628 src0_d, src6_d, s_cur, state_size, s0_stride_seq); 629 CUDA_CHECK(cudaGetLastError()); 630 } 631 632 // Step 3: chunked SSD loop 633 // Per chunk: pre_matmul (incl. M) + 4 cuBLAS (CB, Y, S@C, state update) + scale_state 634 cublasHandle_t handle = ctx.cublas_handle(); 635 const float alpha_one = 1.0f; 636 const float beta_zero = 0.0f; 637 const float beta_one = 1.0f; 638 const int lda_C_src = C_stride_tok; // leading dim for C in CB = C^T @ B 639 const int ldb_B_src = B_stride_tok; // leading dim for B in CB = C^T @ B 640 641 // Scratch buffer for causal M matrix, reused across chunks (max size at chunk_size) 642 const int64_t n_M_max = chunk_size * chunk_size; 643 ggml_cuda_pool_alloc<half> M_buf(ctx.pool(), n_M_max * n_head * n_seq); 644 half * M_mat = M_buf.get(); 645 646 for (int64_t k = 0; k < n_chunks; k++) { 647 const int64_t chunk_offset = k * chunk_size; 648 const int64_t chunk_len = (chunk_offset + chunk_size <= n_tok) ? chunk_size : (n_tok - chunk_offset); 649 650 // 3a: CB = C^T @ B per group 651 for (int64_t s = 0; s < n_seq; s++) { 652 const float * C_s = src5_d + s * C_stride_seq + chunk_offset * C_stride_tok; 653 const float * B_s = src4_d + s * B_stride_seq + chunk_offset * B_stride_tok; 654 float * CB_s = CB + s * chunk_len * chunk_len * n_group; 655 656 if (n_group == 1) { 657 CUBLAS_CHECK(cublasSgemm(handle, CUBLAS_OP_T, CUBLAS_OP_N, 658 chunk_len, chunk_len, d_state, 659 &alpha_one, C_s, lda_C_src, B_s, ldb_B_src, 660 &beta_zero, CB_s, (int)chunk_len)); 661 } else { 662 CUBLAS_CHECK(cublasGemmStridedBatchedEx(handle, CUBLAS_OP_T, CUBLAS_OP_N, 663 chunk_len, chunk_len, d_state, 664 &alpha_one, 665 C_s, CUDA_R_32F, lda_C_src, d_state, 666 B_s, CUDA_R_32F, ldb_B_src, d_state, 667 &beta_zero, 668 CB_s, CUDA_R_32F, (int)chunk_len, (long long)(chunk_len * chunk_len), 669 n_group, 670 CUBLAS_COMPUTE_32F, CUBLAS_GEMM_DEFAULT)); 671 } 672 } 673 674 // 3b: prepare X_dt, B_weighted, C_scaled + materialize causal M matrix 675 const int64_t n_M = chunk_len * chunk_len; 676 { 677 constexpr int BLOCK = 256; 678 const int64_t n_xdt = chunk_len * head_dim; 679 const int64_t n_bw = d_state * chunk_len; 680 int64_t max_work = n_xdt; 681 if (n_bw > max_work) max_work = n_bw; 682 if (n_M > max_work) max_work = n_M; 683 dim3 grid((max_work + BLOCK - 1) / BLOCK, n_head, n_seq); 684 ssm_ssd_pre_matmul_kernel<BLOCK, matmul_t><<<grid, BLOCK, 0, stream>>>( 685 cs, dt_sp, src3_d, src1_d, src4_d, src5_d, 686 X_dt, B_weighted, C_scaled, 687 CB, M_mat, 688 chunk_len, head_dim, n_head, n_group, d_state, A_stride, 689 x_stride_tok, x_stride_seq, B_stride_tok, B_stride_seq, C_stride_tok, C_stride_seq, 690 chunk_offset, n_tok); 691 CUDA_CHECK(cudaGetLastError()); 692 } 693 694 // 3c: dst = S_cur^T @ C_scaled (state contribution) 695 { 696 const int64_t stride_S = state_per_head; 697 const int64_t stride_Cs = d_state * chunk_len; 698 699 for (int64_t s = 0; s < n_seq; s++) { 700 float * dst_chunk = dst_d + s * d_inner * n_tok + chunk_offset * d_inner; 701 702 CUBLAS_CHECK(cublasGemmStridedBatchedEx(handle, CUBLAS_OP_T, CUBLAS_OP_N, 703 head_dim, chunk_len, d_state, 704 &alpha_one, 705 s_cur + s * stride_S * n_head, CUDA_R_32F, d_state, stride_S, 706 C_scaled + s * stride_Cs * n_head, CUDA_R_32F, d_state, stride_Cs, 707 &beta_zero, 708 dst_chunk, CUDA_R_32F, d_inner, head_dim, 709 n_head, 710 CUBLAS_COMPUTE_32F, CUBLAS_GEMM_DEFAULT)); 711 } 712 } 713 714 // 3d: dst += X_dt @ M^T (intra-chunk contribution, adds to 3c result) 715 // M is stored as M[t_out, t_in] (lower-triangular), transpose needed for Y = X @ M^T. 716 { 717 const int64_t stride_M = n_M; 718 const int64_t stride_X_h = (int64_t)chunk_len * head_dim; 719 720 for (int64_t s = 0; s < n_seq; s++) { 721 float * dst_chunk = dst_d + s * d_inner * n_tok + chunk_offset * d_inner; 722 CUBLAS_CHECK(cublasGemmStridedBatchedEx(handle, CUBLAS_OP_N, CUBLAS_OP_T, 723 head_dim, chunk_len, chunk_len, 724 &alpha_one, 725 X_dt + s * stride_X_h * n_head, matmul_dtype, head_dim, stride_X_h, 726 M_mat + s * stride_M * n_head, matmul_dtype, chunk_len, stride_M, 727 &beta_one, 728 dst_chunk, CUDA_R_32F, d_inner, head_dim, 729 n_head, 730 CUBLAS_COMPUTE_32F, CUBLAS_GEMM_DEFAULT)); 731 } 732 } 733 734 // 3e: s_cur = B_weighted @ X_dt^T + decay_total * s_cur_old (state update) 735 { 736 // Scale s_cur in-place by per-head decay_total BEFORE cuBLAS overwrites it 737 constexpr int BLOCK = 256; 738 dim3 grid((state_per_head + BLOCK - 1) / BLOCK, n_head, n_seq); 739 ssm_ssd_scale_state_kernel<BLOCK><<<grid, BLOCK, 0, stream>>>( 740 s_cur, cs, src3_d, 741 d_state, head_dim, n_head, 742 chunk_offset, chunk_len, n_tok, A_stride); 743 CUDA_CHECK(cudaGetLastError()); 744 745 // cuBLAS with beta=1: s_cur = B_weighted @ X_dt^T + 1.0 * s_cur (already scaled) 746 const int64_t stride_Bw = d_state * chunk_len; 747 const int64_t stride_X = chunk_len * head_dim; 748 const int64_t stride_S = state_per_head; 749 750 for (int64_t s = 0; s < n_seq; s++) { 751 // X_dt is d-fastest {hd, C}, read as OP_T to get {C, hd} 752 CUBLAS_CHECK(cublasGemmStridedBatchedEx(handle, CUBLAS_OP_N, CUBLAS_OP_T, 753 d_state, head_dim, chunk_len, 754 &alpha_one, 755 B_weighted + s * stride_Bw * n_head, matmul_dtype, d_state, stride_Bw, 756 X_dt + s * stride_X * n_head, matmul_dtype, head_dim, stride_X, 757 &beta_one, 758 s_cur + s * stride_S * n_head, CUDA_R_32F, d_state, stride_S, 759 n_head, 760 CUBLAS_COMPUTE_32F, CUBLAS_GEMM_DEFAULT)); 761 } 762 } 763 } 764} 765#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA) 766 767void ggml_cuda_op_ssm_scan(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { 768 const struct ggml_tensor * src0 = dst->src[0]; // s 769 const struct ggml_tensor * src1 = dst->src[1]; // x 770 const struct ggml_tensor * src2 = dst->src[2]; // dt 771 const struct ggml_tensor * src3 = dst->src[3]; // A 772 const struct ggml_tensor * src4 = dst->src[4]; // B 773 const struct ggml_tensor * src5 = dst->src[5]; // C 774 const struct ggml_tensor * src6 = dst->src[6]; // ids 775 776 const int64_t nc = src0->ne[0]; // d_state 777 const int64_t nr = src0->ne[1]; // head_dim or 1 778 const int64_t nh = src1->ne[1]; // n_head 779 const int64_t ng = src4->ne[1]; // n_group 780 const int64_t n_t = src1->ne[2]; // number of tokens per sequence 781 const int64_t n_s = src1->ne[3]; // number of sequences in the batch 782 const int32_t K_param = ggml_get_op_params_i32(dst, 0); 783 const int64_t K = K_param > 0 ? K_param : 1; 784 785 const int64_t s_off = ggml_nelements(src1) * sizeof(float); 786 787 GGML_ASSERT(ggml_nelements(src1) + K*nc*nr*nh*n_s == ggml_nelements(dst)); 788 GGML_ASSERT(src0->nb[0] == sizeof(float)); 789 GGML_ASSERT(src1->nb[0] == sizeof(float)); 790 GGML_ASSERT(src2->nb[0] == sizeof(float)); 791 GGML_ASSERT(src3->nb[0] == sizeof(float)); 792 GGML_ASSERT(src4->nb[0] == sizeof(float)); 793 GGML_ASSERT(src5->nb[0] == sizeof(float)); 794 GGML_ASSERT(src6->nb[0] == sizeof(int32_t)); 795 GGML_ASSERT(src3->ne[0] == 1 || K == 1); 796 797 const float * src0_d = (const float *) src0->data; 798 const float * src1_d = (const float *) src1->data; 799 const float * src2_d = (const float *) src2->data; 800 const float * src3_d = (const float *) src3->data; 801 const float * src4_d = (const float *) src4->data; 802 const float * src5_d = (const float *) src5->data; 803 const int32_t * src6_d = (const int32_t *) src6->data; 804 float * dst_d = (float *) dst->data; 805 cudaStream_t stream = ctx.stream(); 806 807 GGML_ASSERT(src0->type == GGML_TYPE_F32); 808 GGML_ASSERT(src6->type == GGML_TYPE_I32); 809 GGML_ASSERT(dst->type == GGML_TYPE_F32); 810 811 // Byte strides are narrowed to int for both scan and SSD paths. 812 GGML_ASSERT(src0->nb[2] <= (size_t)INT_MAX); 813 GGML_ASSERT(src0->nb[3] <= (size_t)INT_MAX); 814 GGML_ASSERT(src1->nb[2] <= (size_t)INT_MAX); 815 GGML_ASSERT(src1->nb[3] <= (size_t)INT_MAX); 816 GGML_ASSERT(src2->nb[1] <= (size_t)INT_MAX); 817 GGML_ASSERT(src2->nb[2] <= (size_t)INT_MAX); 818 GGML_ASSERT(src3->nb[1] <= (size_t)INT_MAX); 819 GGML_ASSERT(src4->nb[2] <= (size_t)INT_MAX); 820 GGML_ASSERT(src4->nb[3] <= (size_t)INT_MAX); 821 GGML_ASSERT(src5->nb[2] <= (size_t)INT_MAX); 822 GGML_ASSERT(src5->nb[3] <= (size_t)INT_MAX); 823 824#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA) 825 // Mamba-2 with scalar A per head: use SSD matmul path for long sequences. 826 // Requires NVIDIA Turing+ otherwise fallback to scan. 827 const bool is_mamba2 = (src3->nb[1] == sizeof(float)); 828 const int cc = ggml_cuda_info().devices[ggml_cuda_get_device()].cc; 829 const bool use_ssd = is_mamba2 && n_t > SSM_SSD_MIN_TOKENS 830 && K == 1 831 && n_t <= SSM_SSD_MAX_TOKENS 832 && GGML_CUDA_CC_IS_NVIDIA(cc) 833 && cc >= GGML_CUDA_CC_TURING 834 && nr % 8 == 0; // cuBLAS requires 8-element (16-byte) alignment 835 836 if (use_ssd) { 837 // ssm_ssd_init_state_kernel uses flat linear indexing within each sequence, 838 // so src0 must be fully contiguous across all inner dimensions. 839 // The scan path handles non-contiguous nb[2] via src0_nb2 but does not handle nb[1]. 840 GGML_ASSERT(src0->nb[1] == nc * sizeof(float)); 841 GGML_ASSERT(src0->nb[2] == nc * nr * sizeof(float)); 842 843 ssm_scan_ssd_f32_cuda(ctx, 844 src0_d, src1_d, src2_d, src3_d, src4_d, src5_d, src6_d, dst_d, 845 (int64_t)(src0->nb[3] / sizeof(float)), 846 (int)(src1->nb[2] / sizeof(float)), (int)(src1->nb[3] / sizeof(float)), 847 (int)(src2->nb[1] / sizeof(float)), (int)(src2->nb[2] / sizeof(float)), 848 (int)(src3->nb[1] / sizeof(float)), 849 (int)(src4->nb[2] / sizeof(float)), (int)(src4->nb[3] / sizeof(float)), 850 (int)(src5->nb[2] / sizeof(float)), (int)(src5->nb[3] / sizeof(float)), 851 s_off, nc, nr, nh, ng, n_t, n_s); 852 return; 853 } 854#endif 855 ssm_scan_f32_cuda(src0_d, src1_d, src2_d, src3_d, src4_d, src5_d, src6_d, dst_d, 856 src0->nb[2], src0->nb[3], src1->nb[2], src1->nb[3], src2->nb[1], src2->nb[2], 857 src3->nb[1], src4->nb[2], src4->nb[3], src5->nb[2], src5->nb[3], 858 s_off, nc, nr, nh, ng, n_t, n_s, K, stream); 859}