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