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
14.5 KiB345 linesraw
1#ifndef HTP_FLASH_ATTN_OPS_H
2#define HTP_FLASH_ATTN_OPS_H
3
4#include <stdint.h>
5#include <stddef.h>
6#include <stdbool.h>
7
8#include "hex-fastdiv.h"
9#include "hex-common.h"
10#include "htp-vtcm.h"
11
12#ifdef __cplusplus
13extern "C" {
14#endif
15
16// Tile constants (mirrored from hmx-utils.h for use on host side if needed)
17#define HTP_FA_HMX_TILE_SIZE   2048
18#define HMX_FP16_TILE_SIZE     2048
19#define HMX_FP16_TILE_N_ROWS   32
20#define HMX_FP16_TILE_N_COLS   32
21#define HMX_FP16_TILE_N_ELMS   1024
22
23#define HVX_FA_DMA_CACHE_SIZE  128
24#define HMX_FA_DMA_CACHE_SIZE  4
25
26
27#define HTP_FA_M_INITIAL_VAL  -10000.0f
28
29enum htp_fa_kernel_type {
30    HTP_FA_KERNEL_UNSUPPORTED = 0,
31    HTP_FA_KERNEL_HVX,
32    HTP_FA_KERNEL_HMX
33};
34
35struct htp_fa_kernel_params {
36    uint8_t  kernel_type;        // enum htp_fa_kernel_type
37    uint8_t  is_q_fp32;          // 1 = Q type is F32, 0 = F16
38    uint8_t  is_dst_fp32;        // 1 = dst type is F32, 0 = F16
39    uint8_t  n_threads;          // Number of threads to run
40
41    // Common parameters
42    uint16_t Br;
43    uint16_t Bc;
44    uint16_t n_kv_blocks;        // also HVX's n_blocks
45    uint16_t G;                  // GQA factor (n_heads / n_kv_heads)
46
47    float    scale;
48    float    max_bias;
49    float    logit_softcap;
50    uint32_t vtcm_size;
51
52    uint32_t qrows;
53    uint32_t qrows_per_thread;
54    uint32_t qrow_start;
55    float    m0;
56    float    m1;
57    uint32_t n_head_log2;
58
59    struct fastdiv_values src3_div2;
60    struct fastdiv_values src3_div3;
61
62    struct fastdiv_values broadcast_rk2;
63    struct fastdiv_values broadcast_rk3;
64    struct fastdiv_values broadcast_rv2;
65    struct fastdiv_values broadcast_rv3;
66
67    union {
68        struct {
69            uint32_t g_br;
70            uint32_t row_buf_stride;
71            uint32_t mask_buf_row_stride;
72            int32_t  mask_broadcast;
73            int32_t  pipeline;
74            struct fastdiv_values div_G;
75        } hmx;
76        struct {
77            uint32_t size_q_row_padded;
78            uint32_t size_k_row_padded;
79            uint32_t size_v_row_padded;
80            struct fastdiv_values src0_div21;
81            struct fastdiv_values src0_div1;
82        } hvx;
83    } u;
84};
85
86#if defined(__cplusplus)
87static_assert(sizeof(struct htp_fa_kernel_params) <= 128, "htp_fa_kernel_params is too large for kernel_params blob");
88#endif
89
90// VTCM region layout for the HMX flash-attention kernel.
91//
92// Single source of truth for both the host (which needs the total size to pick a
93// (Br, Bc) tiling that fits the VTCM budget) and the device (which needs the actual
94// byte offsets to place each scratch buffer). Building the layout once and reading
95// offsets/total from it makes host estimate and device allocation impossible to
96// desync -- previously they were duplicated formulas in two files and drifted.
97//
98// All fields are byte offsets / byte sizes -- no HVX_Vector type is named here so the
99// header stays host-includable. The device casts (base + off_*) to the proper type.
100// An offset of 0 marks a region that is not allocated for this configuration (only
101// off_v_tiles[1], which exists only when pipelining); the device sets such pointers NULL.
102struct hmx_fa_vtcm_layout {
103    // Byte offsets from vtcm_base for each region.
104    size_t off_q_tiles;
105    size_t off_q_dma;
106    size_t off_o_tiles[2];
107    size_t off_k_fp16[2];
108    size_t off_v_fp16[2];
109    size_t off_k_tiles[2];
110    size_t off_v_tiles[2];
111    size_t off_s_tiles[2];
112    size_t off_p_tiles[2];
113    size_t off_d_tiles[2];
114    size_t off_d_inv_l;
115    size_t off_m_vec;
116    size_t off_l_vec;
117    size_t off_s_rowmax;
118    size_t off_p_rowsum;
119    size_t off_row_bufs;
120    size_t off_hmx_scales_id;
121    size_t off_hmx_scales_qk;
122    size_t off_mask_buf;
123    size_t off_slopes;
124
125    // Region byte sizes reused by the device at runtime (not just for allocation).
126    size_t q_tile_bytes;
127    size_t o_tile_bytes;
128    size_t s_tile_bytes;       // S and P tiles (same size)
129    size_t d_tile_bytes;       // d_tiles[0..1] + d_inv_l, allocated back to back
130    size_t m_line_bytes;       // one mask row
131    size_t m_buf_slot_bytes;   // one dma_cache slot = align_up(Br * m_line_bytes, 4096)
132    size_t col_vec_bytes;
133
134    // Derived strides.
135    size_t row_buf_stride;       // HVX vectors (128B) per row buffer
136    size_t mask_buf_row_stride;  // __fp16 elements per row in the mask buffer
137
138    bool   pipeline;
139    size_t total_bytes;
140};
141
142// Build the VTCM layout.
143
144static inline void hmx_fa_vtcm_layout_build(struct hmx_fa_vtcm_layout * L,
145                                       size_t gqa_factor, size_t DK, size_t DV,
146                                       size_t Br, size_t Bc, size_t n_threads, bool pipeline, bool is_q_fp32) {
147    const size_t g_br         = hex_align_up(gqa_factor * Br, HMX_FP16_TILE_N_ROWS);
148    const size_t q_tile_size  = hex_align_up(g_br * DK   * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
149    const size_t o_tile_size  = hex_align_up(g_br * DV   * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
150    const size_t k_tile_size  = hex_align_up(Bc   * DK   * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
151    const size_t v_tile_size  = hex_align_up(Bc   * DV   * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
152    const size_t s_tile_size  = hex_align_up(g_br * Bc   * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
153
154    // The rescale matrices are diagonal: the HMX kernels only ever load the g_br/32
155    // tiles that sit on the diagonal, so store just those, packed back to back with
156    // a stride of one tile.  The old [g_br, g_br] square layout allocated g_br/32
157    // times more than it used, which is also why a second D buffer was unaffordable.
158    const size_t d_tile_size = (g_br / HMX_FP16_TILE_N_ROWS) * HTP_FA_HMX_TILE_SIZE;
159
160    const size_t q_dma_size   = hex_align_up(g_br * DK * (is_q_fp32 ? sizeof(float) : sizeof(__fp16)), 128);
161    const size_t k_dma_size   = hex_align_up(Bc * hex_round_up(DK * sizeof(__fp16), 128), 128);
162    const size_t v_dma_size   = hex_align_up(Bc * hex_round_up(DV * sizeof(__fp16), 128), 128);
163    const size_t col_vec_size = hex_align_up(g_br * sizeof(float),  256);
164    const size_t row_vec_size = hex_align_up(Bc   * sizeof(__fp16), 256);
165    const size_t m_line_size  = hex_align_up(Bc   * sizeof(__fp16), 128);
166    const size_t m_buf_slot   = hex_align_up(Br * m_line_size, 256);
167    const size_t m_buf_size   = m_buf_slot * HMX_FA_DMA_CACHE_SIZE;
168    const size_t slopes_size  = hex_align_up(g_br * sizeof(__fp16), 128);
169
170    size_t off = 0;
171
172    // Group A (Part 1 - HMX Tiled buffers)
173    VTCM_LAYOUT_ALLOC(off, off_q_tiles,       q_tile_size);
174    VTCM_LAYOUT_ALLOC(off, off_o_tiles[0],    o_tile_size);
175    VTCM_LAYOUT_ALLOC(off, off_o_tiles[1],    o_tile_size);
176    VTCM_LAYOUT_ALLOC(off, off_d_tiles[0],    d_tile_size);
177    VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_d_tiles[1], d_tile_size, pipeline);
178    VTCM_LAYOUT_ALLOC(off, off_d_inv_l,       d_tile_size);
179
180    // Group B & C share start offset (Group B tiles must be 2KB aligned)
181    size_t off_group_b_c = hex_align_up(off, HTP_FA_HMX_TILE_SIZE);
182
183    // Group B: Compute-only buffers
184    size_t off_group_b = off_group_b_c;
185    VTCM_LAYOUT_ALLOC(off_group_b, off_k_tiles[0],    k_tile_size);
186    VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_k_tiles[1], k_tile_size, pipeline);
187    VTCM_LAYOUT_ALLOC(off_group_b, off_v_tiles[0],    v_tile_size);
188    VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_v_tiles[1], v_tile_size, pipeline);
189    VTCM_LAYOUT_ALLOC(off_group_b, off_s_tiles[0],    s_tile_size);
190    VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_s_tiles[1], s_tile_size, pipeline);
191    VTCM_LAYOUT_ALLOC(off_group_b, off_p_tiles[0],    s_tile_size);
192    VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_p_tiles[1], s_tile_size, pipeline);
193    VTCM_LAYOUT_ALLOC(off_group_b, off_s_rowmax,      col_vec_size);
194    VTCM_LAYOUT_ALLOC(off_group_b, off_p_rowsum,      col_vec_size);
195    VTCM_LAYOUT_ALLOC(off_group_b, off_row_bufs,      row_vec_size * 2 * n_threads);
196
197    const size_t group_b_size = off_group_b - off_group_b_c;
198
199    // Group C: Q fetch DMA buffer
200    size_t off_group_c = off_group_b_c;
201    VTCM_LAYOUT_ALLOC(off_group_c, off_q_dma,         q_dma_size);
202
203    const size_t group_c_size = off_group_c - off_group_b_c;
204
205    off = off_group_b_c + hex_smax(group_b_size, group_c_size);
206
207    // Group A (Part 2 - remaining non-HMX buffers)
208    VTCM_LAYOUT_ALLOC(off, off_k_fp16[0],     k_dma_size);
209    VTCM_LAYOUT_ALLOC(off, off_k_fp16[1],     k_dma_size);
210    VTCM_LAYOUT_ALLOC(off, off_v_fp16[0],     v_dma_size);
211    VTCM_LAYOUT_ALLOC(off, off_v_fp16[1],     v_dma_size);
212    VTCM_LAYOUT_ALLOC(off, off_m_vec,         col_vec_size);
213    VTCM_LAYOUT_ALLOC(off, off_l_vec,         col_vec_size);
214    VTCM_LAYOUT_ALLOC(off, off_hmx_scales_id, 256);
215    VTCM_LAYOUT_ALLOC(off, off_hmx_scales_qk, 256);
216    VTCM_LAYOUT_ALLOC(off, off_mask_buf,      m_buf_size);
217    VTCM_LAYOUT_ALLOC(off, off_slopes,        slopes_size);
218
219    L->q_tile_bytes        = q_tile_size;
220    L->o_tile_bytes        = o_tile_size;
221    L->col_vec_bytes       = col_vec_size;
222    L->s_tile_bytes        = s_tile_size;
223    // Measured from the actual offsets rather than assumed to be N * d_tile_size, so
224    // that inserting a region between them (or adding padding to VTCM_LAYOUT_ALLOC)
225    // cannot silently leave the tail of the run unzeroed.
226    L->d_tile_bytes        = (L->off_d_inv_l + d_tile_size) - L->off_d_tiles[0];
227    L->m_line_bytes        = m_line_size;
228    L->m_buf_slot_bytes    = m_buf_slot;
229    L->row_buf_stride      = row_vec_size / 128;
230    L->mask_buf_row_stride = m_line_size / sizeof(__fp16);
231    L->pipeline            = pipeline;
232    L->total_bytes         = off;
233}
234
235// Exact VTCM usage for a given (gqa_factor, DK, DV, Br, Bc) configuration.
236static inline size_t hmx_fa_compute_vtcm_usage(size_t gqa_factor, size_t DK, size_t DV, size_t Br, size_t Bc, size_t n_threads, bool pipeline, bool is_q_fp32) {
237    struct hmx_fa_vtcm_layout L;
238    hmx_fa_vtcm_layout_build(&L, gqa_factor, DK, DV, Br, Bc, n_threads, pipeline, is_q_fp32);
239    return L.total_bytes;
240}
241
242#define FA_HVX_BLOCK_SIZE 64
243
244static inline size_t hvx_fa_compute_vtcm_usage(size_t DK, size_t DV, bool is_q_fp32, bool has_mask, size_t n_threads) {
245    const size_t size_q_row_padded = hex_round_up(DK * (is_q_fp32 ? 4 : 2), 128);
246    const size_t size_k_row_padded = hex_round_up(DK * sizeof(__fp16), 128);
247    const size_t size_v_row_padded = hex_round_up(DV * sizeof(__fp16), 128);
248
249    const size_t size_q_block = size_q_row_padded * 1;
250    const size_t size_k_block = size_k_row_padded * FA_HVX_BLOCK_SIZE;
251    const size_t size_v_block = size_v_row_padded * FA_HVX_BLOCK_SIZE;
252    const size_t size_m_block = hex_round_up(FA_HVX_BLOCK_SIZE * sizeof(__fp16), 128);
253    const size_t size_vkq_acc = hex_round_up(DV * sizeof(float), 128);
254
255    const size_t size_per_thread = size_q_block * 1
256                                 + size_k_block * 2
257                                 + size_v_block * 2
258                                 + (has_mask ? size_m_block * HVX_FA_DMA_CACHE_SIZE : 0)
259                                 + size_vkq_acc;
260
261    return size_per_thread * n_threads;
262}
263
264#define FA_MIN_KV_BLOCKS 3
265
266// Cost-based (Br, Bc) search for flash attention with pipeline constraint.
267static inline int hmx_fa_find_chunk_size(size_t * Br_out,
268                                  size_t * Bc_out,
269                                  size_t   gqa_factor,
270                                  size_t   DK,
271                                  size_t   DV,
272                                  size_t   qo_len,
273                                  size_t   kv_len,
274                                  size_t   vtcm_budget,
275                                  size_t   n_threads,
276                                  bool     is_q_fp32) {
277    const size_t T       = HMX_FP16_TILE_N_ROWS;  // 32
278    const size_t br_unit = hmx_ceil_div(T, gqa_factor);
279    const size_t bc_unit = HMX_FP16_TILE_N_COLS * 2;  // 64
280    const bool   can_pipeline = (kv_len >= FA_MIN_KV_BLOCKS * bc_unit && n_threads >= 2);
281
282    // Br_max: largest Br aligned to br_unit that does not exceed qo_len.
283    const size_t Br_max = qo_len >= br_unit ? hex_align_down(qo_len, br_unit) : br_unit;
284
285    // Pipeline constraint: cap Bc so n_kv_blocks >= FA_MIN_KV_BLOCKS.
286    // Only relax when kv_len is too short to form enough blocks.
287    const size_t Bc_limit     = can_pipeline ? hex_align_down(kv_len / FA_MIN_KV_BLOCKS, bc_unit) :
288                                               (kv_len >= bc_unit ? hex_align_down(kv_len, bc_unit) : bc_unit);
289    // Cost coefficients calibrated from profiling
290    const size_t c_q_fixed    = 800;   // per-Q-block: q_load + epilogue o_update + o_norm + o_store
291    const size_t c_iter_base  = 200;   // per-KV-iter base (HMX dot/update + DMA)
292    const size_t c_softmax    = 600;   // per 64-row vector chunk on HVX
293
294    size_t best_cost = SIZE_MAX, best_mn = 0;
295    size_t best_Br = 0, best_Bc = 0;
296
297    for (size_t Br = Br_max; Br >= br_unit; Br -= br_unit) {
298        // Try all Bc candidates from Bc_limit down to bc_unit
299        for (size_t Bc = Bc_limit; Bc >= bc_unit; Bc -= bc_unit) {
300            size_t vtcm_needed = hmx_fa_compute_vtcm_usage(gqa_factor, DK, DV, Br, Bc, n_threads, can_pipeline, is_q_fp32);
301            if (vtcm_needed <= vtcm_budget) {
302                // This Bc fits for this Br!
303                const size_t q_blocks       = (qo_len + Br - 1) / Br;
304                const size_t kv_blocks      = (kv_len + Bc - 1) / Bc;
305                const size_t actual_threads = (kv_blocks >= 3 && n_threads >= 2) ? n_threads : 1;
306                const size_t n_rows_g       = Br * gqa_factor;
307                const size_t n_row_vec_cnt  = (n_rows_g + 63) / 64;
308                const size_t n_use          = n_row_vec_cnt < actual_threads ? n_row_vec_cnt : actual_threads;
309                const size_t vecs_per_t     = n_use > 0 ? (n_row_vec_cnt + n_use - 1) / n_use : 1;
310
311                const size_t c_iter_actual  = c_iter_base + c_softmax * vecs_per_t;
312                const size_t cost           = q_blocks * (c_q_fixed + kv_blocks * c_iter_actual);
313                const size_t mn             = Br * Bc;
314
315                if (cost < best_cost || (cost == best_cost && mn > best_mn)) {
316                    best_cost = cost;
317                    best_mn   = mn;
318                    best_Br   = Br;
319                    best_Bc   = Bc;
320                }
321                // Since we iterate Bc from largest to smallest, this is the largest Bc that fits
322                // for this Br. We can break to the next Br.
323                break;
324            }
325        }
326
327        if (Br == br_unit) {
328            break;
329        }
330    }
331
332    if (best_Br == 0 || best_Bc == 0) {
333        return -1;
334    }
335
336    *Br_out = best_Br;
337    *Bc_out = best_Bc;
338    return 0;
339}
340
341#ifdef __cplusplus
342}
343#endif
344
345#endif /* HTP_FLASH_ATTN_OPS_H */