Download build/torch-neuron/nki_kernels/attention_block_l1.py from aws-neuron/vit-encoder-neuron-kernels: direct link, hf CLI and curl.
- Browser
- Download file 21.7 kB
-
https://huggingface.co/kernels/aws-neuron/vit-encoder-neuron-kernels/resolve/v1/build/torch-neuron/nki_kernels/attention_block_l1.py
- Command line
-
hf download hf://aws-neuron/vit-encoder-neuron-kernels@v1/build/torch-neuron/nki_kernels/attention_block_l1.py
-
curl -L -o attention_block_l1.py https://huggingface.co/kernels/aws-neuron/vit-encoder-neuron-kernels/resolve/v1/build/torch-neuron/nki_kernels/attention_block_l1.py
21.7 kB
| """Attention for LNC=1: ONE program over ALL head groups. | |
| The 2-core kernel in ``attention_block.py`` owns one 8-head group per program. At LNC=1 that | |
| means one launch per group per layer (2 for ViT-L, 3 for ViT-H+), each re-transposing x, each | |
| re-loading the rope tables, each writing an [N, H] bf16 out-projection PARTIAL to HBM that the | |
| LN-fold kernel then sums. | |
| This kernel, per image (``nimg`` images stacked on the row axis, one launch): | |
| * transposes x once and loads the rope tables once (shared by every group: tables are | |
| quad-tiled per head, identical for all heads); | |
| * for each group: projects Q/K/V + RoPE (same producer as attention_block), runs the vendor | |
| attention per head, and writes O feature-major into a PERSISTENT per-group SBUF slab; | |
| * after all groups: ONE out-projection contracting over ALL features (G*4 chunks accumulate | |
| in one fp32 PSUM tile), adds the FULL bias once, writes ONE [N, H] result. | |
| No partials, no host sum; the LN-fold kernel receives a single branch. | |
| ``resident=1``: Q/K/V/O weights, biases and rope tables are staged ONCE per launch (bottom of | |
| SBUF) and reused by every image. Host decides by an SBUF estimate (does not fit at N>=1024 for | |
| ViT-L); ``resident=0`` restages each group's weights per image. | |
| N<=2048 only (the shipping single-section vendor path). Numerics: identical producer and vendor | |
| attention; the out-projection now accumulates all groups in fp32 PSUM instead of summing bf16 | |
| per-group partials, so results differ from attention_block by bf16 rounding of the partials. | |
| """ | |
| from __future__ import annotations | |
| import torch | |
| from nki.framework.kernel import wrap_nki_to_torch_hop | |
| from torch_neuronx import nki_op | |
| import nki | |
| import nki.isa as nisa | |
| import nki.language as nl | |
| import os as _osr | |
| _ROPE_BF16 = _osr.environ.get("VIT_ENC_ROPE_BF16", "0") == "1" | |
| _ROPE_DT = nl.bfloat16 if _ROPE_BF16 else nl.float32 | |
| from nki.isa import engine | |
| from nkilib.core.utils.modular_allocator import ModularAllocator | |
| from .attention_inner import _attention_cte | |
| from .attention_block import pack_qk_bias as _pack_qk_bias | |
| _P_MAX = 128 | |
| _PSUM_FMAX = 512 | |
| _PSUM_BANK = 2048 | |
| _K_TILE = 512 | |
| _V_TILE = 128 | |
| def _kernel_assert(cond: bool, msg: str): | |
| assert cond, f"[INTERNAL_ERROR] [NCC_INKI016] Kernel validation exception: {msg}" | |
| def sbuf_estimate(H: int, Fp: int, N: int, D: int, resident: bool) -> int: | |
| """Conservative bytes/lane at the kernel's peak (host-side decision helper, not exact).""" | |
| G = Fp // _PSUM_FMAX | |
| n_chunks = H // _P_MAX | |
| n_tt = (N + _PSUM_FMAX - 1) // _PSUM_FMAX | |
| n_ktiles = (N + _K_TILE - 1) // _K_TILE | |
| lh = _PSUM_FMAX // D | |
| n_och = (lh * D) // _P_MAX | |
| w_grp = 3 * n_chunks * _PSUM_FMAX * 2 + _PSUM_FMAX * 4 + 64 # wqk + wv + bv_bc + biases | |
| w_out = G * n_och * H * 2 + H * 4 # wo + bo_bc | |
| tab = 4 * n_tt * _PSUM_FMAX * 4 | |
| o_feat = G * n_och * N * 2 | |
| x_t = n_chunks * n_tt * _PSUM_FMAX * 2 | |
| kv = lh * (n_ktiles * _K_TILE * 2 + (N // _V_TILE) * D * 2) | |
| work = 2 * _PSUM_FMAX * 2 * 2 + 2 * _PSUM_FMAX * 4 + _PSUM_FMAX * 4 | |
| inner = 48 * 1024 | |
| resid = (G * w_grp + w_out + tab) if resident else 0 | |
| base = resid + o_feat + x_t + (0 if resident else tab) | |
| p1 = kv + (0 if resident else w_grp) + work | |
| p2 = kv + inner | |
| p3 = (0 if resident else w_out) + 2048 | |
| return base + max(p1, p2, p3) | |
| def _attention_block_l1_kernel(h_hbm, wqk_hbm, bqk_hbm, tab_hbm, wvt_hbm, bv_hbm, | |
| wot_hbm, bo_hbm, scale, head_dim, n_valid, nimg=1, resident=0): | |
| NT, H = h_hbm.shape | |
| N = NT // nimg | |
| Fp = wqk_hbm.shape[2] | |
| D = head_dim | |
| half = D // 2 | |
| quad = _P_MAX // half | |
| local_heads = _PSUM_FMAX // D | |
| local_quads = local_heads // quad | |
| G = Fp // _PSUM_FMAX # head groups, ALL handled by this one program | |
| n_chunks = H // _P_MAX | |
| n_tt = (N + _PSUM_FMAX - 1) // _PSUM_FMAX | |
| n_ktiles = (N + _K_TILE - 1) // _K_TILE | |
| n_vtiles = N // _V_TILE | |
| n_ochunks = (local_heads * D) // _P_MAX # feature chunks per group | |
| v_per_tt = _PSUM_FMAX // _V_TILE | |
| _kernel_assert(wqk_hbm.shape == (2, H, Fp), "bad wqk") | |
| _kernel_assert(bqk_hbm.shape == (2, _P_MAX, Fp // _P_MAX), "bqk must be pack_qk_bias(...)") | |
| _kernel_assert(tab_hbm.shape == (4, _P_MAX, N), "bad tab") | |
| _kernel_assert(wvt_hbm.shape == (H, Fp) and bv_hbm.shape == (1, Fp), "bad V weights") | |
| _kernel_assert(wot_hbm.shape == (Fp, H) and bo_hbm.shape == (1, H), "bad O weights") | |
| _kernel_assert(H % _P_MAX == 0, "H must be a multiple of 128") | |
| _kernel_assert(quad * half == _P_MAX, "D must give quad*half=128") | |
| _kernel_assert(Fp % _PSUM_FMAX == 0, "padded features must be a multiple of 512") | |
| _kernel_assert(N % _P_MAX == 0, "N must be a multiple of 128") | |
| _kernel_assert(N <= _PSUM_FMAX * 4, "N>2048 not supported") | |
| _kernel_assert(NT == nimg * N, "rows must be nimg x N") | |
| result = nl.ndarray((1, NT, H), dtype=nl.bfloat16, buffer=nl.shared_hbm) | |
| # ---------------- resident (per-launch invariant) staging ---------------- | |
| res_top = 0 | |
| r_w = [] | |
| r_wv = [] | |
| r_b = [] | |
| r_bv = [] | |
| if resident: | |
| ra = ModularAllocator(initial_address=0) | |
| for g in range(G): | |
| col0 = g * _PSUM_FMAX | |
| w_sb_ = ra.alloc_sbuf_tensor(shape=(_P_MAX, local_quads * 2 * _P_MAX), dtype=nl.bfloat16, | |
| block_dim=[2, n_chunks], num_free_tiles=[2, n_chunks]) | |
| for qk in range(2): | |
| for c in range(n_chunks): | |
| nisa.dma_copy(dst=w_sb_[qk][c], | |
| src=wqk_hbm[qk, c * _P_MAX:(c + 1) * _P_MAX, | |
| col0:col0 + local_quads * 2 * _P_MAX]) | |
| wv_sb_ = ra.alloc_sbuf_tensor(shape=(_P_MAX, local_heads * D), dtype=nl.bfloat16, | |
| block_dim=[n_chunks], num_free_tiles=[n_chunks]) | |
| for c in range(n_chunks): | |
| nisa.dma_copy(dst=wv_sb_[c], | |
| src=wvt_hbm[c * _P_MAX:(c + 1) * _P_MAX, col0:col0 + local_heads * D]) | |
| # Q/K biases: host-packed fp32 [2, 128, Fp/128] (pack_qk_bias); this program's groups land in | |
| # ONE DMA per q/k on the Scalar HW-DGE queue (was one [128,1] DMA + one cast per group). | |
| b_sb_ = ra.alloc_sbuf_tensor(shape=(_P_MAX, local_quads * 2), dtype=nl.float32, | |
| block_dim=[2], num_free_tiles=[2], align_to=4) | |
| for qk in range(2): | |
| nisa.dma_copy(dst=b_sb_[qk], src=bqk_hbm[qk, 0:_P_MAX, g * (_PSUM_FMAX // _P_MAX):g * (_PSUM_FMAX // _P_MAX) + local_quads * 2], | |
| dge_mode=nisa.dge_mode.hwdge, engine=nisa.engine.scalar) | |
| bv_bc_ = ra.alloc_sbuf_tensor(shape=(_V_TILE, local_heads * D), dtype=nl.float32, align_to=4) | |
| nisa.dma_copy(dst=bv_bc_, | |
| src=bv_hbm.ap(pattern=[[0, _V_TILE], [1, local_heads * D]], offset=col0)) | |
| r_w.append(w_sb_) | |
| r_wv.append(wv_sb_) | |
| r_b.append(b_sb_) | |
| r_bv.append(bv_bc_) | |
| r_tab = ra.alloc_sbuf_tensor(shape=(_P_MAX, _PSUM_FMAX), dtype=_ROPE_DT, | |
| block_dim=[4, n_tt], num_free_tiles=[4, n_tt], align_to=4) | |
| for s in range(n_tt): | |
| for t_i in range(4): | |
| _tw = min(_PSUM_FMAX, N - s * _PSUM_FMAX) | |
| nisa.dma_copy(dst=r_tab[t_i][s][:, 0:_tw], | |
| src=tab_hbm[t_i, 0:_P_MAX, s * _PSUM_FMAX:s * _PSUM_FMAX + _tw]) | |
| r_wo = ra.alloc_sbuf_tensor(shape=(_P_MAX, H), dtype=nl.bfloat16, | |
| block_dim=[G * n_ochunks], num_free_tiles=[G * n_ochunks]) | |
| for oc in range(G * n_ochunks): | |
| nisa.dma_copy(dst=r_wo[oc], src=wot_hbm[oc * _P_MAX:(oc + 1) * _P_MAX, 0:H]) | |
| r_bo = ra.alloc_sbuf_tensor(shape=(_P_MAX, H), dtype=nl.float32, align_to=4) | |
| nisa.dma_copy(dst=r_bo, src=bo_hbm.ap(pattern=[[0, _P_MAX], [1, H]], offset=0)) | |
| res_top = ra.get_current_address() | |
| n_grp = N // _P_MAX | |
| for img in range(nimg): | |
| rofs = img * N * H | |
| allocator = ModularAllocator(initial_address=res_top) | |
| # O for ALL groups stays in SBUF until the single out-projection. | |
| o_feat = [] | |
| for g in range(G): | |
| of_ = allocator.alloc_sbuf_tensor(shape=(_P_MAX, N), dtype=nl.bfloat16, | |
| block_dim=[n_ochunks], num_free_tiles=[n_ochunks], | |
| align_to=32) | |
| o_feat.append(of_) | |
| # x^T: shared by every group (transposed ONCE per image). | |
| x_t = allocator.alloc_sbuf_tensor(shape=(_P_MAX, _PSUM_FMAX), dtype=nl.bfloat16, | |
| block_dim=[n_chunks, n_tt], num_free_tiles=[n_chunks, n_tt], | |
| align_to=32) | |
| for s in range(n_tt): | |
| for c in range(n_chunks): | |
| nisa.dma_transpose( | |
| dst=x_t[c][s].ap([[_PSUM_FMAX, _P_MAX], [1, 1], [1, 1], | |
| [1, min(_PSUM_FMAX, N - s * _PSUM_FMAX)]], offset=0), | |
| src=h_hbm.ap([[H, min(_PSUM_FMAX, N - s * _PSUM_FMAX)], [1, 1], [1, 1], [1, _P_MAX]], | |
| offset=rofs + (s * _PSUM_FMAX) * H + c * _P_MAX), | |
| ) | |
| # rope tables: shared by every group. | |
| if resident: | |
| tab = r_tab | |
| else: | |
| tab = allocator.alloc_sbuf_tensor(shape=(_P_MAX, _PSUM_FMAX), dtype=_ROPE_DT, | |
| block_dim=[4, n_tt], num_free_tiles=[4, n_tt], align_to=4) | |
| for s in range(n_tt): | |
| for t_i in range(4): | |
| _tw = min(_PSUM_FMAX, N - s * _PSUM_FMAX) | |
| nisa.dma_copy(dst=tab[t_i][s][:, 0:_tw], | |
| src=tab_hbm[t_i, 0:_P_MAX, s * _PSUM_FMAX:s * _PSUM_FMAX + _tw]) | |
| img_base = allocator.get_current_address() | |
| for g in range(G): | |
| allocator.set_current_address(img_base) | |
| col0 = g * _PSUM_FMAX | |
| k_tiles = [] | |
| v_tiles = [] | |
| for _lh in range(local_heads): | |
| kt_ = allocator.alloc_sbuf_tensor(shape=(D, _K_TILE), dtype=nl.bfloat16, | |
| block_dim=[n_ktiles], num_free_tiles=[n_ktiles], | |
| align_to=32) | |
| k_tiles.append(kt_) | |
| vt_ = allocator.alloc_sbuf_tensor(shape=(_V_TILE, D), dtype=nl.bfloat16, | |
| block_dim=[n_vtiles], num_free_tiles=[n_vtiles]) | |
| v_tiles.append(vt_) | |
| phase2_base = allocator.get_current_address() | |
| if resident: | |
| w_sb = r_w[g] | |
| wv_sb = r_wv[g] | |
| b_sb = r_b[g] | |
| bv_bc = r_bv[g] | |
| else: | |
| w_sb = allocator.alloc_sbuf_tensor(shape=(_P_MAX, local_quads * 2 * _P_MAX), | |
| dtype=nl.bfloat16, | |
| block_dim=[2, n_chunks], num_free_tiles=[2, n_chunks]) | |
| for qk in range(2): | |
| for c in range(n_chunks): | |
| nisa.dma_copy(dst=w_sb[qk][c], | |
| src=wqk_hbm[qk, c * _P_MAX:(c + 1) * _P_MAX, | |
| col0:col0 + local_quads * 2 * _P_MAX]) | |
| wv_sb = allocator.alloc_sbuf_tensor(shape=(_P_MAX, local_heads * D), dtype=nl.bfloat16, | |
| block_dim=[n_chunks], num_free_tiles=[n_chunks]) | |
| for c in range(n_chunks): | |
| nisa.dma_copy(dst=wv_sb[c], | |
| src=wvt_hbm[c * _P_MAX:(c + 1) * _P_MAX, col0:col0 + local_heads * D]) | |
| # Q/K biases: host-packed fp32 [2, 128, Fp/128] (pack_qk_bias); this program's groups land in | |
| # ONE DMA per q/k on the Scalar HW-DGE queue (was one [128,1] DMA + one cast per group). | |
| b_sb = allocator.alloc_sbuf_tensor(shape=(_P_MAX, local_quads * 2), dtype=nl.float32, | |
| block_dim=[2], num_free_tiles=[2], align_to=4) | |
| for qk in range(2): | |
| nisa.dma_copy(dst=b_sb[qk], src=bqk_hbm[qk, 0:_P_MAX, g * (_PSUM_FMAX // _P_MAX):g * (_PSUM_FMAX // _P_MAX) + local_quads * 2], | |
| dge_mode=nisa.dge_mode.hwdge, engine=nisa.engine.scalar) | |
| bv_bc = allocator.alloc_sbuf_tensor(shape=(_V_TILE, local_heads * D), dtype=nl.float32, | |
| align_to=4) | |
| nisa.dma_copy(dst=bv_bc, | |
| src=bv_hbm.ap(pattern=[[0, _V_TILE], [1, local_heads * D]], offset=col0)) | |
| proj = allocator.alloc_sbuf_tensor(shape=(_P_MAX, _PSUM_FMAX), dtype=nl.bfloat16, | |
| block_dim=[2], num_free_tiles=[2]) | |
| acc = allocator.alloc_sbuf_tensor(shape=(_P_MAX, _PSUM_FMAX), dtype=_ROPE_DT, align_to=4) | |
| tmp = allocator.alloc_sbuf_tensor(shape=(_P_MAX, _PSUM_FMAX), dtype=_ROPE_DT, align_to=4) | |
| roped = allocator.alloc_sbuf_tensor(shape=(_P_MAX, _PSUM_FMAX), dtype=nl.bfloat16, | |
| block_dim=[2], num_free_tiles=[2]) | |
| vout = allocator.alloc_sbuf_tensor(shape=(_V_TILE, local_heads * D), dtype=nl.float32, | |
| align_to=4) | |
| q_priv = [] | |
| for _lh in range(local_heads): | |
| q_priv.append(nl.ndarray((1, D, N), dtype=nl.bfloat16, buffer=nl.private_hbm)) | |
| # ---- producer (identical to attention_block's N<=2048 path) ---- | |
| for tt in range(n_tt): | |
| t0 = tt * _PSUM_FMAX | |
| tw = min(_PSUM_FMAX, N - t0) | |
| for vsub in range(tw // _V_TILE): | |
| ps_v = nl.ndarray((_V_TILE, local_heads * D), dtype=nl.float32, | |
| buffer=nl.psum, address=(0, 7 * _PSUM_BANK)) | |
| for c in range(n_chunks): | |
| nisa.nc_matmul(dst=ps_v, | |
| stationary=x_t[c][tt][:, vsub * _V_TILE:(vsub + 1) * _V_TILE], | |
| moving=wv_sb[c], accumulate=(c > 0)) | |
| nisa.tensor_tensor(dst=vout, data1=ps_v, data2=bv_bc, op=nl.add) | |
| for lh in range(local_heads): | |
| nisa.tensor_copy(dst=v_tiles[lh][tt * v_per_tt + vsub], | |
| src=vout[:, lh * D:(lh + 1) * D], engine=engine.scalar) | |
| for qk in range(2): | |
| for j in range(local_quads): | |
| for par in range(2): | |
| gq = j * 2 + par | |
| cols = gq * _P_MAX | |
| ps = nl.ndarray((_P_MAX, _PSUM_FMAX), dtype=nl.float32, buffer=nl.psum, | |
| address=(0, (5 + par) * _PSUM_BANK)) | |
| for c in range(n_chunks): | |
| nisa.nc_matmul(dst=ps[:, 0:tw], | |
| stationary=w_sb[qk][c][:, cols:cols + _P_MAX], | |
| moving=x_t[c][tt][:, 0:tw], accumulate=(c > 0)) | |
| nisa.tensor_scalar(dst=proj[par][:, 0:tw], data=ps[:, 0:tw], | |
| op0=nl.add, operand0=b_sb[qk][:, gq:gq + 1]) | |
| nisa.tensor_tensor(dst=acc[:, 0:tw], data1=proj[0][:, 0:tw], | |
| data2=tab[0][tt][:, 0:tw], op=nl.multiply) | |
| nisa.tensor_tensor(dst=tmp[:, 0:tw], data1=proj[1][:, 0:tw], | |
| data2=tab[2][tt][:, 0:tw], op=nl.multiply) | |
| nisa.tensor_tensor(dst=roped[0][:, 0:tw], data1=acc[:, 0:tw], | |
| data2=tmp[:, 0:tw], op=nl.add) | |
| nisa.tensor_tensor(dst=acc[:, 0:tw], data1=proj[1][:, 0:tw], | |
| data2=tab[1][tt][:, 0:tw], op=nl.multiply) | |
| nisa.tensor_tensor(dst=tmp[:, 0:tw], data1=proj[0][:, 0:tw], | |
| data2=tab[3][tt][:, 0:tw], op=nl.multiply) | |
| nisa.tensor_tensor(dst=roped[1][:, 0:tw], data1=acc[:, 0:tw], | |
| data2=tmp[:, 0:tw], op=nl.add) | |
| for b in range(quad): | |
| lh = j * quad + b | |
| r0 = b * half | |
| if qk == 1: | |
| for par2 in range(2): | |
| nisa.tensor_copy( | |
| dst=k_tiles[lh][tt][par2 * half:(par2 + 1) * half, 0:tw], | |
| src=roped[par2][r0:r0 + half, 0:tw]) | |
| else: | |
| for par2 in range(2): | |
| nisa.dma_copy( | |
| dst=q_priv[lh][0, par2 * half:(par2 + 1) * half, t0:t0 + tw], | |
| src=roped[par2][r0:r0 + half, 0:tw]) | |
| # ---- attention for this group's heads -> o_feat[g] (SBUF, feature-major) ---- | |
| allocator.set_current_address(phase2_base) | |
| o_sb = [] | |
| for _lh in range(local_heads): | |
| oc = (_lh * D) // _P_MAX | |
| rr = (_lh * D) % _P_MAX | |
| grp = [] | |
| for _g in range(n_grp): | |
| grp.append(o_feat[g][oc][rr:rr + D, _g * _P_MAX:(_g + 1) * _P_MAX]) | |
| o_sb.append(grp) | |
| attn_base = allocator.get_current_address() | |
| for lh in range(local_heads): | |
| _attention_cte( | |
| q_priv[lh], None, None, | |
| scale=scale, causal_mask=False, | |
| tp_q=False, tp_k=False, tp_out=True, | |
| cache_softmax=False, | |
| k_cache_sbuf=k_tiles[lh], | |
| v_cache_sbuf=v_tiles[lh], | |
| out_o_hbm=o_sb[lh], | |
| init_sbuf_addr=attn_base, | |
| seqlen_k_valid=n_valid, | |
| ) | |
| allocator.set_current_address(attn_base) | |
| # ---- ONE out-projection over ALL G*n_ochunks feature chunks, full bias once ---- | |
| allocator.set_current_address(img_base) | |
| if resident: | |
| wo_sb = r_wo | |
| bo_bc = r_bo | |
| else: | |
| wo_sb = allocator.alloc_sbuf_tensor(shape=(_P_MAX, H), dtype=nl.bfloat16, | |
| block_dim=[G * n_ochunks], num_free_tiles=[G * n_ochunks]) | |
| for oc in range(G * n_ochunks): | |
| nisa.dma_copy(dst=wo_sb[oc], src=wot_hbm[oc * _P_MAX:(oc + 1) * _P_MAX, 0:H]) | |
| bo_bc = allocator.alloc_sbuf_tensor(shape=(_P_MAX, H), dtype=nl.float32, align_to=4) | |
| nisa.dma_copy(dst=bo_bc, src=bo_hbm.ap(pattern=[[0, _P_MAX], [1, H]], offset=0)) | |
| out_sb = allocator.alloc_sbuf_tensor(shape=(_P_MAX, _PSUM_FMAX), dtype=nl.bfloat16, align_to=32) | |
| n_ob = (H + _PSUM_FMAX - 1) // _PSUM_FMAX | |
| for tb in range(n_grp): | |
| for ob in range(n_ob): | |
| ow = min(_PSUM_FMAX, H - ob * _PSUM_FMAX) | |
| ps_out = nl.ndarray((_P_MAX, _PSUM_FMAX), dtype=nl.float32, buffer=nl.psum, | |
| address=(0, 0)) | |
| for g in range(G): | |
| for oc in range(n_ochunks): | |
| nisa.nc_matmul( | |
| dst=ps_out[:, 0:ow], | |
| stationary=o_feat[g][oc][:, tb * _P_MAX:(tb + 1) * _P_MAX], | |
| moving=wo_sb[g * n_ochunks + oc][:, ob * _PSUM_FMAX:ob * _PSUM_FMAX + ow], | |
| accumulate=((g > 0) or (oc > 0)), | |
| ) | |
| nisa.tensor_tensor(dst=out_sb[:, 0:ow], data1=ps_out[:, 0:ow], | |
| data2=bo_bc[:, ob * _PSUM_FMAX:ob * _PSUM_FMAX + ow], op=nl.add) | |
| nisa.dma_copy( | |
| dst=result.ap(pattern=[[H, _P_MAX], [1, ow]], | |
| offset=rofs + tb * _P_MAX * H + ob * _PSUM_FMAX), | |
| src=out_sb[:, 0:ow]) | |
| return result | |
| _ATTN_L1 = wrap_nki_to_torch_hop(_attention_block_l1_kernel[1]) | |
| def attention_block_l1( | |
| h: torch.Tensor, | |
| wqk: torch.Tensor, | |
| bqk: torch.Tensor, | |
| tab: torch.Tensor, | |
| wvt: torch.Tensor, | |
| bv: torch.Tensor, | |
| wot: torch.Tensor, | |
| bo: torch.Tensor, | |
| scale: float, | |
| head_dim: int, | |
| n_valid: int, | |
| nimg: int = 1, | |
| resident: int = 0, | |
| ) -> torch.Tensor: | |
| """All head groups in one program. Returns ONE [1, nimg*N, H] bf16 result (full bias).""" | |
| for t in (h, wqk, bqk, tab, wvt, bv, wot, bo): | |
| if t is not None and t.requires_grad: | |
| raise RuntimeError("attention_block_l1 is inference-only; use torch.no_grad().") | |
| if h.shape[0] // nimg > 2048: | |
| raise NotImplementedError("attention_block_l1 supports N<=2048 only") | |
| if bqk.dim() == 3 and bqk.shape[1] != 128: | |
| bqk = _pack_qk_bias(bqk) | |
| return _ATTN_L1(h, wqk, bqk, tab, wvt, bv, wot, bo, scale, head_dim, n_valid, nimg, resident) | |