char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Max Krasnyanskyhexagon: support for multi-device model split (aka row-split) (#28589)eafe15a5e

char/aliceai-t5-moe
18.8 KiB456 linesraw
1#pragma clang diagnostic ignored "-Wunused-variable"
2#pragma clang diagnostic ignored "-Wunused-function"
3#pragma clang diagnostic ignored "-Wunused-but-set-variable"
4
5#include <HAP_farf.h>
6#include <HAP_mem.h>
7#include <HAP_ps.h>
8#include <hexagon_protos.h>
9#include <hexagon_types.h>
10#include <math.h>
11#include <qurt_thread.h>
12#include <string.h>
13
14#define GGML_COMMON_DECL_C
15#include "ggml-common.h"
16#include "htp-ctx.h"
17#include "hex-dma.h"
18#include "hex-profile.h"
19#include "htp-ops.h"
20#include "htp-tensor.h"
21#include "hvx-utils.h"
22
23#define htp_ssm_conv_tensors_preamble                           \
24    const struct htp_tensor * restrict src0 = octx->src[0];     \
25    const struct htp_tensor * restrict src1 = octx->src[1];     \
26    const struct htp_tensor * restrict dst  = octx->dst;        \
27    struct htp_spad * restrict src0_spad    = &octx->src0_spad; \
28    struct htp_spad * restrict src1_spad    = &octx->src1_spad; \
29    struct htp_spad * restrict dst_spad     = &octx->dst_spad;  \
30                                                                \
31    const uint32_t ne00 = src0->ne[0];                          \
32    const uint32_t ne01 = src0->ne[1];                          \
33    const uint32_t ne02 = src0->ne[2];                          \
34    const uint32_t ne03 = src0->ne[3];                          \
35                                                                \
36    const uint32_t ne10 = src1->ne[0];                          \
37    const uint32_t ne11 = src1->ne[1];                          \
38    const uint32_t ne12 = src1->ne[2];                          \
39    const uint32_t ne13 = src1->ne[3];                          \
40                                                                \
41    const uint32_t ne0 = dst->ne[0];                            \
42    const uint32_t ne1 = dst->ne[1];                            \
43    const uint32_t ne2 = dst->ne[2];                            \
44    const uint32_t ne3 = dst->ne[3];                            \
45                                                                \
46    const uint32_t nb00 = src0->nb[0];                          \
47    const uint32_t nb01 = src0->nb[1];                          \
48    const uint32_t nb02 = src0->nb[2];                          \
49    const uint32_t nb03 = src0->nb[3];                          \
50                                                                \
51    const uint32_t nb10 = src1->nb[0];                          \
52    const uint32_t nb11 = src1->nb[1];                          \
53    const uint32_t nb12 = src1->nb[2];                          \
54    const uint32_t nb13 = src1->nb[3];                          \
55                                                                \
56    const uint32_t nb0 = dst->nb[0];                            \
57    const uint32_t nb1 = dst->nb[1];                            \
58    const uint32_t nb2 = dst->nb[2];                            \
59    const uint32_t nb3 = dst->nb[3];
60
61struct htp_ssm_conv_context {
62    struct htp_ops_context * octx;
63    uint32_t nrows_per_thread;
64    uint32_t d_inner_tile;
65    uint64_t t_start;
66    uint32_t row_start;
67    uint32_t nrows;
68};
69
70#define htp_ssm_conv_preamble                                                   \
71    struct htp_ssm_conv_context * scctx = (struct htp_ssm_conv_context *) data; \
72    struct htp_ops_context *      octx  = scctx->octx;                          \
73    htp_ssm_conv_tensors_preamble;                                              \
74    dma_queue * dma_queue = octx->ctx->dma[ith];
75
76// Scalar FP32 SSM_CONV implementation
77static void ssm_conv_thread_f32_f32(unsigned int nth, unsigned int ith, void *data) {
78    htp_ssm_conv_preamble;
79
80    const uint32_t d_conv  = src1->ne[0];
81    const uint32_t d_inner = src0->ne[1];
82    const uint32_t n_t     = dst->ne[1];
83    const uint32_t n_s     = dst->ne[2];
84
85    const uint32_t src0_stride_inner = src0->nb[1] / sizeof(float); // stride for inner dimension
86    const uint32_t src0_stride_seq   = src0->nb[2] / sizeof(float); // stride for sequence dimension
87    const uint32_t src1_stride_inner = src1->nb[1] / sizeof(float); // stride for inner dimension
88    const uint32_t dst_stride_token  = dst->nb[1]  / sizeof(float); // stride for token dimension
89    const uint32_t dst_stride_seq    = dst->nb[2]  / sizeof(float); // stride for sequence dimension
90
91    const float * src0_data = (const float *) src0->data;
92    const float * src1_data = (const float *) src1->data;
93    float *       dst_data  = (float *) dst->data;
94
95    // Calculate row range for this thread
96    const uint32_t d_inner_per_thread = scctx->nrows_per_thread;
97    const uint32_t d_inner_start = scctx->row_start + d_inner_per_thread * ith;
98    const uint32_t d_inner_end   = MIN(d_inner_start + d_inner_per_thread, scctx->row_start + scctx->nrows);
99
100    // No work for this thread
101    if (d_inner_start >= d_inner_end) {
102        return;
103    }
104
105    struct htp_thread_trace * tr = &octx->ctx->trace[ith];
106    htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) d_inner_start);
107
108    for (uint32_t i3 = 0; i3 < n_s; ++i3) {
109        for (uint32_t i2 = 0; i2 < n_t; ++i2) {
110            for (uint32_t i1 = d_inner_start; i1 < d_inner_end; ++i1) {
111                float sumf = 0.0f;
112
113                for (uint32_t i0 = 0; i0 < d_conv; ++i0) {
114                    const uint32_t src0_idx = (i2 + i0) + i1 * src0_stride_inner + i3 * src0_stride_seq;
115                    const uint32_t src1_idx = i0 + i1 * src1_stride_inner;
116
117                    sumf += src0_data[src0_idx] * src1_data[src1_idx];
118                }
119
120                const uint32_t dst_idx = i1 + i2 * dst_stride_token + i3 * dst_stride_seq;
121                dst_data[dst_idx] = sumf;
122            }
123        }
124    }
125
126    htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) d_inner_end);
127
128    FARF(HIGH, "ssm-conv-f32 %d/%d: %ux%ux%ux%u (%u:%u) * %ux%ux%ux%u -> %ux%ux%ux%u\n",
129         ith, nth, src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], d_inner_start, d_inner_end,
130         src1->ne[0], src1->ne[1], src1->ne[2], src1->ne[3], dst->ne[0], dst->ne[1],
131         dst->ne[2], dst->ne[3]);
132}
133
134
135// In-register 32x32 fp32 transpose using std 5-stage HVX vshuff butterfly.
136static inline void hvx_transpose_32x32_f32(HVX_Vector m[32]) {
137    HVX_Vector tmp[32];
138
139    // Stage 0 (R = -4): pair (2i, 2i+1) for i = 0..15. m -> tmp.
140    for (int i = 0; i < 16; ++i) {
141        HVX_VectorPair p = Q6_W_vshuff_VVR(m[2*i + 1], m[2*i], -4);
142        tmp[2*i + 0] = Q6_V_lo_W(p);
143        tmp[2*i + 1] = Q6_V_hi_W(p);
144    }
145
146    // Stage 1 (R = -8): per block of 4, pair (b+0, b+2) and (b+1, b+3). tmp -> m.
147    for (int b = 0; b < 32; b += 4) {
148        HVX_VectorPair p0 = Q6_W_vshuff_VVR(tmp[b + 2], tmp[b + 0], -8);
149        HVX_VectorPair p1 = Q6_W_vshuff_VVR(tmp[b + 3], tmp[b + 1], -8);
150        m[b + 0] = Q6_V_lo_W(p0); m[b + 1] = Q6_V_hi_W(p0);
151        m[b + 2] = Q6_V_lo_W(p1); m[b + 3] = Q6_V_hi_W(p1);
152    }
153
154    // Stage 2 (R = -16): per block of 8, pair (b+i, b+i+4) for i = 0..3. m -> tmp.
155    for (int b = 0; b < 32; b += 8) {
156        for (int i = 0; i < 4; ++i) {
157            HVX_VectorPair p = Q6_W_vshuff_VVR(m[b + i + 4], m[b + i], -16);
158            tmp[b + 2*i + 0] = Q6_V_lo_W(p);
159            tmp[b + 2*i + 1] = Q6_V_hi_W(p);
160        }
161    }
162
163    // Stage 3 (R = -32): per block of 16, pair (b+i, b+i+8) for i = 0..7. tmp -> m.
164    for (int b = 0; b < 32; b += 16) {
165        for (int i = 0; i < 8; ++i) {
166            HVX_VectorPair p = Q6_W_vshuff_VVR(tmp[b + i + 8], tmp[b + i], -32);
167            m[b + 2*i + 0] = Q6_V_lo_W(p);
168            m[b + 2*i + 1] = Q6_V_hi_W(p);
169        }
170    }
171
172    // Stage 4 (R = -64): pair (i, i+16) for i = 0..15. m -> tmp -> m.
173    for (int i = 0; i < 16; ++i) {
174        HVX_VectorPair p = Q6_W_vshuff_VVR(m[i + 16], m[i], -64);
175        tmp[2 * i + 0]   = Q6_V_lo_W(p);
176        tmp[2 * i + 1]   = Q6_V_hi_W(p);
177    }
178
179    for (int i = 0; i < 32; ++i) {
180        m[i] = tmp[i];
181    }
182}
183
184// HVX FP32 SSM_CONV implementation - channel-vectorized HVX kernel with src0/src1
185// transposed into VTCM.
186//
187// VTCM layouts (per thread):
188//   src1_T : {d_inner_stride, d_conv}       - staged once per launch (small).
189//   src0_T : {d_inner_tile,     ncs}        - staged per d_inner-tile.
190//
191// d_inner_tile is chosen so that per-thread VTCM stays under the budget.
192// Each thread iterates ceil(d_inner_per_thread d_inner_tile) tiles serially.
193#define HTP_SSM_CONV_VTCM_BUDGET (1u << 20) // 1 MiB per thread
194
195// Scalar transpose: src1 {d_conv, d_inner} (DDR) -> {d_inner_stride, d_conv} (VTCM)
196static inline void transpose_src1(const float * src1_data,
197                                  uint32_t      src1_stride_inner,
198                                  uint32_t      i1_off,
199                                  uint32_t      d_inner_per_thread,
200                                  uint32_t      d_inner_stride,
201                                  uint32_t      d_conv,
202                                  float *       src1_T) {
203    for (uint32_t i = 0; i < d_inner_per_thread; ++i) {
204        const float * src_row = src1_data + (i1_off + i) * src1_stride_inner;
205        for (uint32_t j = 0; j < d_conv; ++j) {
206            src1_T[j * d_inner_stride + i] = src_row[j];
207        }
208    }
209}
210
211// HVX 32x32 src0 transpose: src0 {ncs, d_inner} (DDR) -> src0_T {d_inner_tile, ncs} (VTCM)
212static inline void transpose_src0_block(const float * src0_block,
213                                        uint32_t      ncs,
214                                        uint32_t      cb_n,
215                                        uint32_t      d_inner_tile,
216                                        float *       src0_T_block_dst,
217                                        uint32_t      cb /* dst column offset */) {
218    const uint32_t T_TILE = VLEN_FP32;
219
220    HVX_Vector __attribute__((aligned(VLEN))) sub[32];
221
222    for (uint32_t t0 = 0; t0 < ncs; t0 += T_TILE) {
223        const uint32_t t_n = MIN(T_TILE, ncs - t0);
224
225        // Load 32 rows (channels) of T_TILE samples; pad missing channels with zeros.
226        for (uint32_t r = 0; r < cb_n; ++r) {
227            const float * src_row = src0_block + r * ncs + t0;
228            if (t_n == T_TILE) {
229                sub[r] = *(const HVX_UVector *) src_row;
230            } else {
231                HVX_Vector v = hvx_vec_splat_f32(0.0f);
232                hvx_vec_store_u(&v, t_n * sizeof(float), hvx_vec_splat_f32(0.0f));
233
234                float __attribute__((aligned(VLEN))) tmp[VLEN_FP32] = { 0 };
235                for (uint32_t k = 0; k < t_n; ++k) tmp[k] = src_row[k];
236                v = *(const HVX_Vector *) tmp;
237                sub[r] = v;
238            }
239        }
240        for (uint32_t r = cb_n; r < T_TILE; ++r) {
241            sub[r] = hvx_vec_splat_f32(0.0f);
242        }
243
244        hvx_transpose_32x32_f32(sub);
245
246        // Store transposed sub-tile to src0_T at offsets (t0 + j) * d_inner_tile + cb.
247        // Only write the valid t_n rows of the transposed result.
248        for (uint32_t r = 0; r < t_n; ++r) {
249            float * dst = src0_T_block_dst + (t0 + r) * d_inner_tile + cb;
250            if (cb_n == T_TILE) {
251                *(HVX_UVector *) dst = sub[r];
252            } else {
253                hvx_vec_store_u(dst, cb_n * sizeof(float), sub[r]);
254            }
255        }
256    }
257}
258
259static void ssm_conv_thread_f32_f32_hvx(unsigned int nth, unsigned int ith, void *data) {
260    htp_ssm_conv_preamble;
261
262    const uint32_t d_conv  = src1->ne[0];
263    const uint32_t d_inner = src0->ne[1];
264    const uint32_t n_t     = dst->ne[1];
265    const uint32_t n_s     = dst->ne[2];
266    const uint32_t ncs     = src0->ne[0];
267
268    const uint32_t src0_stride_inner = src0->nb[1] / sizeof(float);
269    const uint32_t src0_stride_seq   = src0->nb[2] / sizeof(float);
270    const uint32_t src1_stride_inner = src1->nb[1] / sizeof(float);
271    const uint32_t dst_stride_token  = dst->nb[1]  / sizeof(float);
272    const uint32_t dst_stride_seq    = dst->nb[2]  / sizeof(float);
273
274    const uint32_t dr  = scctx->nrows_per_thread;
275    const uint32_t ir0 = scctx->row_start + dr * ith;
276    const uint32_t ir1 = MIN(ir0 + dr, scctx->row_start + scctx->nrows);
277
278    if (ir0 >= ir1) {
279        return;
280    }
281
282    struct htp_thread_trace * tr = &octx->ctx->trace[ith];
283    htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir0);
284
285    const uint32_t d_inner_per_thread = ir1 - ir0;
286    const uint32_t d_inner_stride     = scctx->nrows_per_thread;
287    const uint32_t d_inner_tile       = scctx->d_inner_tile;
288
289    const float * src0_data = (const float *) src0->data;
290    const float * src1_data = (const float *) src1->data;
291    float       * dst_data  = (float       *) dst->data;
292
293    // Per-thread VTCM regions.
294    float * src0_T = (float *)(octx->src0_spad.data + ith * octx->src0_spad.size_per_thread);
295    float * src1_T = (float *)(octx->src1_spad.data + ith * octx->src1_spad.size_per_thread);
296
297    // Stage src1 weights once into VTCM in {d_inner_stride, d_conv} layout.
298    transpose_src1(src1_data, src1_stride_inner, ir0, d_inner_per_thread, d_inner_stride, d_conv, src1_T);
299
300    const uint32_t C_TILE = VLEN_FP32;
301
302    for (uint32_t i3 = 0; i3 < n_s; ++i3) {
303        for (uint32_t tile_off = 0; tile_off < d_inner_per_thread; tile_off += d_inner_tile) {
304            const uint32_t tile_n = MIN(d_inner_tile, d_inner_per_thread - tile_off);
305
306            // Place src0 chunk into VTCM in {d_inner_tile, ncs} layout.
307            const float * src0_block = src0_data + i3 * src0_stride_seq + (ir0 + tile_off) * src0_stride_inner;
308
309            for (uint32_t cb = 0; cb < tile_n; cb += C_TILE) {
310                const uint32_t cb_n = MIN(C_TILE, tile_n - cb);
311                transpose_src0_block(src0_block + cb * src0_stride_inner, ncs, cb_n, d_inner_tile, src0_T, cb);
312            }
313
314            for (uint32_t t = 0; t < n_t; ++t) {
315                for (uint32_t cb = 0; cb < tile_n; cb += C_TILE) {
316                    const uint32_t cb_n = MIN(C_TILE, tile_n - cb);
317
318                    HVX_Vector acc = hvx_vec_splat_f32(0.0f);
319                    for (uint32_t j = 0; j < d_conv; ++j) {
320                        HVX_Vector x = *(const HVX_Vector *) (src0_T + (t + j) * d_inner_tile + cb);
321                        HVX_Vector w = *(const HVX_Vector *) (src1_T + j * d_inner_stride + tile_off + cb);
322                        acc          = Q6_Vqf32_vadd_Vqf32Vqf32(acc, Q6_Vqf32_vmpy_VsfVsf(x, w));
323                    }
324
325                    HVX_Vector y = Q6_Vsf_equals_Vqf32(acc);
326
327                    float * dst_ptr = dst_data + (ir0 + tile_off + cb) + t * dst_stride_token + i3 * dst_stride_seq;
328                    if (cb_n == C_TILE) {
329                        *(HVX_UVector *) dst_ptr = y;
330                    } else {
331                        hvx_vec_store_u(dst_ptr, cb_n * sizeof(float), y);
332                    }
333                }
334            }
335        }
336    }
337
338    htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir1);
339
340    FARF(HIGH, "ssm-conv-f32-hvx %d/%d: %ux%ux%ux%u (%u:%u) * %ux%ux%ux%u -> %ux%ux%ux%u\n",
341         ith, nth, src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], ir0, ir1,
342         src1->ne[0], src1->ne[1], src1->ne[2], src1->ne[3], dst->ne[0], dst->ne[1],
343         dst->ne[2], dst->ne[3]);
344}
345
346int op_ssm_conv_f32(struct htp_ops_context * octx) {
347    const struct htp_tensor * src0 = octx->src[0];
348    const struct htp_tensor * src1 = octx->src[1];
349    const struct htp_tensor * dst  = octx->dst;
350
351    if (src0->type != HTP_TYPE_F32 || src1->type != HTP_TYPE_F32 || dst->type != HTP_TYPE_F32) {
352        return HTP_STATUS_NO_SUPPORT;
353    }
354
355    const uint32_t d_conv  = src1->ne[0];
356    const uint32_t d_inner = src0->ne[1];
357    const uint32_t n_t     = dst->ne[1];  // tokens per sequence
358    const uint32_t n_s     = dst->ne[2];  // number of sequences in the batch
359
360    if (octx->flags & HTP_OPFLAGS_SKIP_COMPUTE) {
361        return HTP_STATUS_OK;
362    }
363
364    uint32_t row_start = 0;
365    uint32_t nrows     = d_inner;
366
367    if (octx->ctx->mdev.count > 1) {
368        const uint32_t elems_per_chunk = VLEN_FP32;
369        const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(d_inner, htp_tensor_mdev_data_aligned(dst) ? elems_per_chunk : 0, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
370        row_start = range.start;
371        nrows     = range.count;
372    }
373
374    if (nrows == 0) {
375        return HTP_STATUS_OK;
376    }
377
378    const uint32_t n_threads = octx->n_threads;
379
380    struct htp_ssm_conv_context scctx = { 0 };
381    scctx.octx      = octx;
382    scctx.row_start = row_start;
383    scctx.nrows     = nrows;
384
385    uint32_t use_hvx = 0;
386    if (nrows >= VLEN_FP32 && n_t >= VLEN_FP32) {
387        use_hvx = 1;
388    }
389
390    const uint32_t raw_rpt = fastdiv(nrows + n_threads - 1, &octx->n_threads_div);
391    scctx.nrows_per_thread = hex_round_up(raw_rpt, VLEN_FP32);
392
393    const uint32_t d_inner_per_thread = scctx.nrows_per_thread;
394    const uint32_t ncs                = src0->ne[0];
395
396    const uint32_t src1_T_size = hex_round_up(d_conv * d_inner_per_thread * sizeof(float), 256);
397    const uint32_t src0_T_max = HTP_SSM_CONV_VTCM_BUDGET > src1_T_size ? HTP_SSM_CONV_VTCM_BUDGET - src1_T_size : 0;
398
399    uint32_t d_inner_tile = (src0_T_max / sizeof(float)) / ncs;
400    d_inner_tile -= (d_inner_tile % VLEN_FP32);
401    if (d_inner_tile == 0) {
402        FARF(HIGH, "ssm_conv-f32: inner tile rounds to 0 (ncs=%u), falling back to scalar\n", ncs);
403        use_hvx = 0;
404    } else {
405        scctx.d_inner_tile = d_inner_tile;
406
407        octx->src0_spad.size_per_thread = hex_round_up(d_inner_tile * ncs * sizeof(float), 256);
408        octx->src1_spad.size_per_thread = src1_T_size;
409        octx->dst_spad.size_per_thread  = 0;
410
411        octx->src0_spad.size = octx->src0_spad.size_per_thread * n_threads;
412        octx->src1_spad.size = octx->src1_spad.size_per_thread * n_threads;
413        octx->dst_spad.size  = 0;
414
415        octx->src0_spad.data = octx->ctx->vtcm_base;
416        octx->src1_spad.data = octx->src0_spad.data + octx->src0_spad.size;
417        octx->src0_spad.src  = NULL;
418        octx->src1_spad.src  = NULL;
419
420        const size_t total_spad = octx->src0_spad.size + octx->src1_spad.size;
421        if (total_spad > octx->ctx->vtcm_size) {
422            FARF(HIGH, "ssm_conv-f32: scratchpad %zu exceeds VTCM %zu, falling back to scalar\n",
423                 total_spad, octx->ctx->vtcm_size);
424            use_hvx = 0;
425        }
426    }
427
428    FARF(HIGH, "ssm-conv-f32: (%ux%ux%ux%u) x (%ux%ux%ux%u) -> (%ux%ux%ux%u) : use_hvx %d\n", src0->ne[0],
429         src0->ne[1], src0->ne[2], src0->ne[3], src1->ne[0], src1->ne[1], src1->ne[2], src1->ne[3], dst->ne[0],
430         dst->ne[1], dst->ne[2], dst->ne[3], use_hvx);
431
432    if (use_hvx) {
433        work_queue_run(octx->ctx->work_queue, ssm_conv_thread_f32_f32_hvx, &scctx, n_threads);
434    } else {
435        work_queue_run(octx->ctx->work_queue, ssm_conv_thread_f32_f32, &scctx, n_threads);
436    }
437
438    return HTP_STATUS_OK;
439}
440
441int op_ssm_conv(struct htp_ops_context * octx) {
442    const struct htp_tensor * dst = octx->dst;
443
444    int err = HTP_STATUS_OK;
445
446    switch (dst->type) {
447        case HTP_TYPE_F32:
448            err = op_ssm_conv_f32(octx);
449            break;
450        default:
451            err = HTP_STATUS_NO_SUPPORT;
452            break;
453    }
454
455    return err;
456}