← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
ssm_kernels.c
Go to the documentation of this file.
1/**
2 * @file ssm_kernels.c
3 * @brief FP32 SSM causal depthwise convolution kernels for qwen3next/Qwen3.5.
4 *
5 * CK-ENGINE KERNEL RULES:
6 * =======================
7 * 1. NO malloc/free - memory via bump allocator, pointers passed in
8 * 2. NO OpenMP - parallelization at orchestrator/codegen layer
9 * 3. API must define: inputs, outputs, workspace, and memory layouts
10 * 4. Pure computation - deterministic, no side effects
11 *
12 * After changes: make test-ssm-conv && make test-kernels
13 *
14 * This file implements the GGML_OP_SSM_CONV semantics used by qwen3next before
15 * the recurrent DeltaNet update:
16 * out[seq, token, ch] = dot(conv_x[seq, ch, token:token+kernel], kernel[ch, :])
17 *
18 * Memory layouts:
19 * conv_x : [num_seqs, num_channels, kernel_size - 1 + num_tokens]
20 * kernel : [num_channels, kernel_size]
21 * out : [num_seqs, num_tokens, num_channels]
22 * d_out : same as out
23 * d_conv_x : same as conv_x
24 * d_kernel : same as kernel
25 */
26
27#include "bf16_utils.h"
28#include "ckernel_engine.h"
29#include "ck_threadpool.h"
30
31#include <math.h>
32#include <stddef.h>
33#include <string.h>
34
35#if defined(CK_TARGET_X86)
36#include <immintrin.h>
37#endif
38
39void ssm_conv1d_forward_ref(const float *conv_x,
40 const float *kernel,
41 float *out,
42 int kernel_size,
43 int num_channels,
44 int num_tokens,
45 int num_seqs)
46{
47 if (!conv_x || !kernel || !out) {
48 return;
49 }
50 if (kernel_size <= 0 || num_channels <= 0 || num_tokens < 0 || num_seqs <= 0) {
51 return;
52 }
53
54 const size_t seq_width = (size_t)kernel_size - 1u + (size_t)num_tokens;
55 const size_t conv_seq_stride = (size_t)num_channels * seq_width;
56 const size_t out_seq_stride = (size_t)num_tokens * (size_t)num_channels;
57
58 for (int seq = 0; seq < num_seqs; ++seq) {
59 const float *conv_seq = conv_x + (size_t)seq * conv_seq_stride;
60 float *out_seq = out + (size_t)seq * out_seq_stride;
61
62 for (int tok = 0; tok < num_tokens; ++tok) {
63 float *out_tok = out_seq + (size_t)tok * (size_t)num_channels;
64
65 for (int ch = 0; ch < num_channels; ++ch) {
66 const float *conv_row = conv_seq + (size_t)ch * seq_width + (size_t)tok;
67 const float *kernel_row = kernel + (size_t)ch * (size_t)kernel_size;
68 float sumf = 0.0f;
69 for (int k = 0; k < kernel_size; ++k) {
70 sumf += conv_row[k] * kernel_row[k];
71 }
72 out_tok[ch] = sumf;
73 }
74 }
75 }
76}
77
78void ssm_conv1d_backward_ref(const float *d_out,
79 const float *conv_x,
80 const float *kernel,
81 float *d_conv_x,
82 float *d_kernel,
83 int kernel_size,
84 int num_channels,
85 int num_tokens,
86 int num_seqs)
87{
88 if (!d_out || !conv_x || !kernel || !d_conv_x || !d_kernel) {
89 return;
90 }
91 if (kernel_size <= 0 || num_channels <= 0 || num_tokens < 0 || num_seqs <= 0) {
92 return;
93 }
94
95 const size_t seq_width = (size_t)kernel_size - 1u + (size_t)num_tokens;
96 const size_t conv_total = (size_t)num_seqs * (size_t)num_channels * seq_width;
97 const size_t kernel_total = (size_t)num_channels * (size_t)kernel_size;
98 const size_t conv_seq_stride = (size_t)num_channels * seq_width;
99 const size_t out_seq_stride = (size_t)num_tokens * (size_t)num_channels;
100
101 memset(d_conv_x, 0, conv_total * sizeof(float));
102 memset(d_kernel, 0, kernel_total * sizeof(float));
103
104 for (int seq = 0; seq < num_seqs; ++seq) {
105 const float *d_out_seq = d_out + (size_t)seq * out_seq_stride;
106 const float *conv_seq = conv_x + (size_t)seq * conv_seq_stride;
107 float *d_conv_seq = d_conv_x + (size_t)seq * conv_seq_stride;
108
109 for (int tok = 0; tok < num_tokens; ++tok) {
110 const float *d_out_tok = d_out_seq + (size_t)tok * (size_t)num_channels;
111
112 for (int ch = 0; ch < num_channels; ++ch) {
113 const float grad = d_out_tok[ch];
114 const float *conv_row = conv_seq + (size_t)ch * seq_width + (size_t)tok;
115 float *d_conv_row = d_conv_seq + (size_t)ch * seq_width + (size_t)tok;
116 const float *kernel_row = kernel + (size_t)ch * (size_t)kernel_size;
117 float *d_kernel_row = d_kernel + (size_t)ch * (size_t)kernel_size;
118
119 for (int k = 0; k < kernel_size; ++k) {
120 d_kernel_row[k] += grad * conv_row[k];
121 d_conv_row[k] += grad * kernel_row[k];
122 }
123 }
124 }
125 }
126}
127
128void ssm_conv1d_forward(const float *conv_x,
129 const float *kernel,
130 float *out,
131 int kernel_size,
132 int num_channels,
133 int num_tokens,
134 int num_seqs)
135{
136 ssm_conv1d_forward_ref(conv_x, kernel, out, kernel_size, num_channels, num_tokens, num_seqs);
137}
138
139/*
140 * llama.cpp's production GGML_OP_SSM_CONV CPU implementation accumulates
141 * every channel/token dot product as an ascending scalar multiply/add chain.
142 * Preserve the rounded product before each addition: contraction or
143 * vectorization changes FP32 rounding boundaries.
144 */
145typedef struct {
146 const float *conv_x;
147 const float *kernel;
148 float *out;
149 int kernel_size;
150 int num_channels;
151 int num_tokens;
152 int num_seqs;
153} ck_ssm_conv1d_llama_args_t;
154
155static void ck_ssm_conv1d_llama_channel_range(int begin, int end, void *opaque)
156{
157 const ck_ssm_conv1d_llama_args_t *args =
158 (const ck_ssm_conv1d_llama_args_t *)opaque;
159 const int kernel_size = args->kernel_size;
160 const int num_channels = args->num_channels;
161 const int num_tokens = args->num_tokens;
162 const size_t seq_width = (size_t)kernel_size - 1u + (size_t)num_tokens;
163 const size_t conv_seq_stride = (size_t)num_channels * seq_width;
164 const size_t out_seq_stride = (size_t)num_tokens * (size_t)num_channels;
165
166 for (int seq = 0; seq < args->num_seqs; ++seq) {
167 const float *conv_seq = args->conv_x + (size_t)seq * conv_seq_stride;
168 float *out_seq = args->out + (size_t)seq * out_seq_stride;
169
170 for (int ch = begin; ch < end; ++ch) {
171 const float *kernel_row =
172 args->kernel + (size_t)ch * (size_t)kernel_size;
173 const float *conv_channel = conv_seq + (size_t)ch * seq_width;
174 for (int tok = 0; tok < num_tokens; ++tok) {
175 const float *conv_row =
176 conv_channel + (size_t)tok;
177 float sum = 0.0f;
178 for (int k = 0; k < kernel_size; ++k) {
179 volatile float product = conv_row[k] * kernel_row[k];
180 volatile float next = sum + product;
181 sum = next;
182 }
183 out_seq[(size_t)tok * (size_t)num_channels + (size_t)ch] = sum;
184 }
185 }
186 }
187}
188
190 const float *kernel,
191 float *out,
192 int kernel_size,
193 int num_channels,
194 int num_tokens,
195 int num_seqs)
196{
197 if (!conv_x || !kernel || !out || kernel_size <= 0 || num_channels <= 0 ||
198 num_tokens < 0 || num_seqs <= 0) {
199 return;
200 }
201 ck_ssm_conv1d_llama_args_t args = {
202 conv_x, kernel, out, kernel_size, num_channels, num_tokens, num_seqs,
203 };
204 ck_ssm_conv1d_llama_channel_range(0, num_channels, &args);
205}
206
207void ssm_conv1d_forward_llama_production(const float *conv_x,
208 const float *kernel,
209 float *out,
210 int kernel_size,
211 int num_channels,
212 int num_tokens,
213 int num_seqs)
214{
215 if (!conv_x || !kernel || !out || kernel_size <= 0 || num_channels <= 0 ||
216 num_tokens < 0 || num_seqs <= 0) {
217 return;
218 }
219 ck_ssm_conv1d_llama_args_t args = {
220 conv_x, kernel, out, kernel_size, num_channels, num_tokens, num_seqs,
221 };
222 ck_threadpool_t *pool = ck_threadpool_global();
223 const int workers = pool ? ck_threadpool_n_threads(pool) : 1;
224 const int active = workers < num_channels ? workers : num_channels;
225 if (active > 1 && num_tokens > 1) {
227 pool, active, 0, num_channels, 32,
229 } else {
230 ck_ssm_conv1d_llama_channel_range(0, num_channels, &args);
231 }
232}
233
234/*
235 * Explicit contracted counterpart to the separated multiply/add provider.
236 * Some llama.cpp x86 builds compile GGML_OP_SSM_CONV's source expression to
237 * this arithmetic. Keep it distinct so flags cannot silently change the
238 * numerical contract.
239 */
241 int begin, int end, void *opaque)
242{
243 const ck_ssm_conv1d_llama_args_t *args =
244 (const ck_ssm_conv1d_llama_args_t *)opaque;
245 const int kernel_size = args->kernel_size;
246 const int num_channels = args->num_channels;
247 const int num_tokens = args->num_tokens;
248 const size_t seq_width = (size_t)kernel_size - 1u + (size_t)num_tokens;
249 const size_t conv_seq_stride = (size_t)num_channels * seq_width;
250 const size_t out_seq_stride = (size_t)num_tokens * (size_t)num_channels;
251
252 for (int seq = 0; seq < args->num_seqs; ++seq) {
253 const float *conv_seq = args->conv_x + (size_t)seq * conv_seq_stride;
254 float *out_seq = args->out + (size_t)seq * out_seq_stride;
255
256 for (int ch = begin; ch < end; ++ch) {
257 const float *kernel_row =
258 args->kernel + (size_t)ch * (size_t)kernel_size;
259 const float *conv_channel = conv_seq + (size_t)ch * seq_width;
260 for (int tok = 0; tok < num_tokens; ++tok) {
261 const float *conv_row = conv_channel + (size_t)tok;
262 float sum = 0.0f;
263 for (int k = 0; k < kernel_size; ++k) {
264 sum = fmaf(conv_row[k], kernel_row[k], sum);
265 }
266 out_seq[(size_t)tok * (size_t)num_channels + (size_t)ch] = sum;
267 }
268 }
269 }
270}
271
272void ssm_conv1d_forward_llama_fma(const float *conv_x,
273 const float *kernel,
274 float *out,
275 int kernel_size,
276 int num_channels,
277 int num_tokens,
278 int num_seqs)
279{
280 if (!conv_x || !kernel || !out || kernel_size <= 0 || num_channels <= 0 ||
281 num_tokens < 0 || num_seqs <= 0) {
282 return;
283 }
284 ck_ssm_conv1d_llama_args_t args = {
285 conv_x, kernel, out, kernel_size, num_channels, num_tokens, num_seqs,
286 };
287 ck_threadpool_t *pool = ck_threadpool_global();
288 const int workers = pool ? ck_threadpool_n_threads(pool) : 1;
289 const int active = workers < num_channels ? workers : num_channels;
290 if (active > 1 && num_tokens > 1) {
292 pool, active, 0, num_channels, 32,
294 } else {
295 ck_ssm_conv1d_llama_fma_channel_range(0, num_channels, &args);
296 }
297}
298
300 const float *kernel,
301 float *out,
302 int kernel_size,
303 int num_channels,
304 int num_tokens,
305 int num_seqs)
306{
308 conv_x, kernel, out, kernel_size, num_channels, num_tokens, num_seqs);
309 const size_t count =
310 (size_t)num_seqs * (size_t)num_tokens * (size_t)num_channels;
311 for (size_t i = 0; i < count; ++i) {
312 out[i] = bf16_to_float(float_to_bf16(out[i]));
313 }
314}
315
316void ssm_conv1d_backward(const float *d_out,
317 const float *conv_x,
318 const float *kernel,
319 float *d_conv_x,
320 float *d_kernel,
321 int kernel_size,
322 int num_channels,
323 int num_tokens,
324 int num_seqs)
325{
326 ssm_conv1d_backward_ref(d_out, conv_x, kernel, d_conv_x, d_kernel, kernel_size, num_channels, num_tokens, num_seqs);
327}
static uint16_t float_to_bf16(float f)
Definition bf16_utils.h:90
static float bf16_to_float(uint16_t v)
Definition bf16_utils.h:38
Persistent pthread thread pool for CK-Engine inference.
void ck_threadpool_parallel_for_n(ck_threadpool_t *pool, int active_threads, int begin, int end, int grain_size, ck_range_fn_t fn, void *args)
ck_threadpool_t * ck_threadpool_global(void)
int ck_threadpool_n_threads(const ck_threadpool_t *pool)
static void ck_ssm_conv1d_llama_channel_range(int begin, int end, void *opaque)
static void ck_ssm_conv1d_llama_fma_channel_range(int begin, int end, void *opaque)
void ssm_conv1d_forward_llama_production_serial(const float *conv_x, const float *kernel, float *out, int kernel_size, int num_channels, int num_tokens, int num_seqs)
void ssm_conv1d_forward_ref(const float *conv_x, const float *kernel, float *out, int kernel_size, int num_channels, int num_tokens, int num_seqs)
Definition ssm_kernels.c:39
void ssm_conv1d_forward_llama_fma(const float *conv_x, const float *kernel, float *out, int kernel_size, int num_channels, int num_tokens, int num_seqs)
void ssm_conv1d_backward_ref(const float *d_out, const float *conv_x, const float *kernel, float *d_conv_x, float *d_kernel, int kernel_size, int num_channels, int num_tokens, int num_seqs)
Definition ssm_kernels.c:78
void ssm_conv1d_forward_pytorch_bf16_storage(const float *conv_x, const float *kernel, float *out, int kernel_size, int num_channels, int num_tokens, int num_seqs)
void ssm_conv1d_forward(const float *conv_x, const float *kernel, float *out, int kernel_size, int num_channels, int num_tokens, int num_seqs)
void ssm_conv1d_forward_llama_production(const float *conv_x, const float *kernel, float *out, int kernel_size, int num_channels, int num_tokens, int num_seqs)
void ssm_conv1d_backward(const float *d_out, const float *conv_x, const float *kernel, float *d_conv_x, float *d_kernel, int kernel_size, int num_channels, int num_tokens, int num_seqs)
uint32_t end
Definition utf8.c:215