Skip to content

Commit 81ff7ab

Browse files
hexagon: new vtcm layouts and improved pipelines for MUL_MAT, MUL_MAT_ID and FLASH_ATTN_EXT (#25425)
* hex-fa: refactor kernel param compute to use common layout builder * hmx: add explicit compiler barriers to make hmx funcs more robust * hex-vtcm: more generic vtcm layout builder for mm and flash-attn kernels * hex-hmx: unroll inner kernels * hex-hmx: use inline asm instead of intrinsics to avoid compiler issues * hex-hmx: define inline asm macros and simplify code * hex-hmx: replace leftover intrinsics * hmx-fa: minor cleanup for hmx asm * hmx-mm: move per-task stucts out of the kernels header * hmx-mm: simplify core_dot_chunk * hmx-mm: simplify inner loops that call hmx instructions * hmx-mm: proper instrumentation for activation prep work for dma pipelined version * hmx-mm: update a-prep loop for better prefetch * hex-vtcm: improved vtcm layout alloc for mm to support overlapping areas * hmx-mm: reduce the number of act fetch tows to 4 for now, going larger doesnt help here * hex-hmx: always use hmx-queue in all modes * hmx-mm: update comments and minor formatting * hmx-mm: further improve synchro fallback path to prefetch the weights earlier * hex-fa: further pipeline improvements (earlier prefetch) * hmx-mm: cleanup dma pipelines to use dst cached in the queue * hmx-fa: minor cleanup and opts for fa dma pipelines * hmx-fa: optimize q-prep stage with dma and unrolling * hmx-fa: use o_tile size from layout instead of computing it * hmx-mm: cleanup types and size handling * hmx-mm: replace divs with fastdiv in qprep loops * hmx-fa: minor update/formatting to q_tile handling * hmx-fa: cleanup the layout to avoid overpadding * hmx-fa: simplified and improved cost mode for hmx fa solver that uses vtcm layout funcs * hmx-queue: add support queue wakeup and make suspend async to avoid hmx-lock latency * hex-hmx: move queue wakeup / suspend to the op-batch level * hex-threads: add hybrid polling to workpool * hex-mm: fix trailing spaces
1 parent c264f65 commit 81ff7ab

17 files changed

Lines changed: 2014 additions & 1221 deletions

ggml/src/ggml-hexagon/ggml-hexagon.cpp

Lines changed: 132 additions & 174 deletions
Large diffs are not rendered by default.

ggml/src/ggml-hexagon/htp/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_library(${HTP_LIB} SHARED
2020
worker-pool.c
2121
hex-dma.c
2222
hmx-queue.c
23+
gated-delta-net-ops.c
2324
binary-ops.c
2425
unary-ops.c
2526
sum-rows-ops.c
@@ -37,10 +38,9 @@ add_library(${HTP_LIB} SHARED
3738
concat-ops.c
3839
diag-ops.c
3940
solve-tri-ops.c
40-
gated-delta-net-ops.c
4141
pad-ops.c
42-
matmul-ops.c
4342
flash-attn-ops.c
43+
matmul-ops.c
4444
)
4545

4646
target_compile_definitions(${HTP_LIB} PRIVATE

ggml/src/ggml-hexagon/htp/concat-ops.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include "hexagon_protos.h"
55
#include "hvx_hexagon_protos.h"
66
#include "hex-dma.h"
7-
#include "vtcm-utils.h"
7+
#include "htp-vtcm.h"
88
#include "hvx-utils.h"
99
#include "hex-fastdiv.h"
1010
#include <string.h>

ggml/src/ggml-hexagon/htp/flash-attn-ops.c

Lines changed: 277 additions & 293 deletions
Large diffs are not rendered by default.

ggml/src/ggml-hexagon/htp/flash-attn-ops.h

Lines changed: 144 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,23 @@
77

88
#include "hex-fastdiv.h"
99
#include "hex-common.h"
10+
#include "htp-vtcm.h"
1011

1112
#ifdef __cplusplus
1213
extern "C" {
1314
#endif
1415

1516
// 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
1619
#define HMX_FP16_TILE_N_ROWS 32
1720
#define HMX_FP16_TILE_N_COLS 32
1821
#define HMX_FP16_TILE_N_ELMS 1024
19-
#define HMX_FP16_TILE_SIZE 2048
22+
2023
#define HVX_FA_DMA_CACHE_SIZE 128
2124
#define HMX_FA_DMA_CACHE_SIZE 4
2225

26+
2327
#define HTP_FA_M_INITIAL_VAL -10000.0f
2428

2529
enum htp_fa_kernel_type {
@@ -54,6 +58,11 @@ struct htp_fa_kernel_params {
5458
struct fastdiv_values src3_div2;
5559
struct fastdiv_values src3_div3;
5660

61+
struct fastdiv_values broadcast_rk2;
62+
struct fastdiv_values broadcast_rk3;
63+
struct fastdiv_values broadcast_rv2;
64+
struct fastdiv_values broadcast_rv3;
65+
5766
union {
5867
struct {
5968
uint32_t g_br;
@@ -69,10 +78,6 @@ struct htp_fa_kernel_params {
6978
uint32_t size_v_row_padded;
7079
struct fastdiv_values src0_div21;
7180
struct fastdiv_values src0_div1;
72-
struct fastdiv_values broadcast_rk2;
73-
struct fastdiv_values broadcast_rk3;
74-
struct fastdiv_values broadcast_rv2;
75-
struct fastdiv_values broadcast_rv3;
7681
} hvx;
7782
} u;
7883
};
@@ -81,39 +86,124 @@ struct htp_fa_kernel_params {
8186
static_assert(sizeof(struct htp_fa_kernel_params) <= 128, "htp_fa_kernel_params is too large for kernel_params blob");
8287
#endif
8388

84-
// Exact VTCM usage for a given (gqa_factor, DK, DV, Br, Bc) configuration.
85-
// g_br = hex_align_up(gqa_factor * Br, 32) replaces Br for all Q/O/S/P/D dimensions.
86-
// Layout: Q + O_ping + O_pong + K_dma*2 + V_dma*2 + K_tile + V_tile + S + P + D + vectors + scales
87-
// Mask is DMA'd into a VTCM buffer (Br rows per KV block) to avoid DDR reads in softmax.
88-
static 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) {
89+
// VTCM region layout for the HMX flash-attention kernel.
90+
//
91+
// Single source of truth for both the host (which needs the total size to pick a
92+
// (Br, Bc) tiling that fits the VTCM budget) and the device (which needs the actual
93+
// byte offsets to place each scratch buffer). Building the layout once and reading
94+
// offsets/total from it makes host estimate and device allocation impossible to
95+
// desync -- previously they were duplicated formulas in two files and drifted.
96+
//
97+
// All fields are byte offsets / byte sizes -- no HVX_Vector type is named here so the
98+
// header stays host-includable. The device casts (base + off_*) to the proper type.
99+
// An offset of 0 marks a region that is not allocated for this configuration (only
100+
// off_v_tiles[1], which exists only when pipelining); the device sets such pointers NULL.
101+
struct hmx_fa_vtcm_layout {
102+
// Byte offsets from vtcm_base for each region.
103+
size_t off_q_tiles;
104+
size_t off_o_tiles[2];
105+
size_t off_k_fp16[2];
106+
size_t off_v_fp16[2];
107+
size_t off_k_tiles;
108+
size_t off_v_tiles[2]; // [1] allocated only when pipeline, else 0
109+
size_t off_s_tiles;
110+
size_t off_p_tiles;
111+
size_t off_d_tiles;
112+
size_t off_m_vec;
113+
size_t off_l_vec;
114+
size_t off_s_rowmax;
115+
size_t off_p_rowsum;
116+
size_t off_row_bufs;
117+
size_t off_hmx_scales_id;
118+
size_t off_hmx_scales_qk;
119+
size_t off_mask_buf;
120+
size_t off_slopes;
121+
122+
// Region byte sizes reused by the device at runtime (not just for allocation).
123+
size_t q_tile_bytes;
124+
size_t o_tile_bytes;
125+
size_t s_tile_bytes; // S and P tiles (same size)
126+
size_t d_tile_bytes;
127+
size_t m_line_bytes; // one mask row
128+
size_t m_buf_slot_bytes; // one dma_cache slot = align_up(Br * m_line_bytes, 4096)
129+
size_t col_vec_bytes;
130+
131+
// Derived strides.
132+
size_t row_buf_stride; // HVX vectors (128B) per row buffer
133+
size_t mask_buf_row_stride; // __fp16 elements per row in the mask buffer
134+
135+
bool pipeline;
136+
size_t total_bytes;
137+
};
138+
139+
// Build the VTCM layout.
140+
141+
static inline void hmx_fa_vtcm_layout_build(struct hmx_fa_vtcm_layout * L,
142+
size_t gqa_factor, size_t DK, size_t DV,
143+
size_t Br, size_t Bc, size_t n_threads, bool pipeline) {
89144
const size_t g_br = hex_align_up(gqa_factor * Br, HMX_FP16_TILE_N_ROWS);
90-
const size_t q_tile_size = hex_align_up(g_br * DK * sizeof(__fp16), 4096); // Q: [g_br, DK]
91-
const size_t o_tile_size = hex_align_up(g_br * DV * sizeof(__fp16), 4096); // O: [g_br, DV] x2 ping-pong
92-
const size_t k_dma_size = hex_align_up(Bc * hex_round_up(DK * sizeof(__fp16), 128), 4096); // K DMA: [Bc, DK] x2 double-buf
93-
const size_t v_dma_size = hex_align_up(Bc * hex_round_up(DV * sizeof(__fp16), 128), 4096); // V DMA: [Bc, DV] x2 double-buf
94-
const size_t k_tile_size = hex_align_up(Bc * DK * sizeof(__fp16), 4096); // K tiles: [Bc, DK] interleaved
95-
const size_t v_tile_size = hex_align_up(Bc * DV * sizeof(__fp16), 4096); // V tiles: [Bc, DV] interleaved
96-
const size_t s_tile_size = hex_align_up(g_br * Bc * sizeof(__fp16), 4096); // S/P:[g_br, Bc]
97-
const size_t d_tile_size = hex_align_up(g_br * g_br * sizeof(__fp16), 4096); // D: [g_br, g_br]
98-
const size_t col_vec_size = hex_align_up(g_br * sizeof(float), 256); // m, l, etc.
99-
const size_t row_vec_size = hex_align_up(Bc * sizeof(__fp16), 256);
100-
const size_t m_line_size = hex_align_up(Bc * sizeof(__fp16), 128);
101-
const size_t m_buf_size = hex_align_up(Br * m_line_size, 4096) * HMX_FA_DMA_CACHE_SIZE;
145+
const size_t q_tile_size = hex_align_up(g_br * DK * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
146+
const size_t o_tile_size = hex_align_up(g_br * DV * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
147+
const size_t k_tile_size = hex_align_up(Bc * DK * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
148+
const size_t v_tile_size = hex_align_up(Bc * DV * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
149+
const size_t s_tile_size = hex_align_up(g_br * Bc * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
150+
const size_t d_tile_size = hex_align_up(g_br * g_br * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
151+
152+
const size_t k_dma_size = hex_align_up(Bc * hex_round_up(DK * sizeof(__fp16), 128), 128);
153+
const size_t v_dma_size = hex_align_up(Bc * hex_round_up(DV * sizeof(__fp16), 128), 128);
154+
const size_t col_vec_size = hex_align_up(g_br * sizeof(float), 256);
155+
const size_t row_vec_size = hex_align_up(Bc * sizeof(__fp16), 256);
156+
const size_t m_line_size = hex_align_up(Bc * sizeof(__fp16), 128);
157+
const size_t m_buf_slot = hex_align_up(Br * m_line_size, 256);
158+
const size_t m_buf_size = m_buf_slot * HMX_FA_DMA_CACHE_SIZE;
102159
const size_t slopes_size = hex_align_up(g_br * sizeof(__fp16), 128);
103160

104-
return q_tile_size * 1 // Q tiles
105-
+ o_tile_size * 2 // O ping-pong
106-
+ k_dma_size * 2 // K DMA x2
107-
+ v_dma_size * 2 // V DMA x2
108-
+ k_tile_size * 1 // K tiles
109-
+ v_tile_size * (pipeline ? 2 : 1) // V tiles (double-buffered if pipelining)
110-
+ s_tile_size * 2 // S + P
111-
+ d_tile_size * 1 // D (diagonal matrix)
112-
+ col_vec_size * 4 // m_vec, l_vec, s_rowmax, p_rowsum
113-
+ row_vec_size * 2 * n_threads // per-thread softmax row scratch
114-
+ m_buf_size * 1 // mask VTCM buffer [Br rows]
115-
+ slopes_size // Slopes
116-
+ 256 * 2; // HMX scales (id + qk)
161+
size_t off = 0;
162+
163+
// Section 1: HMX Tiled Buffers (FA_HMX_TILE_SIZE = 2KB Aligned)
164+
VTCM_LAYOUT_ALLOC(off, off_q_tiles, q_tile_size);
165+
VTCM_LAYOUT_ALLOC(off, off_o_tiles[0], o_tile_size);
166+
VTCM_LAYOUT_ALLOC(off, off_o_tiles[1], o_tile_size);
167+
VTCM_LAYOUT_ALLOC(off, off_k_tiles, k_tile_size);
168+
VTCM_LAYOUT_ALLOC(off, off_v_tiles[0], v_tile_size);
169+
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_v_tiles[1], v_tile_size, pipeline);
170+
VTCM_LAYOUT_ALLOC(off, off_s_tiles, s_tile_size);
171+
VTCM_LAYOUT_ALLOC(off, off_p_tiles, s_tile_size);
172+
VTCM_LAYOUT_ALLOC(off, off_d_tiles, d_tile_size);
173+
174+
// Section 2: HVX/DMA flat and vector buffers (128B / 256B Aligned)
175+
VTCM_LAYOUT_ALLOC(off, off_k_fp16[0], k_dma_size);
176+
VTCM_LAYOUT_ALLOC(off, off_k_fp16[1], k_dma_size);
177+
VTCM_LAYOUT_ALLOC(off, off_v_fp16[0], v_dma_size);
178+
VTCM_LAYOUT_ALLOC(off, off_v_fp16[1], v_dma_size);
179+
VTCM_LAYOUT_ALLOC(off, off_m_vec, col_vec_size);
180+
VTCM_LAYOUT_ALLOC(off, off_l_vec, col_vec_size);
181+
VTCM_LAYOUT_ALLOC(off, off_s_rowmax, col_vec_size);
182+
VTCM_LAYOUT_ALLOC(off, off_p_rowsum, col_vec_size);
183+
VTCM_LAYOUT_ALLOC(off, off_row_bufs, row_vec_size * 2 * n_threads);
184+
VTCM_LAYOUT_ALLOC(off, off_hmx_scales_id, 256);
185+
VTCM_LAYOUT_ALLOC(off, off_hmx_scales_qk, 256);
186+
VTCM_LAYOUT_ALLOC(off, off_mask_buf, m_buf_size);
187+
VTCM_LAYOUT_ALLOC(off, off_slopes, slopes_size);
188+
189+
L->q_tile_bytes = q_tile_size;
190+
L->o_tile_bytes = o_tile_size;
191+
L->col_vec_bytes = col_vec_size;
192+
L->s_tile_bytes = s_tile_size;
193+
L->d_tile_bytes = d_tile_size;
194+
L->m_line_bytes = m_line_size;
195+
L->m_buf_slot_bytes = m_buf_slot;
196+
L->row_buf_stride = row_vec_size / 128;
197+
L->mask_buf_row_stride = m_line_size / sizeof(__fp16);
198+
L->pipeline = pipeline;
199+
L->total_bytes = off;
200+
}
201+
202+
// Exact VTCM usage for a given (gqa_factor, DK, DV, Br, Bc) configuration.
203+
static 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) {
204+
struct hmx_fa_vtcm_layout L;
205+
hmx_fa_vtcm_layout_build(&L, gqa_factor, DK, DV, Br, Bc, n_threads, pipeline);
206+
return L.total_bytes;
117207
}
118208

119209
#define FA_HVX_BLOCK_SIZE 64
@@ -153,23 +243,8 @@ static inline int hmx_fa_find_chunk_size(size_t * Br_out,
153243
const size_t T = HMX_FP16_TILE_N_ROWS; // 32
154244
const size_t br_unit = hmx_ceil_div(T, gqa_factor);
155245
const size_t bc_unit = HMX_FP16_TILE_N_COLS * 2; // 64
156-
const size_t fp16 = sizeof(__fp16);
157246
const bool can_pipeline = (kv_len >= FA_MIN_KV_BLOCKS * bc_unit && n_threads >= 2);
158247

159-
// Approximate per-unit VTCM costs (without per-buffer alignment padding).
160-
const size_t per_gbr = (DK + 2 * DV) * fp16 + 4 * sizeof(float); // Q + O*2 + 4 col vectors
161-
const size_t per_gbr2 = fp16; // D diagonal matrix
162-
const size_t per_bc =
163-
3 * DK * fp16 + (can_pipeline ? 4 : 3) * DV * fp16 + 2 * n_threads * fp16; // K/V DMA x2 + tiles + row bufs
164-
const size_t per_gbr_bc = 2 * fp16; // S + P
165-
166-
const size_t overhead = 256 * 2 + 13 * 4096;
167-
168-
if (vtcm_budget <= overhead) {
169-
return -1;
170-
}
171-
const size_t usable = vtcm_budget - overhead;
172-
173248
// Br_max: largest Br aligned to br_unit that does not exceed qo_len.
174249
const size_t Br_max = qo_len >= br_unit ? hex_align_down(qo_len, br_unit) : br_unit;
175250

@@ -185,59 +260,34 @@ static inline int hmx_fa_find_chunk_size(size_t * Br_out,
185260
size_t best_Br = 0, best_Bc = 0;
186261

187262
for (size_t Br = Br_max; Br >= br_unit; Br -= br_unit) {
188-
const size_t g_br = hex_align_up(gqa_factor * Br, T);
189-
190-
// g_br-dependent VTCM cost: g_br * per_gbr + g_br*g_br * per_gbr2
191-
const size_t gbr_cost = g_br * per_gbr + g_br * g_br * per_gbr2;
192-
if (gbr_cost >= usable) {
193-
if (Br == br_unit) {
194-
break;
195-
}
196-
continue;
197-
}
198-
199-
// Analytically solve for max Bc:
200-
// remain >= Bc * (per_bc + g_br * per_gbr_bc + Br * fp16 * HMX_FA_DMA_CACHE_SIZE)
201-
// The Br * fp16 term accounts for the VTCM mask buffer [Br * Bc].
202-
const size_t remain = usable - gbr_cost;
203-
const size_t bc_denom = per_bc + g_br * per_gbr_bc + Br * fp16 * HMX_FA_DMA_CACHE_SIZE;
204-
size_t Bc = hex_smin(hex_align_down(remain / bc_denom, bc_unit), Bc_limit);
205-
if (Bc < bc_unit) {
206-
if (Br == br_unit) {
263+
// Try all Bc candidates from Bc_limit down to bc_unit
264+
for (size_t Bc = Bc_limit; Bc >= bc_unit; Bc -= bc_unit) {
265+
size_t vtcm_needed = hmx_fa_compute_vtcm_usage(gqa_factor, DK, DV, Br, Bc, n_threads, can_pipeline);
266+
if (vtcm_needed <= vtcm_budget) {
267+
// This Bc fits for this Br!
268+
const size_t q_blocks = (qo_len + Br - 1) / Br;
269+
const size_t kv_blocks = (kv_len + Bc - 1) / Bc;
270+
const size_t cost = q_blocks * (c_q_fixed + kv_blocks * c_iter_fixed);
271+
const size_t mn = Br * Bc;
272+
273+
if (cost < best_cost || (cost == best_cost && mn > best_mn)) {
274+
best_cost = cost;
275+
best_mn = mn;
276+
best_Br = Br;
277+
best_Bc = Bc;
278+
}
279+
// Since we iterate Bc from largest to smallest, this is the largest Bc that fits
280+
// for this Br. We can break to the next Br.
207281
break;
208282
}
209-
continue;
210-
}
211-
212-
// Exact VTCM verification (alignment padding may push over budget)
213-
while (Bc >= bc_unit && hmx_fa_compute_vtcm_usage(gqa_factor, DK, DV, Br, Bc, n_threads, can_pipeline) > vtcm_budget) {
214-
Bc -= bc_unit;
215-
}
216-
if (Bc < bc_unit) {
217-
if (Br == br_unit) {
218-
break;
219-
}
220-
continue;
221-
}
222-
223-
const size_t q_blocks = (qo_len + Br - 1) / Br;
224-
const size_t kv_blocks = (kv_len + Bc - 1) / Bc;
225-
const size_t cost = q_blocks * (c_q_fixed + kv_blocks * c_iter_fixed);
226-
const size_t mn = Br * Bc;
227-
228-
if (cost < best_cost || (cost == best_cost && mn > best_mn)) {
229-
best_cost = cost;
230-
best_mn = mn;
231-
best_Br = Br;
232-
best_Bc = Bc;
233283
}
234284

235285
if (Br == br_unit) {
236286
break;
237287
}
238288
}
239289

240-
if (best_Br == 0) {
290+
if (best_Br == 0 || best_Bc == 0) {
241291
return -1;
242292
}
243293

0 commit comments

Comments
 (0)