1use std::sync::Arc;
6
7use super::{WgpuContext, linear_grid};
8use crate::tensor::WgpuStorage;
9
10pub fn alloc_storage(ctx: &Arc<WgpuContext>, numel: usize) -> WgpuStorage {
11 alloc(ctx, numel)
12}
13
14fn alloc(ctx: &Arc<WgpuContext>, numel: usize) -> WgpuStorage {
15 WgpuStorage {
16 ctx: ctx.clone(),
17 buf: Arc::new(ctx.create_pooled(numel * 4)),
18 offset: 0,
19 }
20}
21
22fn alloc_zeroed(ctx: &Arc<WgpuContext>, numel: usize) -> WgpuStorage {
28 WgpuStorage {
29 ctx: ctx.clone(),
30 buf: Arc::new(ctx.create_zeroed(numel * 4)),
31 offset: 0,
32 }
33}
34
35fn bind(s: &WgpuStorage, numel: usize) -> (&wgpu::Buffer, usize, usize) {
36 (&s.buf, s.offset * 4, numel * 4)
37}
38
39pub fn add(a: &WgpuStorage, b: &WgpuStorage, n: usize) -> WgpuStorage {
40 let out = alloc(&a.ctx, n);
41 a.ctx.dispatch(
42 "add",
43 &[n as u32, 0, 0, 0],
44 &[bind(a, n), bind(b, n), bind(&out, n)],
45 linear_grid(n),
46 );
47 out
48}
49
50pub fn gelu(x: &WgpuStorage, n: usize) -> WgpuStorage {
51 let out = alloc(&x.ctx, n);
52 x.ctx.dispatch(
53 "gelu",
54 &[n as u32, 0, 0, 0],
55 &[bind(x, n), bind(&out, n)],
56 linear_grid(n),
57 );
58 out
59}
60
61#[allow(clippy::too_many_arguments)]
62pub fn matmul(
63 a: &WgpuStorage,
64 b: &WgpuStorage,
65 bias: Option<&WgpuStorage>,
66 m: usize,
67 k: usize,
68 n: usize,
69 batch: usize,
70 a_stride: usize,
71 b_stride: usize,
72 trans_a: bool,
73 trans_b: bool,
74 alpha: f32,
75) -> WgpuStorage {
76 let out = alloc(&a.ctx, batch * m * n);
77 matmul_into(
78 &out, a, b, bias, m, k, n, batch, a_stride, b_stride, trans_a, trans_b, alpha, 0, n,
79 );
80 out
81}
82
83#[allow(clippy::too_many_arguments)]
87pub fn matmul_into(
88 out: &WgpuStorage,
89 a: &WgpuStorage,
90 b: &WgpuStorage,
91 bias: Option<&WgpuStorage>,
92 m: usize,
93 k: usize,
94 n: usize,
95 batch: usize,
96 a_stride: usize,
97 b_stride: usize,
98 trans_a: bool,
99 trans_b: bool,
100 alpha: f32,
101 n_off: usize,
102 n_out: usize,
103) {
104 let ctx = &a.ctx;
105 let dummy;
107 let bias_binding = match bias {
108 Some(bias) => bind(bias, n),
109 None => {
110 dummy = alloc(ctx, 1);
111 bind(&dummy, 1)
112 }
113 };
114 let a_numel = if a_stride == 0 {
115 m * k
116 } else {
117 batch * a_stride
118 };
119 let b_numel = if b_stride == 0 {
120 k * n
123 } else {
124 batch * b_stride
125 };
126 let params = [
127 m as u32,
128 k as u32,
129 n as u32,
130 batch as u32,
131 a_stride as u32,
132 b_stride as u32,
133 trans_b as u32,
134 bias.is_some() as u32,
135 alpha.to_bits(),
136 n_off as u32,
137 n_out as u32,
138 trans_a as u32,
139 ];
140 ctx.dispatch(
141 "matmul",
142 ¶ms,
143 &[
144 bind(a, a_numel),
145 bind(b, b_numel),
146 bias_binding,
147 bind(out, batch * m * n_out),
148 ],
149 (n.div_ceil(16) as u32, m.div_ceil(16) as u32, batch as u32),
150 );
151}
152
153pub fn softmax(
154 x: &WgpuStorage,
155 rows: usize,
156 cols: usize,
157 q_len: usize,
158 causal: bool,
159 off: usize,
160) -> WgpuStorage {
161 let n = rows * cols;
162 let out = alloc(&x.ctx, n);
163 x.ctx.dispatch(
164 "softmax",
165 &[
166 rows as u32,
167 cols as u32,
168 q_len as u32,
169 causal as u32,
170 off as u32,
171 0,
172 0,
173 0,
174 ],
175 &[bind(x, n), bind(&out, n)],
176 (rows as u32, 1, 1),
177 );
178 out
179}
180
181pub fn layernorm(
182 x: &WgpuStorage,
183 gamma: &WgpuStorage,
184 beta: &WgpuStorage,
185 rows: usize,
186 cols: usize,
187 eps: f32,
188) -> WgpuStorage {
189 let n = rows * cols;
190 let out = alloc(&x.ctx, n);
191 x.ctx.dispatch(
192 "layernorm",
193 &[rows as u32, cols as u32, eps.to_bits(), 0],
194 &[
195 bind(x, n),
196 bind(gamma, cols),
197 bind(beta, cols),
198 bind(&out, n),
199 ],
200 (rows as u32, 1, 1),
201 );
202 out
203}
204
205#[allow(clippy::too_many_arguments)]
209pub fn embedding(
210 ids: &WgpuStorage,
211 chunks: &[(&WgpuStorage, usize)], chunk_rows: usize,
213 wpe: Option<&WgpuStorage>,
214 t: usize,
215 c: usize,
216 n_ctx: usize,
217 pos: usize,
218) -> WgpuStorage {
219 let ctx = &ids.ctx;
220 let n = t * c;
221 let out = alloc(ctx, n);
222 let dummy;
223 let wpe_binding = match wpe {
224 Some(wpe) => bind(wpe, n_ctx * c),
225 None => {
226 dummy = alloc(ctx, 1);
227 bind(&dummy, 1)
228 }
229 };
230 for (i, (chunk, rows)) in chunks.iter().enumerate() {
231 let row_start = i * chunk_rows;
232 ctx.dispatch(
233 "embedding",
234 &[
235 t as u32,
236 c as u32,
237 pos as u32,
238 wpe.is_some() as u32,
239 row_start as u32,
240 (row_start + rows) as u32,
241 0,
242 0,
243 ],
244 &[
245 bind(ids, t),
246 bind(chunk, rows * c),
247 wpe_binding,
248 bind(&out, n),
249 ],
250 linear_grid(n),
251 );
252 }
253 out
254}
255
256pub fn kv_append(
258 cache: &WgpuStorage,
259 src: &WgpuStorage,
260 h: usize,
261 t: usize,
262 hd: usize,
263 cap: usize,
264 len: usize,
265) {
266 let n = h * t * hd;
267 src.ctx.dispatch(
268 "kv_append",
269 &[
270 h as u32, t as u32, hd as u32, cap as u32, len as u32, 0, 0, 0,
271 ],
272 &[bind(src, n), bind(cache, h * cap * hd)],
273 linear_grid(n),
274 );
275}
276
277pub fn split_heads(
278 qkv: &WgpuStorage,
279 t: usize,
280 c: usize,
281 h: usize,
282) -> (WgpuStorage, WgpuStorage, WgpuStorage) {
283 let ctx = &qkv.ctx;
284 let n = t * c;
285 let (q, k, v) = (alloc(ctx, n), alloc(ctx, n), alloc(ctx, n));
286 ctx.dispatch(
287 "split_heads",
288 &[t as u32, c as u32, h as u32, 0],
289 &[bind(qkv, t * 3 * c), bind(&q, n), bind(&k, n), bind(&v, n)],
290 linear_grid(n),
291 );
292 (q, k, v)
293}
294
295pub fn merge_heads(x: &WgpuStorage, t: usize, c: usize, h: usize) -> WgpuStorage {
296 let n = t * c;
297 let out = alloc(&x.ctx, n);
298 x.ctx.dispatch(
299 "merge_heads",
300 &[t as u32, c as u32, h as u32, 0],
301 &[bind(x, n), bind(&out, n)],
302 linear_grid(n),
303 );
304 out
305}
306
307pub fn gelu_bwd(x: &WgpuStorage, dy: &WgpuStorage, n: usize) -> WgpuStorage {
310 let out = alloc(&x.ctx, n);
311 x.ctx.dispatch(
312 "gelu_bwd",
313 &[n as u32, 0, 0, 0],
314 &[bind(x, n), bind(dy, n), bind(&out, n)],
315 linear_grid(n),
316 );
317 out
318}
319
320pub fn softmax_bwd(y: &WgpuStorage, dy: &WgpuStorage, rows: usize, cols: usize) -> WgpuStorage {
321 let n = rows * cols;
322 let out = alloc(&y.ctx, n);
323 y.ctx.dispatch(
324 "softmax_bwd",
325 &[rows as u32, cols as u32, 0, 0],
326 &[bind(y, n), bind(dy, n), bind(&out, n)],
327 (rows as u32, 1, 1),
328 );
329 out
330}
331
332pub fn layernorm_bwd(
333 x: &WgpuStorage,
334 gamma: &WgpuStorage,
335 dy: &WgpuStorage,
336 rows: usize,
337 cols: usize,
338 eps: f32,
339) -> (WgpuStorage, WgpuStorage, WgpuStorage) {
340 let n = rows * cols;
341 let ctx = &x.ctx;
342 let dx = alloc(ctx, n);
343 let stats = alloc(ctx, rows * 2);
344 ctx.dispatch(
345 "layernorm_bwd_dx",
346 &[rows as u32, cols as u32, eps.to_bits(), 0],
347 &[
348 bind(x, n),
349 bind(gamma, cols),
350 bind(dy, n),
351 bind(&dx, n),
352 bind(&stats, rows * 2),
353 ],
354 (rows as u32, 1, 1),
355 );
356 let dgamma = alloc(ctx, cols);
357 let dbeta = alloc(ctx, cols);
358 ctx.dispatch(
359 "layernorm_bwd_dp",
360 &[rows as u32, cols as u32, 0, 0],
361 &[
362 bind(x, n),
363 bind(dy, n),
364 bind(&stats, rows * 2),
365 bind(&dgamma, cols),
366 bind(&dbeta, cols),
367 ],
368 linear_grid(cols),
369 );
370 (dx, dgamma, dbeta)
371}
372
373pub fn sum_rows(x: &WgpuStorage, rows: usize, cols: usize) -> WgpuStorage {
374 let out = alloc(&x.ctx, cols);
375 x.ctx.dispatch(
376 "sum_rows",
377 &[rows as u32, cols as u32, 0, 0],
378 &[bind(x, rows * cols), bind(&out, cols)],
379 linear_grid(cols),
380 );
381 out
382}
383
384pub fn scatter_add_rows(
386 dst: &WgpuStorage,
387 ids: &WgpuStorage,
388 src: &WgpuStorage,
389 t: usize,
390 c: usize,
391 dst_numel: usize,
392) {
393 src.ctx.dispatch(
394 "scatter_add",
395 &[t as u32, c as u32, 0, 0],
396 &[bind(ids, t), bind(src, t * c), bind(dst, dst_numel)],
397 linear_grid(t * c),
398 );
399}
400
401pub fn gather_nll(probs: &WgpuStorage, ids: &WgpuStorage, rows: usize, cols: usize) -> WgpuStorage {
402 let out = alloc(&probs.ctx, rows);
403 probs.ctx.dispatch(
404 "gather_nll",
405 &[rows as u32, cols as u32, 0, 0],
406 &[bind(probs, rows * cols), bind(ids, rows), bind(&out, rows)],
407 linear_grid(rows),
408 );
409 out
410}
411
412pub fn ce_bwd(
413 probs: &WgpuStorage,
414 ids: &WgpuStorage,
415 rows: usize,
416 cols: usize,
417 scale: f32,
418) -> WgpuStorage {
419 let n = rows * cols;
420 let out = alloc(&probs.ctx, n);
421 probs.ctx.dispatch(
422 "ce_bwd",
423 &[rows as u32, cols as u32, scale.to_bits(), 0],
424 &[bind(probs, n), bind(ids, rows), bind(&out, n)],
425 linear_grid(n),
426 );
427 out
428}
429
430pub fn dropout(x: &WgpuStorage, n: usize, p: f32, scale: f32, seed: u32) -> WgpuStorage {
431 let out = alloc(&x.ctx, n);
432 x.ctx.dispatch(
433 "dropout",
434 &[n as u32, seed, p.to_bits(), scale.to_bits()],
435 &[bind(x, n), bind(&out, n)],
436 linear_grid(n),
437 );
438 out
439}
440
441pub fn unsplit_head(d: &WgpuStorage, t: usize, c: usize, h: usize, which: usize) -> WgpuStorage {
442 let n = t * c;
443 let out = alloc_zeroed(&d.ctx, t * 3 * c);
447 d.ctx.dispatch(
448 "unsplit_heads",
449 &[t as u32, c as u32, h as u32, which as u32],
450 &[bind(d, n), bind(&out, t * 3 * c)],
451 linear_grid(n),
452 );
453 out
454}
455
456pub fn unmerge_heads(dy: &WgpuStorage, t: usize, c: usize, h: usize) -> WgpuStorage {
457 let n = t * c;
458 let out = alloc(&dy.ctx, n);
459 dy.ctx.dispatch(
460 "unmerge_heads",
461 &[t as u32, c as u32, h as u32, 0],
462 &[bind(dy, n), bind(&out, n)],
463 linear_grid(n),
464 );
465 out
466}
467
468pub fn sumsq_partials(x: &WgpuStorage, n: usize) -> (WgpuStorage, usize) {
472 let grid = linear_grid(n);
473 let groups = (grid.0 * grid.1) as usize;
474 let out = alloc(&x.ctx, groups);
475 x.ctx.dispatch(
476 "sumsq",
477 &[n as u32, 0, 0, 0],
478 &[bind(x, n), bind(&out, groups)],
479 grid,
480 );
481 (out, groups)
482}
483
484pub fn scale(x: &WgpuStorage, n: usize, alpha: f32) -> WgpuStorage {
485 let out = alloc(&x.ctx, n);
486 x.ctx.dispatch(
487 "scale",
488 &[n as u32, alpha.to_bits(), 0, 0],
489 &[bind(x, n), bind(&out, n)],
490 linear_grid(n),
491 );
492 out
493}
494
495#[allow(clippy::too_many_arguments)]
497pub fn adamw(
498 param: &WgpuStorage,
499 grad: &WgpuStorage,
500 m: &WgpuStorage,
501 v: &WgpuStorage,
502 n: usize,
503 lr: f32,
504 beta1: f32,
505 beta2: f32,
506 eps: f32,
507 weight_decay: f32,
508 step: u32,
509) {
510 let bc1 = 1.0 - beta1.powi(step as i32);
511 let bc2 = 1.0 - beta2.powi(step as i32);
512 param.ctx.dispatch(
513 "adamw",
514 &[
515 n as u32,
516 lr.to_bits(),
517 beta1.to_bits(),
518 beta2.to_bits(),
519 eps.to_bits(),
520 weight_decay.to_bits(),
521 bc1.to_bits(),
522 bc2.to_bits(),
523 ],
524 &[bind(grad, n), bind(param, n), bind(m, n), bind(v, n)],
525 linear_grid(n),
526 );
527}