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
16.1 KiB430 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_perf.h>
7
8#include <math.h>
9#include <string.h>
10
11#include "hex-dma.h"
12#include "hvx-utils.h"
13#include "hex-fastdiv.h"
14
15#define GGML_COMMON_DECL_C
16#include "ggml-common.h"
17#include "hex-common.h"
18#include "hex-profile.h"
19#include "htp-ctx.h"
20#include "htp-ops.h"
21#include "htp-tensor.h"
22
23#define htp_softmax_preamble3                     \
24    const uint32_t ne00 = src0->ne[0];            \
25    const uint32_t ne01 = src0->ne[1];            \
26    const uint32_t ne02 = src0->ne[2];            \
27    const uint32_t ne03 = src0->ne[3];            \
28                                                  \
29    const uint32_t nb00 = src0->nb[0];            \
30    const uint32_t nb01 = src0->nb[1];            \
31    const uint32_t nb02 = src0->nb[2];            \
32    const uint32_t nb03 = src0->nb[3];            \
33                                                  \
34    const uint32_t ne10 = src1 ? src1->ne[0] : 1; \
35    const uint32_t ne11 = src1 ? src1->ne[1] : 1; \
36    const uint32_t ne12 = src1 ? src1->ne[2] : 1; \
37    const uint32_t ne13 = src1 ? src1->ne[3] : 1; \
38                                                  \
39    const uint32_t nb10 = src1 ? src1->nb[0] : 1; \
40    const uint32_t nb11 = src1 ? src1->nb[1] : 1; \
41    const uint32_t nb12 = src1 ? src1->nb[2] : 1; \
42    const uint32_t nb13 = src1 ? src1->nb[3] : 1; \
43                                                  \
44    const uint32_t ne0 = dst->ne[0];              \
45    const uint32_t ne1 = dst->ne[1];              \
46    const uint32_t ne2 = dst->ne[2];              \
47    const uint32_t ne3 = dst->ne[3];              \
48                                                  \
49    const uint32_t nb0 = dst->nb[0];              \
50    const uint32_t nb1 = dst->nb[1];              \
51    const uint32_t nb2 = dst->nb[2];              \
52    const uint32_t nb3 = dst->nb[3];
53
54struct htp_softmax_context {
55    struct htp_ops_context * octx;
56
57    bool     use_f16;
58    bool     use_src1;
59
60    uint32_t n_head;
61    uint32_t n_head_log2;
62
63    float    scale;
64    float    max_bias;
65    float    m0;
66    float    m1;
67
68    struct fastdiv_values fastdiv_ne01;
69    struct fastdiv_values fastdiv_ne02;
70    struct fastdiv_values fastdiv_ne12; // For mask broadcasting
71    struct fastdiv_values fastdiv_ne13; // For mask broadcasting
72
73    uint32_t src0_nrows_per_thread;
74    uint32_t row_start;
75    uint32_t nrows;
76};
77
78static void apply_mask(float * restrict wp0,
79                       const float * restrict mp_f32,
80                       const __fp16 * restrict mp_f16,
81                       uint32_t ne00,
82                       float slope,
83                       bool use_f16) {
84    if (!mp_f32) {
85        return;
86    }
87    if (use_f16) {
88        for (uint32_t i = 0; i < ne00; ++i) {
89            wp0[i] += slope * (float) mp_f16[i];
90        }
91    } else {
92        for (uint32_t i = 0; i < ne00; ++i) {
93            wp0[i] += slope * mp_f32[i];
94        }
95    }
96}
97
98static void init_softmax_ctx(struct htp_softmax_context * smctx, struct htp_ops_context * octx) {
99    const struct htp_tensor * src0 = octx->src[0];
100    const struct htp_tensor * src1 = octx->src[1];
101
102    memset(smctx, 0, sizeof(struct htp_softmax_context));
103
104    memcpy(&smctx->scale,    (float *) octx->op_params,     sizeof(float));
105    memcpy(&smctx->max_bias, (float *) octx->op_params + 1, sizeof(float));
106
107    smctx->n_head      = src0->ne[2];
108    smctx->n_head_log2 = 1u << (uint32_t) floor(log2(smctx->n_head));
109
110    smctx->m0 = powf(2.0f, -(smctx->max_bias) / smctx->n_head_log2);
111    smctx->m1 = powf(2.0f, -(smctx->max_bias / 2.0f) / smctx->n_head_log2);
112
113    smctx->use_src1 = (src1 != 0);
114    smctx->use_f16  = (src1 != 0) && (src1->type == HTP_TYPE_F16);
115
116    smctx->octx = octx;
117
118    // Initialize fastdiv values
119    const uint32_t ne01 = src0->ne[1];
120    const uint32_t ne02 = src0->ne[2];
121
122    if (ne01 > 0) smctx->fastdiv_ne01 = init_fastdiv_values(ne01);
123    if (ne02 > 0) smctx->fastdiv_ne02 = init_fastdiv_values(ne02);
124
125    const uint32_t ne12 = src1 ? src1->ne[2] : 1;
126    const uint32_t ne13 = src1 ? src1->ne[3] : 1;
127
128    if (ne12 > 0) smctx->fastdiv_ne12 = init_fastdiv_values(ne12);
129    if (ne13 > 0) smctx->fastdiv_ne13 = init_fastdiv_values(ne13);
130}
131
132static void hvx_fast_softmax_prep_f32(const uint8_t * restrict src,
133                                      uint8_t * restrict dst,
134                                      const int num_elems,
135                                      float     scale,
136                                      const uint8_t * restrict mask,
137                                      float slope) {
138    const uint8_t * restrict src_curr  = src;
139    uint8_t * restrict dst_curr        = dst;
140    const uint8_t * restrict mask_curr = mask;
141
142    HVX_Vector scale_vec = hvx_vec_splat_f32(scale);
143    HVX_Vector slope_vec = hvx_vec_splat_f32(slope);
144
145    int step_of_1 = num_elems >> 5;
146
147    #pragma unroll(4)
148    for (int i = 0; i < step_of_1; i++) {
149        HVX_Vector v1 = *(HVX_Vector *) src_curr;
150
151        HVX_Vector v3 = *(HVX_Vector *) mask_curr;
152
153        HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, scale_vec);
154
155        HVX_Vector v4 = Q6_Vqf32_vmpy_VsfVsf(v3, slope_vec);
156
157        HVX_Vector v5 = Q6_Vqf32_vadd_Vqf32Vqf32(v2, v4);
158
159        *(HVX_Vector *) dst_curr = Q6_Vsf_equals_Vqf32(v5);
160
161        src_curr += VLEN;
162        dst_curr += VLEN;
163        mask_curr += VLEN;
164    }
165}
166
167static void hvx_fast_softmax_f32(const uint8_t * restrict src, uint8_t * restrict dst, uint8_t * restrict pad, const int num_elems) {
168    const HVX_Vector * restrict v_src = (HVX_Vector *) src;
169    HVX_Vector * restrict v_pad       = (HVX_Vector *) pad;
170    HVX_Vector * restrict v_dst       = (HVX_Vector *) dst;
171
172    HVX_Vector sum_vec = Q6_V_vsplat_R(0x00000000);
173    HVX_Vector max_vec = hvx_vec_splat_f32(((const float *) src)[0]);
174    HVX_Vector zero_v  = Q6_V_vzero();
175    HVX_Vector one_v   = hvx_vec_splat_f32(1.0);
176
177    int step_of_1 = num_elems >> 5;
178
179    #pragma unroll(4)
180    for (int i = 0; i < step_of_1; i++) {
181        HVX_Vector v1 = v_src[i];
182        max_vec       = Q6_Vsf_vmax_VsfVsf(max_vec, v1);
183    }
184
185    max_vec = hvx_vec_reduce_max_f32(max_vec); // replicated over all lanes
186
187    #pragma unroll(4)
188    for (int i = 0; i < step_of_1; i++) {
189        HVX_Vector v1 = v_src[i];
190        HVX_Vector v2 = Q6_Vqf32_vsub_VsfVsf(v1, max_vec);
191
192        HVX_Vector v3 = hvx_vec_exp_f32(Q6_Vsf_equals_Vqf32(v2));
193
194        sum_vec = Q6_Vqf32_vadd_VsfVsf(Q6_Vsf_equals_Vqf32(sum_vec), v3);
195
196        v_pad[i] = v3;
197    }
198
199    sum_vec = hvx_vec_reduce_sum_f32(Q6_Vsf_equals_Vqf32(sum_vec)); // replicated over all lanes
200
201    HVX_VectorPred pos_sum   = Q6_Q_vcmp_gt_VwVw(sum_vec, zero_v);
202    HVX_Vector     v4        = hvx_vec_inverse_f32(sum_vec);
203    HVX_Vector     scale_vec = Q6_V_vmux_QVV(pos_sum, v4, one_v);
204
205    #pragma unroll(4)
206    for (int i = 0; i < step_of_1; i++) {
207        HVX_Vector v1 = v_pad[i];
208        HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, scale_vec);
209        v_dst[i]      = Q6_Vsf_equals_Vqf32(v2);
210    }
211}
212
213static float hvx_softmax_f32(const uint8_t * restrict src, uint8_t * restrict dst, uint8_t * restrict spad, const int  num_elems, const float max) {
214    hvx_sub_scalar_f32(spad, src, max, num_elems);
215
216    hvx_exp_f32(dst, spad, num_elems, false);
217    return hvx_reduce_sum_f32(dst, num_elems);
218}
219
220static void softmax_job_f32(unsigned int nth, unsigned int ith, void * data) {
221    struct htp_softmax_context * smctx = (struct htp_softmax_context *) data;
222    struct htp_ops_context * octx = smctx->octx;
223
224    const struct htp_tensor * src0 = octx->src[0];
225    const struct htp_tensor * src1 = octx->src[1];
226    const struct htp_tensor * dst  = octx->dst;
227
228    htp_softmax_preamble3;
229
230    const uint32_t src0_nrows            = smctx->nrows;
231    const uint32_t src0_nrows_per_thread = smctx->src0_nrows_per_thread;
232
233    const uint32_t src0_start_row = smctx->row_start + src0_nrows_per_thread * ith;
234    const uint32_t src0_end_row   = MIN(src0_start_row + src0_nrows_per_thread, smctx->row_start + src0_nrows);
235
236    // no work for this thread
237    if (src0_start_row >= src0_end_row) {
238        return;
239    }
240
241    int is_aligned = 1;
242    int opt_path   = 0;
243
244    if (!hex_is_aligned((void *) src0->data, VLEN) || !hex_is_aligned((void *) dst->data, VLEN)) {
245        is_aligned = 0;
246        FARF(HIGH, "softmax-f32: unaligned addresses in elementwise op, possibly slower execution\n");
247    }
248
249    // Only use the fast path when aligned AND row size is multiple of VLEN (128 bytes)
250    // The fast path (hvx_fast_softmax_f32) doesn't handle tail elements
251    // The non-opt path uses hvx_softmax_f32 which properly handles all sizes via its helper functions
252    if ((1 == is_aligned) && !(nb01 & (VLEN - 1))) {
253        opt_path = 1;
254    }
255
256    uint8_t * src0_spad_data = octx->src0_spad.data + (ith * octx->src0_spad.size_per_thread);
257    uint8_t * src1_spad_data = octx->src1_spad.data + (ith * octx->src1_spad.size_per_thread);
258    uint8_t * dst_spad_data  = octx->dst_spad.data  + (ith * octx->dst_spad.size_per_thread);
259
260    float * wp0 = (float *) src0_spad_data;
261    float * wp1 = (float *) src1_spad_data;
262    float * wp2 = (float *) dst_spad_data;
263
264    uint32_t prev_i2 = (uint32_t)-1;
265    float slope = 1.0f;
266
267    struct htp_thread_trace * tr = &octx->ctx->trace[ith];
268    htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, src0_start_row);
269
270    for (uint32_t r = src0_start_row; r < src0_end_row; ++r) {
271        uint32_t i1 = fastmodulo(r, ne01, &smctx->fastdiv_ne01);
272        uint32_t r_div_ne01 = fastdiv(r, &smctx->fastdiv_ne01);
273        uint32_t i2 = fastmodulo(r_div_ne01, ne02, &smctx->fastdiv_ne02);
274        uint32_t i3 = fastdiv(r_div_ne01, &smctx->fastdiv_ne02);
275
276        // Map to original logic indices
277        // i01 = i1
278        // i02 = i2
279        // i03 = i3
280
281        const uint32_t i11 = i1;
282        // const uint32_t i12 = i2 % ne12;
283        // const uint32_t i13 = i3 % ne13;
284
285        uint32_t i12, i13;
286        if (ne12 == ne02) {
287             i12 = i2;
288        } else {
289             i12 = fastmodulo(i2, ne12, &smctx->fastdiv_ne12);
290        }
291
292        if (ne13 == ne03) {
293             i13 = i3;
294        } else {
295             i13 = fastmodulo(i3, ne13, &smctx->fastdiv_ne13);
296        }
297
298        // ALiBi
299        if (i2 != prev_i2) {
300            const uint32_t h = i2;  // head
301            slope = (smctx->max_bias > 0.0f) ? h < smctx->n_head_log2 ? powf(smctx->m0, h + 1) : powf(smctx->m1, 2 * (h - smctx->n_head_log2) + 1) : 1.0f;
302            prev_i2 = i2;
303        }
304
305        float * sp = (float *) ((char *) src0->data + i1 * nb01 + i2 * nb02 + i3 * nb03);
306        float * dp = (float *) ((char *) dst->data  + i1 * nb1  + i2 * nb2  + i3 * nb3);
307
308        // broadcast the mask across rows
309        __fp16 * mp_f16 = (smctx->use_src1) ? (__fp16 *) ((char *) src1->data + i11 * nb11 + i12 * nb12 + i13 * nb13) : NULL;
310        float *  mp_f32 = (smctx->use_src1) ? (float *)  ((char *) src1->data + i11 * nb11 + i12 * nb12 + i13 * nb13) : NULL;
311
312        if ((1 == opt_path) && (mp_f32) && !(smctx->use_f16)) {
313            hvx_fast_softmax_prep_f32((const uint8_t *) sp, (uint8_t *) wp0, ne00, smctx->scale, (const uint8_t *) mp_f32, slope);
314            hvx_fast_softmax_f32((const uint8_t *) wp0, (uint8_t *) dp, (uint8_t *) wp1, ne00);
315        } else if (1 == opt_path) {
316            hvx_scale_f32((uint8_t *) wp0, (const uint8_t *) sp, ne00, smctx->scale);
317            apply_mask(wp0, mp_f32, mp_f16, ne00, slope, smctx->use_f16);
318            hvx_fast_softmax_f32((const uint8_t *) wp0, (uint8_t *) dp, (uint8_t *) wp1, ne00);
319        } else {
320            // Non-optimized path: uses HVX helper functions that properly handle all tensor sizes
321            // including non-multiples of 32 (the HVX vector lane count for f32)
322            hvx_scale_f32((uint8_t *) wp0, (const uint8_t *) sp, ne00, smctx->scale);
323            apply_mask(wp0, mp_f32, mp_f16, ne00, slope, smctx->use_f16);
324            float max = hvx_reduce_max_f32((const uint8_t *) wp0, ne00);
325            float sum = hvx_softmax_f32((const uint8_t *) wp0, (uint8_t *) wp2, (uint8_t *) wp1, ne00, max);
326            sum       = sum > 0.0 ? (1.0 / sum) : 1;
327            hvx_scale_f32((uint8_t *) dp, (const uint8_t *) wp2, ne00, sum);
328        }
329    }
330
331    htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, src0_start_row);
332
333    FARF(HIGH, "softmax-f32 %d/%d: %ux%ux%ux%u (%u:%u) x %ux%ux%ux%u -> %ux%ux%ux%u : opt %u f16 %u\n", ith, nth,
334         ne00, ne01, ne02, ne03, src0_start_row, src0_end_row, ne10, ne11, ne12, ne13,
335         ne0, ne1, ne2, ne3, opt_path, smctx->use_f16);
336}
337
338static int execute_op_softmax_f32(struct htp_ops_context * octx) {
339    int err = HTP_STATUS_OK;
340
341    const struct htp_tensor * src0 = octx->src[0];
342    const struct htp_tensor * src1 = octx->src[1];
343    const struct htp_tensor * dst  = octx->dst;
344
345    struct htp_softmax_context smctx;
346    const char * op_type = "softmax-f32";
347
348    init_softmax_ctx(&smctx, octx);
349
350    const uint32_t src0_nrows = src0->ne[1] * src0->ne[2] * src0->ne[3];
351    const size_t elem_size = sizeof(float);
352    const size_t dst_row_size = dst->nb[1];
353
354    uint32_t row_start = 0;
355    uint32_t nrows     = src0_nrows;
356
357    if (octx->ctx->mdev.count > 1) {
358        uint32_t rows_per_chunk = 0;
359        htp_tensor_mdev_rows_per_chunk(dst, (uint32_t) elem_size, (uint32_t) dst_row_size, &rows_per_chunk);
360        const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(src0_nrows, rows_per_chunk, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
361        row_start = range.start;
362        nrows     = range.count;
363    }
364
365    if (nrows == 0) {
366        return HTP_STATUS_OK;
367    }
368
369    const uint32_t n_threads = octx->n_threads;
370
371    smctx.src0_nrows_per_thread = fastdiv(nrows + n_threads - 1, &octx->n_threads_div);
372    smctx.row_start             = row_start;
373    smctx.nrows                 = nrows;
374
375    const size_t src0_row_size = src0->nb[1];
376    const size_t src1_row_size = src0_row_size;
377
378    // VTCM scratchpads for all tensors
379    // 4 rows per thread, padded to HVX vector size
380    octx->src0_spad.size_per_thread = hex_round_up(4 * src0_row_size, 128);
381    octx->src1_spad.size_per_thread = hex_round_up(4 * src1_row_size, 128);
382    octx->dst_spad.size_per_thread  = hex_round_up(4 * dst_row_size, 128);
383
384    octx->src0_spad.size = octx->src0_spad.size_per_thread * n_threads;
385    octx->src1_spad.size = octx->src1_spad.size_per_thread * n_threads;
386    octx->dst_spad.size  = octx->dst_spad.size_per_thread  * n_threads;
387
388    size_t spad_size = octx->src0_spad.size + octx->src1_spad.size + octx->dst_spad.size;
389
390    if (src1) {
391        FARF(HIGH, "%s: %ux%ux%ux%u x %ux%ux%ux%u -> %ux%ux%ux%u : src0-spad-size %u src1-spad-size %u dst-spad-size %u\n",
392             op_type, src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], src1->ne[0], src1->ne[1], src1->ne[2],
393             src1->ne[3], dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3], octx->src0_spad.size, octx->src1_spad.size,
394             octx->dst_spad.size);
395    } else {
396        FARF(HIGH, "%s: %ux%ux%ux%u -> %ux%ux%ux%u : src0-spad-size %u src1-spad-size %u dst-spad-size %u\n", op_type,
397             src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3],
398             octx->src0_spad.size, octx->src1_spad.size, octx->dst_spad.size);
399    }
400
401    // Make sure the reserved vtcm size is sufficient
402    if (octx->ctx->vtcm_size < spad_size) {
403        FARF(ERROR, "%s : current VTCM reservation %zu is too small, needed %zu\n", op_type, octx->ctx->vtcm_size, spad_size);
404        return HTP_STATUS_VTCM_TOO_SMALL;
405    }
406
407    octx->src0_spad.data = octx->ctx->vtcm_base;                        octx->src0_spad.src = NULL;
408    octx->src1_spad.data = octx->src0_spad.data + octx->src0_spad.size; octx->src1_spad.src = NULL;
409    octx->dst_spad.data  = octx->src1_spad.data + octx->src1_spad.size; octx->dst_spad.src  = NULL;
410
411    work_queue_run(octx->ctx->work_queue, softmax_job_f32, &smctx, n_threads);
412
413    return err;
414}
415
416int op_softmax(struct htp_ops_context * octx) {
417    int err = HTP_STATUS_OK;
418
419    switch (octx->src[0]->type) {
420        case HTP_TYPE_F32:
421            err = execute_op_softmax_f32(octx);
422            break;
423
424        default:
425            err = HTP_STATUS_NO_SUPPORT;
426            break;
427    }
428
429    return err;
430}