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
11.6 KiB299 linesraw
1#pragma clang diagnostic ignored "-Wunused-but-set-variable"
2
3#include <HAP_farf.h>
4#include <string.h>
5
6#include "hex-common.h"
7#include "hex-profile.h"
8
9#define GGML_COMMON_DECL_C
10#include "ggml-common.h"
11#include "htp-ctx.h"
12#include "htp-ops.h"
13#include "htp-tensor.h"
14#include "hvx-types.h"
15#include "hvx-utils.h"
16
17struct htp_solve_tri_context {
18    struct htp_ops_context * octx;
19    uint32_t                 jobs_per_thread;
20    uint32_t                 total_jobs;
21    uint32_t                 job_start;
22    uint32_t                 k_chunks;
23    uint32_t                 col_block;
24};
25
26static inline void solve_tri_row_scalar(const float * A_row,
27                                        const float * B_row,
28                                        float *       X,
29                                        uint32_t      row,
30                                        uint32_t      k,
31                                        uint32_t      col0,
32                                        uint32_t      coln,
33                                        float         inv_diag) {
34    for (uint32_t col = col0; col < col0 + coln; ++col) {
35        float sum = 0.0f;
36        for (uint32_t t = 0; t < row; ++t) {
37            sum += A_row[t] * X[t * k + col];
38        }
39        X[row * k + col] = (B_row[col] - sum) * inv_diag;
40    }
41}
42
43static inline HVX_Vector hvx_load_partial_f32(const float * src, uint32_t n) {
44    HVX_Vector v = *((const HVX_UVector *) src);
45    HVX_VectorPred mask = Q6_Q_vsetq2_R(n * sizeof(float));
46    return Q6_V_vmux_QVV(mask, v, Q6_V_vzero());
47}
48
49static inline void solve_tri_row_hvx(const float * A_row,
50                                     const float * B_row,
51                                     float *       X,
52                                     uint32_t      row,
53                                     uint32_t      k,
54                                     uint32_t      col0,
55                                     uint32_t      coln,
56                                     float         inv_diag) {
57    const bool full = (coln == VLEN_FP32);
58
59    HVX_Vector sum_v = Q6_V_vzero();
60    for (uint32_t t = 0; t < row; ++t) {
61        const float   a         = A_row[t];
62        const float * x_row_col = X + t * k + col0;
63
64        HVX_Vector x_v = full ? *((const HVX_UVector *) x_row_col) : hvx_load_partial_f32(x_row_col, coln);
65        HVX_Vector a_v = hvx_vec_splat_f32(a);
66        sum_v          = hvx_vec_add_f32_f32(sum_v, hvx_vec_mul_f32_f32(x_v, a_v));
67    }
68
69    const float * b_row_col = B_row + col0;
70    float *       x_out_col = X + row * k + col0;
71
72    HVX_Vector b_v        = full ? *((const HVX_UVector *) b_row_col) : hvx_load_partial_f32(b_row_col, coln);
73    HVX_Vector inv_diag_v = hvx_vec_splat_f32(inv_diag);
74
75    HVX_Vector out_v = hvx_vec_mul_f32_f32(hvx_vec_sub_f32_f32(b_v, sum_v), inv_diag_v);
76    hvx_vec_store_u((void *) x_out_col, coln * sizeof(float), out_v);
77}
78
79// Batch-level thread: each job is one full batch.
80static void solve_tri_batch_thread_f32(unsigned int nth, unsigned int ith, void * data) {
81    struct htp_solve_tri_context * sctx = (struct htp_solve_tri_context *) data;
82    struct htp_ops_context *       octx = sctx->octx;
83
84    const struct htp_tensor * src0 = octx->src[0];  // A
85    const struct htp_tensor * src1 = octx->src[1];  // B
86    const struct htp_tensor * dst  = octx->dst;     // X
87
88    const uint32_t n = src0->ne[0];
89    const uint32_t k = src1->ne[0];
90
91    const uint32_t ne02 = src0->ne[2];
92
93    const uint32_t col_block = VLEN_FP32;
94    const uint32_t k_full    = (k / col_block) * col_block;
95
96    const uint32_t start_batch = sctx->job_start + sctx->jobs_per_thread * ith;
97    const uint32_t end_batch   = MIN(start_batch + sctx->jobs_per_thread, sctx->job_start + sctx->total_jobs);
98
99    struct htp_thread_trace * tr = &octx->ctx->trace[ith];
100    htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) start_batch);
101
102    for (uint32_t batch = start_batch; batch < end_batch; ++batch) {
103        const uint32_t i03 = batch / ne02;
104        const uint32_t i02 = batch - i03 * ne02;
105
106        const float * A_batch =
107            (const float *) ((const uint8_t *) (uintptr_t) src0->data + i02 * src0->nb[2] + i03 * src0->nb[3]);
108        const float * B_batch =
109            (const float *) ((const uint8_t *) (uintptr_t) src1->data + i02 * src1->nb[2] + i03 * src1->nb[3]);
110        float * X_batch = (float *) ((uint8_t *) (uintptr_t) dst->data + i02 * dst->nb[2] + i03 * dst->nb[3]);
111
112        for (uint32_t row = 0; row < n; ++row) {
113            const float   diag     = A_batch[row * n + row];
114            const float   inv_diag = 1.0f / diag;
115            const float * A_row    = A_batch + row * n;
116            const float * B_row    = B_batch + row * k;
117
118            uint32_t col0 = 0;
119            for (; col0 < k_full; col0 += col_block) {
120                solve_tri_row_hvx(A_row, B_row, X_batch, row, k, col0, col_block, inv_diag);
121            }
122
123            if (col0 < k) {
124                const uint32_t coln = k - col0;
125                if (coln >= 8) {
126                    solve_tri_row_hvx(A_row, B_row, X_batch, row, k, col0, coln, inv_diag);
127                } else {
128                    solve_tri_row_scalar(A_row, B_row, X_batch, row, k, col0, coln, inv_diag);
129                }
130            }
131        }
132    }
133
134    htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) end_batch);
135
136    FARF(HIGH, "solve-tri-batch %d/%d: A=(%ux%u) B=(%ux%u) batch %u:%u\n",
137         ith, nth, n, n, k, n, start_batch, end_batch);
138}
139
140// Chunk-level thread: each job is one (batch, col_chunk) pair.
141static void solve_tri_chunk_thread_f32(unsigned int nth, unsigned int ith, void * data) {
142    struct htp_solve_tri_context * sctx = (struct htp_solve_tri_context *) data;
143    struct htp_ops_context *       octx = sctx->octx;
144
145    const struct htp_tensor * src0 = octx->src[0];  // A
146    const struct htp_tensor * src1 = octx->src[1];  // B
147    const struct htp_tensor * dst  = octx->dst;     // X
148
149    const uint32_t n = src0->ne[0];
150    const uint32_t k = src1->ne[0];
151
152    const uint32_t ne02 = src0->ne[2];
153
154    const uint32_t start_job = sctx->job_start + sctx->jobs_per_thread * ith;
155    const uint32_t end_job   = MIN(start_job + sctx->jobs_per_thread, sctx->job_start + sctx->total_jobs);
156
157    struct htp_thread_trace * tr = &octx->ctx->trace[ith];
158    htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) start_job);
159
160    for (uint32_t job = start_job; job < end_job; ++job) {
161        const uint32_t batch = job / sctx->k_chunks;
162        const uint32_t chunk = job - batch * sctx->k_chunks;
163
164        const uint32_t i03 = batch / ne02;
165        const uint32_t i02 = batch - i03 * ne02;
166
167        const float * A_batch =
168            (const float *) ((const uint8_t *) (uintptr_t) src0->data + i02 * src0->nb[2] + i03 * src0->nb[3]);
169        const float * B_batch =
170            (const float *) ((const uint8_t *) (uintptr_t) src1->data + i02 * src1->nb[2] + i03 * src1->nb[3]);
171        float * X_batch = (float *) ((uint8_t *) (uintptr_t) dst->data + i02 * dst->nb[2] + i03 * dst->nb[3]);
172
173        const uint32_t col0 = chunk * sctx->col_block;
174        const uint32_t coln = MIN(sctx->col_block, k - col0);
175
176        for (uint32_t row = 0; row < n; ++row) {
177            const float diag     = A_batch[row * n + row];
178            const float inv_diag = 1.0f / diag;
179
180            const float * A_row = A_batch + row * n;
181            const float * B_row = B_batch + row * k;
182
183            if (coln >= 8) {
184                solve_tri_row_hvx(A_row, B_row, X_batch, row, k, col0, coln, inv_diag);
185            } else {
186                solve_tri_row_scalar(A_row, B_row, X_batch, row, k, col0, coln, inv_diag);
187            }
188        }
189    }
190
191    htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) end_job);
192
193    FARF(HIGH, "solve-tri-chunk %d/%d: A=(%ux%u) B=(%ux%u) jobs %u:%u\n",
194         ith, nth, n, n, k, n, start_job, end_job);
195}
196
197int op_solve_tri(struct htp_ops_context * octx) {
198    const struct htp_tensor * src0 = octx->src[0];  // A
199    const struct htp_tensor * src1 = octx->src[1];  // B
200    const struct htp_tensor * dst  = octx->dst;     // X
201
202    if (src0->type != HTP_TYPE_F32 || src1->type != HTP_TYPE_F32 || dst->type != HTP_TYPE_F32) {
203        return HTP_STATUS_NO_SUPPORT;
204    }
205
206    // left=true, lower=true, uni=false only
207    if (src0->ne[0] != src0->ne[1]) {
208        return HTP_STATUS_INVAL_PARAMS;
209    }
210    if (src0->ne[1] != src1->ne[1]) {
211        return HTP_STATUS_INVAL_PARAMS;
212    }
213    if (src0->ne[2] != src1->ne[2] || src0->ne[3] != src1->ne[3]) {
214        return HTP_STATUS_INVAL_PARAMS;
215    }
216    if (dst->ne[0] != src1->ne[0] || dst->ne[1] != src1->ne[1] || dst->ne[2] != src1->ne[2] ||
217        dst->ne[3] != src1->ne[3]) {
218        return HTP_STATUS_INVAL_PARAMS;
219    }
220
221    if (octx->flags & HTP_OPFLAGS_SKIP_COMPUTE) {
222        return HTP_STATUS_OK;
223    }
224
225    const uint32_t k = src1->ne[0];
226
227    const uint32_t col_block     = VLEN_FP32;
228    const uint32_t k_chunks      = (k + col_block - 1) / col_block;
229    const uint32_t total_batches = src0->ne[2] * src0->ne[3];
230    const bool     batched       = total_batches >= (uint32_t) octx->n_threads;
231
232    FARF(HIGH, "solve-tri: (%ux%ux%ux%u) x (%ux%ux%ux%u) -> (%ux%ux%ux%u) : batched %d\n",
233         src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3],
234         src1->ne[0], src1->ne[1], src1->ne[2], src1->ne[3],
235         dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3], batched);
236
237    if (batched) {
238        uint32_t job_start = 0;
239        uint32_t njobs     = total_batches;
240
241        if (octx->ctx->mdev.count > 1) {
242            const uint32_t batch_size = dst->nb[2];
243            const uint32_t batches_per_chunk = (batch_size > 0) ? (HEX_L2_LINE_SIZE / hex_gcd_u32(batch_size, HEX_L2_LINE_SIZE)) : 1;
244            const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(total_batches, htp_tensor_mdev_data_aligned(dst) ? batches_per_chunk : 0, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
245            job_start = range.start;
246            njobs     = range.count;
247        }
248
249        if (njobs == 0) {
250            return HTP_STATUS_OK;
251        }
252
253        // Batch-level parallelism
254        const uint32_t n_threads = octx->n_threads;
255
256        struct htp_solve_tri_context sctx = {
257            .octx            = octx,
258            .jobs_per_thread = fastdiv(njobs + n_threads - 1, &octx->n_threads_div),
259            .total_jobs      = njobs,
260            .job_start       = job_start,
261            .k_chunks        = k_chunks,
262            .col_block       = col_block,
263        };
264
265        work_queue_run(octx->ctx->work_queue, solve_tri_batch_thread_f32, &sctx, n_threads);
266    } else {
267        // Chunk-level parallelism
268        const uint32_t total_jobs = total_batches * k_chunks;
269
270        uint32_t job_start = 0;
271        uint32_t njobs     = total_jobs;
272
273        if (octx->ctx->mdev.count > 1) {
274            const bool can_split = htp_tensor_mdev_data_aligned(dst) && ((dst->nb[1] & (HTP_TENSOR_MDEV_LINE_SIZE - 1)) == 0);
275            const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(total_jobs, can_split ? 1 : 0, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
276            job_start = range.start;
277            njobs     = range.count;
278        }
279
280        if (njobs == 0) {
281            return HTP_STATUS_OK;
282        }
283
284        const uint32_t n_threads = octx->n_threads;
285
286        struct htp_solve_tri_context sctx = {
287            .octx            = octx,
288            .jobs_per_thread = fastdiv(njobs + n_threads - 1, &octx->n_threads_div),
289            .total_jobs      = njobs,
290            .job_start       = job_start,
291            .k_chunks        = k_chunks,
292            .col_block       = col_block,
293        };
294
295        work_queue_run(octx->ctx->work_queue, solve_tri_chunk_thread_f32, &sctx, n_threads);
296    }
297
298    return HTP_STATUS_OK;
299}