Skip to main content

pagesift_gpu/
exec_ops.rs

1//! [`BatchExec`] 的各个算子:语义同 `pagesift_infer::exec::Cpu`,批次维藏在 `self.n` 里。
2
3use std::rc::Rc;
4
5use pagesift_infer::exec::{Exec, attention_scale};
6use pagesift_infer::{Activation, Conv2d, ConvTranspose2d, LayerNorm, Linear};
7
8use crate::buffer::Args;
9use crate::exec::{BatchExec, DevTensor};
10use crate::ops::ConvShape;
11
12/// 图像特征的 `[C, H, W]`。
13fn chw(x: &DevTensor<'_, '_>) -> (usize, usize, usize) {
14    // [协议] 图像特征的约定形状是三维
15    assert_eq!(x.shape.len(), 3, "需要 [C, H, W],实际 {:?}", x.shape);
16    (x.shape[0], x.shape[1], x.shape[2])
17}
18
19/// 序列的 `[T, C]`。
20fn tc(x: &DevTensor<'_, '_>) -> (usize, usize) {
21    assert_eq!(x.shape.len(), 2, "需要 [T, C],实际 {:?}", x.shape);
22    (x.shape[0], x.shape[1])
23}
24
25/// 对各样本的有效宽度套同一个尺寸公式。
26fn map_widths(w: &[u32], f: impl Fn(usize) -> usize) -> Rc<Vec<u32>> {
27    Rc::new(w.iter().map(|&v| f(v as usize) as u32).collect())
28}
29
30impl<'c, 'g> BatchExec<'c, 'g> {
31    fn elems(&self, shape: &[usize]) -> usize {
32        self.n * shape.iter().product::<usize>()
33    }
34
35    fn new_tensor(&self, shape: Vec<usize>, widths: Rc<Vec<u32>>) -> DevTensor<'c, 'g> {
36        let len = self.elems(&shape);
37        self.alloc(len, shape, widths)
38    }
39}
40
41impl<'c, 'g> BatchExec<'c, 'g> {
42    /// 隐式卷积:见 [`ConvShape::implicit`]。权重按 `[kh, kw, o, c]` 重排后存放,每个核位置是连续的
43    /// `[o, c]`;输出先写到行距 `nw` 的临时缓冲,最后裁掉每行多余的列。每个通道算
44    /// `(oh − 1)·nw + ow` 列:最后一行只算有效的 `ow` 列,窗口才不会读出补边后的平面。
45    fn conv_implicit(
46        &self,
47        l: &Conv2d,
48        x: &DevTensor<'c, 'g>,
49        s: &ConvShape,
50        out: &DevTensor<'c, 'g>,
51        bias: Option<u64>,
52    ) {
53        let (oh, ow) = s.out_hw();
54        let (nh, nw) = (s.h + 2 * s.pad.0, s.w + 2 * s.pad.1);
55        let none = || Rc::new(Vec::new());
56        let k = &self.ctx.gpu.k;
57        let padded = self.new_tensor(vec![s.c, nh, nw], none());
58        let total = self.elems(&padded.shape);
59        let args = Args::default()
60            .raw_ptr(x.ptr())
61            .raw_ptr(padded.ptr())
62            .u32(self.n * s.c)
63            .u32(s.h)
64            .u32(s.w)
65            .u32(s.pad.0)
66            .u32(s.pad.1)
67            .u32(nh)
68            .u32(nw);
69        self.d1(k.pad_all, total, args);
70        let cols = (oh - 1) * nw + ow;
71        let wide = self.new_tensor(vec![s.o, cols], none());
72        let wt = self.weight_kernel_first(l);
73        let per_k = s.o * s.c; // 一个核位置的权重个数
74        let ldb = nh * nw;
75        for (i, (ky, kx)) in (0..s.kh)
76            .flat_map(|ky| (0..s.kw).map(move |kx| (ky, kx)))
77            .enumerate()
78        {
79            if !self.live() {
80                break;
81            }
82            let (a, b) = (
83                wt + bytes_of(i * per_k),
84                padded.ptr() + bytes_of(ky * nw + kx),
85            );
86            self.check(self.ctx.gpu.sgemm_ext(
87                a,
88                b,
89                wide.ptr(),
90                (s.o, cols, s.c),
91                ldb,
92                i > 0,
93                self.n,
94                (0, s.c * ldb, s.o * cols),
95            ));
96        }
97        let total = self.elems(&out.shape);
98        let args = Args::default()
99            .raw_ptr(wide.ptr())
100            .raw_ptr(out.ptr())
101            .u32(cols)
102            .u32(nw)
103            .u32(oh)
104            .u32(ow)
105            .u32(total);
106        self.d1(k.crop_cols, total, args);
107        if let Some(b) = bias {
108            let args = Args::default()
109                .raw_ptr(out.ptr())
110                .raw_ptr(b)
111                .u32(oh * ow)
112                .u32(s.o)
113                .u32(total);
114            self.d1(k.add_bias, total, args);
115        }
116    }
117}
118
119impl<'c, 'g> Exec for BatchExec<'c, 'g> {
120    type T = DevTensor<'c, 'g>;
121
122    fn shape(&self, x: &Self::T) -> Vec<usize> {
123        x.shape.clone()
124    }
125
126    fn conv(&self, l: &Conv2d, x: &Self::T) -> Self::T {
127        let (c, h, w) = chw(x);
128        let s = ConvShape {
129            c,
130            h,
131            w,
132            o: l.weight.shape[0],
133            kh: l.weight.shape[2],
134            kw: l.weight.shape[3],
135            stride: l.stride,
136            pad: l.pad,
137            dilation: l.dilation,
138            groups: l.groups,
139        };
140        let (oh, ow) = s.out_hw();
141        let widths = map_widths(&x.widths, |v| {
142            (v + 2 * s.pad.1 - s.dilation.1 * (s.kw - 1) - 1) / s.stride.1 + 1
143        });
144        let out = self.new_tensor(vec![s.o, oh, ow], widths);
145        let bias = l.bias.as_deref().map(|b| self.weight(b));
146        if s.implicit() {
147            self.conv_implicit(l, x, &s, &out, bias);
148            self.mask(&out);
149            return out;
150        }
151        let wt = self.weight(&l.weight.data);
152        // im2col 的临时缓冲也要算进试算的峰值
153        let col = s.needs_col().then(|| {
154            let len = self.n * s.col_len();
155            self.alloc(len, vec![len], Rc::default())
156        });
157        if self.live() {
158            self.check(self.ctx.gpu.conv_raw(
159                x.ptr(),
160                wt,
161                bias,
162                &s,
163                self.n,
164                out.ptr(),
165                col.as_ref().map(|t| t.ptr()),
166            ));
167        }
168        drop(col);
169        self.mask(&out);
170        out
171    }
172
173    fn conv_transpose(&self, l: &ConvTranspose2d, x: &Self::T) -> Self::T {
174        let (c, h, w) = chw(x);
175        let (o, kh, kw) = (l.weight.shape[1], l.weight.shape[2], l.weight.shape[3]);
176        let oh = (h - 1) * l.stride.0 + kh - 2 * l.pad.0;
177        let ow = (w - 1) * l.stride.1 + kw - 2 * l.pad.1;
178        let widths = map_widths(&x.widths, |v| (v - 1) * l.stride.1 + kw - 2 * l.pad.1);
179        let out = self.new_tensor(vec![o, oh, ow], widths);
180        let wt = self.weight(&l.weight.data);
181        let bias = l.bias.as_deref().map_or(0, |b| self.weight(b));
182        let total = self.elems(&out.shape);
183        let args = Args::default()
184            .raw_ptr(x.ptr())
185            .raw_ptr(wt)
186            .raw_ptr(bias)
187            .raw_ptr(out.ptr())
188            .u32(c)
189            .u32(o)
190            .u32(l.bias.is_some() as usize)
191            .u32(h)
192            .u32(w)
193            .u32(kh)
194            .u32(kw)
195            .u32(l.stride.0)
196            .u32(l.stride.1)
197            .u32(l.pad.0)
198            .u32(l.pad.1)
199            .u32(oh)
200            .u32(ow)
201            .u32(total);
202        self.d1(self.ctx.gpu.k.conv_transpose, total, args);
203        self.mask(&out);
204        out
205    }
206
207    fn act(&self, a: Activation, x: &mut Self::T) {
208        let k = &self.ctx.gpu.k;
209        let f = match a {
210            Activation::Identity => return,
211            Activation::Relu => k.relu,
212            Activation::Silu => k.silu,
213            Activation::Gelu => k.gelu,
214            Activation::HardSigmoid => k.hardsigmoid,
215            Activation::Sigmoid => k.sigmoid,
216        };
217        let total = self.elems(&x.shape);
218        self.d1(f, total, Args::default().raw_ptr(x.ptr()).u32(total));
219        // f(0) ≠ 0 的激活会把补边的列变成非零
220        // [协议] 序列是二维、图像特征是三维,只有图像特征有“列”要清
221        if a.apply(0.0) != 0.0 && x.shape.len() == 3 {
222            self.mask(x);
223        }
224    }
225
226    fn add(&self, a: &Self::T, b: &Self::T) -> Self::T {
227        assert_eq!(a.shape, b.shape, "相加的形状不同");
228        let out = self.new_tensor(a.shape.clone(), a.widths.clone());
229        let total = self.elems(&a.shape);
230        let args = Args::default()
231            .raw_ptr(a.ptr())
232            .raw_ptr(b.ptr())
233            .raw_ptr(out.ptr())
234            .u32(total);
235        self.d1(self.ctx.gpu.k.add2, total, args);
236        out
237    }
238
239    fn add_assign(&self, a: &mut Self::T, b: &Self::T) {
240        assert_eq!(a.shape, b.shape, "相加的形状不同");
241        let total = self.elems(&a.shape);
242        let args = Args::default()
243            .raw_ptr(a.ptr())
244            .raw_ptr(b.ptr())
245            .raw_ptr(a.ptr())
246            .u32(total);
247        self.d1(self.ctx.gpu.k.add2, total, args);
248    }
249
250    fn pad_right_bottom(&self, x: &Self::T, right: usize, bottom: usize) -> Self::T {
251        let (c, h, w) = chw(x);
252        let (nh, nw) = (h + bottom, w + right);
253        let out = self.new_tensor(vec![c, nh, nw], map_widths(&x.widths, |v| v + right));
254        let total = self.elems(&out.shape);
255        let args = Args::default()
256            .raw_ptr(x.ptr())
257            .raw_ptr(out.ptr())
258            .u32(self.n * c)
259            .u32(h)
260            .u32(w)
261            .u32(nh)
262            .u32(nw);
263        self.d1(self.ctx.gpu.k.pad_rb, total, args);
264        out
265    }
266
267    fn max_pool2d(
268        &self,
269        x: &Self::T,
270        k: (usize, usize),
271        stride: (usize, usize),
272        ceil_mode: bool,
273    ) -> Self::T {
274        let (c, h, w) = chw(x);
275        // 尺寸公式同 pagesift_infer::ops::max_pool2d
276        let size = |n: usize, k: usize, s: usize| {
277            if ceil_mode {
278                let o = (n - k).div_ceil(s) + 1;
279                if (o - 1) * s >= n { o - 1 } else { o }
280            } else {
281                (n - k) / s + 1
282            }
283        };
284        let (oh, ow) = (size(h, k.0, stride.0), size(w, k.1, stride.1));
285        let out = self.new_tensor(
286            vec![c, oh, ow],
287            map_widths(&x.widths, |v| size(v, k.1, stride.1)),
288        );
289        let total = self.elems(&out.shape);
290        let args = Args::default()
291            .raw_ptr(x.ptr())
292            .raw_ptr(out.ptr())
293            .u32(self.n * c)
294            .u32(h)
295            .u32(w)
296            .u32(k.0)
297            .u32(k.1)
298            .u32(stride.0)
299            .u32(stride.1)
300            .u32(oh)
301            .u32(ow);
302        self.d1(self.ctx.gpu.k.pool_max, total, args);
303        self.mask(&out);
304        out
305    }
306
307    fn avg_pool2d(&self, x: &Self::T, k: (usize, usize)) -> Self::T {
308        let (c, h, w) = chw(x);
309        let (oh, ow) = (h / k.0, w / k.1);
310        let out = self.new_tensor(vec![c, oh, ow], map_widths(&x.widths, |v| v / k.1));
311        let total = self.elems(&out.shape);
312        let args = Args::default()
313            .raw_ptr(x.ptr())
314            .raw_ptr(out.ptr())
315            .u32(self.n * c)
316            .u32(h)
317            .u32(w)
318            .u32(k.0)
319            .u32(k.1)
320            .u32(oh)
321            .u32(ow);
322        self.d1(self.ctx.gpu.k.pool_avg, total, args);
323        self.mask(&out);
324        out
325    }
326
327    fn global_avg(&self, x: &Self::T) -> Self::T {
328        let (c, h, w) = chw(x);
329        let out = self.new_tensor(vec![c, 1, 1], Rc::new(vec![1; self.n]));
330        let total = self.n * c;
331        let args = Args::default()
332            .raw_ptr(x.ptr())
333            .raw_ptr(out.ptr())
334            .raw_ptr(self.widths_ptr(&x.widths))
335            .u32(c)
336            .u32(h)
337            .u32(w)
338            .u32(total);
339        self.d1(self.ctx.gpu.k.global_avg, total, args);
340        out
341    }
342
343    fn scale_channels(&self, x: &mut Self::T, s: &Self::T) {
344        let (_, h, w) = chw(x);
345        let total = self.elems(&x.shape);
346        let args = Args::default()
347            .raw_ptr(x.ptr())
348            .raw_ptr(s.ptr())
349            .u32(h * w)
350            .u32(total);
351        self.d1(self.ctx.gpu.k.scale_ch, total, args);
352    }
353
354    fn se_gate(&self, s: &mut Self::T) {
355        let total = self.elems(&s.shape);
356        let args = Args::default().raw_ptr(s.ptr()).u32(total);
357        self.d1(self.ctx.gpu.k.se_gate, total, args);
358    }
359
360    fn concat(&self, xs: &[&Self::T]) -> Self::T {
361        let (_, h, w) = chw(xs[0]);
362        let c: usize = xs.iter().map(|x| chw(x).0).sum();
363        let out = self.new_tensor(vec![c, h, w], xs[0].widths.clone());
364        let stride = c * h * w;
365        let mut at = 0usize;
366        for x in xs {
367            let (xc, xh, xw) = chw(x);
368            assert_eq!((xh, xw), (h, w), "拼接的张量尺寸不同");
369            let len = xc * h * w;
370            let args = Args::default()
371                .raw_ptr(x.ptr())
372                .raw_ptr(out.ptr() + bytes_of(at))
373                .u32(len)
374                .u32(stride)
375                .u32(self.n * len);
376            self.d1(self.ctx.gpu.k.copy_part, self.n * len, args);
377            at += len;
378        }
379        out
380    }
381
382    fn upsample(&self, x: &Self::T, oh: usize, ow: usize) -> Self::T {
383        let (c, h, w) = chw(x);
384        let out = self.new_tensor(
385            vec![c, oh, ow],
386            map_widths(&x.widths, |v| (v * ow).div_ceil(w)),
387        );
388        let total = self.elems(&out.shape);
389        let args = Args::default()
390            .raw_ptr(x.ptr())
391            .raw_ptr(out.ptr())
392            .u32(self.n * c)
393            .u32(h)
394            .u32(w)
395            .u32(oh)
396            .u32(ow);
397        self.d1(self.ctx.gpu.k.interp_nearest, total, args);
398        self.mask(&out);
399        out
400    }
401
402    fn chw_to_seq(&self, x: &Self::T) -> Self::T {
403        let (c, h, w) = chw(x);
404        assert!(
405            h == 1 || x.widths.iter().all(|&v| v as usize == w),
406            "高不为 1 时各样本要一样宽,序列长度才说得清"
407        );
408        let hw = h * w;
409        let out = self.new_tensor(vec![hw, c], map_widths(&x.widths, |v| v * h));
410        let total = self.elems(&out.shape);
411        let args = Args::default()
412            .raw_ptr(x.ptr())
413            .raw_ptr(out.ptr())
414            .u32(c)
415            .u32(hw)
416            .u32(total);
417        self.d1(self.ctx.gpu.k.chw_to_seq, total, args);
418        out
419    }
420
421    fn linear(&self, l: &Linear, x: &Self::T) -> Self::T {
422        let (t, k) = tc(x);
423        let (kb, o) = (l.weight_t.shape[0], l.weight_t.shape[1]);
424        assert_eq!(k, kb, "矩阵乘的内维不同");
425        let out = self.new_tensor(vec![t, o], x.widths.clone());
426        let wt = self.weight(&l.weight_t.data);
427        let bias = l.bias.as_deref().map(|b| self.weight(b));
428        if self.live() {
429            self.check(self.ctx.gpu.sgemm_raw(
430                x.ptr(),
431                wt,
432                out.ptr(),
433                (t, o, k),
434                self.n,
435                (t * k, 0, t * o),
436            ));
437        }
438        if let Some(b) = bias {
439            // add_bias 按 (i / hw) % wrap 取偏置:hw = 1、wrap = o 就是按列加
440            let total = self.elems(&out.shape);
441            let args = Args::default()
442                .raw_ptr(out.ptr())
443                .raw_ptr(b)
444                .u32(1)
445                .u32(o)
446                .u32(total);
447            self.d1(self.ctx.gpu.k.add_bias, total, args);
448        }
449        out
450    }
451
452    fn layer_norm(&self, l: &LayerNorm, x: &Self::T) -> Self::T {
453        let (t, c) = tc(x);
454        let out = self.new_tensor(vec![t, c], x.widths.clone());
455        let (wt, bias) = (self.weight(&l.weight), self.weight(&l.bias));
456        let rows = self.n * t;
457        let args = Args::default()
458            .raw_ptr(x.ptr())
459            .raw_ptr(out.ptr())
460            .raw_ptr(wt)
461            .raw_ptr(bias)
462            .f32(l.eps)
463            .u32(c)
464            .u32(rows);
465        self.d1(self.ctx.gpu.k.layer_norm, rows, args);
466        out
467    }
468
469    fn attention(&self, qkv: &Self::T, heads: usize) -> Self::T {
470        let (t, c3) = tc(qkv);
471        let c = c3 / 3; // [协议] q、k、v 三份
472        let hd = c / heads;
473        let nh = self.n * heads;
474        let k = &self.ctx.gpu.k;
475        let none = || Rc::new(Vec::new());
476        let q = self.new_tensor(vec![heads, t, hd], none());
477        let kt = self.new_tensor(vec![heads, hd, t], none());
478        let v = self.new_tensor(vec![heads, t, hd], none());
479        let total = nh * t * hd;
480        let args = Args::default()
481            .raw_ptr(qkv.ptr())
482            .raw_ptr(q.ptr())
483            .raw_ptr(kt.ptr())
484            .raw_ptr(v.ptr())
485            .u32(t)
486            .u32(heads)
487            .u32(hd)
488            .u32(total);
489        self.d1(k.attn_split, total, args);
490        let a = self.new_tensor(vec![heads, t, t], none());
491        if self.live() {
492            self.check(self.ctx.gpu.sgemm_raw(
493                q.ptr(),
494                kt.ptr(),
495                a.ptr(),
496                (t, t, hd),
497                nh,
498                (t * hd, hd * t, t * t),
499            ));
500        }
501        drop((q, kt));
502        let rows = nh * t;
503        let args = Args::default()
504            .raw_ptr(a.ptr())
505            .raw_ptr(self.widths_ptr(&qkv.widths))
506            .f32(attention_scale(hd))
507            .u32(t)
508            .u32(heads)
509            .u32(rows);
510        self.d1(k.attn_softmax, rows, args);
511        let o = self.new_tensor(vec![heads, t, hd], none());
512        if self.live() {
513            self.check(self.ctx.gpu.sgemm_raw(
514                a.ptr(),
515                v.ptr(),
516                o.ptr(),
517                (t, hd, t),
518                nh,
519                (t * t, t * hd, t * hd),
520            ));
521        }
522        drop((a, v));
523        let out = self.new_tensor(vec![t, c], qkv.widths.clone());
524        let total = self.elems(&out.shape);
525        let args = Args::default()
526            .raw_ptr(o.ptr())
527            .raw_ptr(out.ptr())
528            .u32(t)
529            .u32(heads)
530            .u32(hd)
531            .u32(total);
532        self.d1(k.attn_merge, total, args);
533        out
534    }
535
536    fn ctc_digest(&self, logits: &Self::T) -> Vec<Vec<(u32, f32)>> {
537        let (t, n) = tc(logits);
538        let rows = self.n * t;
539        let out = self.new_tensor(vec![t, 2], logits.widths.clone());
540        let args = Args::default()
541            .raw_ptr(logits.ptr())
542            .raw_ptr(out.ptr())
543            .u32(n)
544            .u32(rows);
545        self.d1(self.ctx.gpu.k.ctc_digest, rows, args);
546        if !self.live() {
547            return vec![Vec::new(); self.n];
548        }
549        let host = match self.download(&out) {
550            Ok(h) => h,
551            Err(e) => {
552                self.fail(e);
553                return vec![Vec::new(); self.n];
554            }
555        };
556        logits
557            .widths
558            .iter()
559            .enumerate()
560            .map(|(z, &len)| {
561                host[z * t * 2..(z * t + len as usize) * 2]
562                    .as_chunks::<2>()
563                    .0
564                    .iter()
565                    .map(|p| (p[0].to_bits(), p[1]))
566                    .collect()
567            })
568            .collect()
569    }
570}
571
572/// `n` 个 f32 的字节数,用于设备地址的偏移。
573fn bytes_of(n: usize) -> u64 {
574    crate::pool::bytes(n)
575}