whispercpp 1.3.7 → 1.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -4
- data/ext/options.rb +1 -1
- data/ext/ruby_whisper.c +0 -1
- data/ext/ruby_whisper.h +7 -1
- data/ext/ruby_whisper_context.c +50 -1
- data/ext/ruby_whisper_log_settable.h +1 -2
- data/ext/ruby_whisper_params.c +9 -8
- data/ext/ruby_whisper_transcribe.cpp +0 -19
- data/ext/ruby_whisper_vad_context.c +30 -10
- data/ext/ruby_whisper_vad_context_detect.cpp +8 -9
- data/ext/ruby_whisper_vad_params.c +4 -4
- data/ext/ruby_whisper_vad_segment.c +2 -2
- data/ext/sources/CMakeLists.txt +2 -1
- data/ext/sources/cmake/parakeet.pc.in +2 -2
- data/ext/sources/cmake/whisper.pc.in +2 -2
- data/ext/sources/examples/cli/cli.cpp +9 -1
- data/ext/sources/examples/common-ggml.cpp +2 -0
- data/ext/sources/examples/vad-speech-segments/speech.cpp +3 -2
- data/ext/sources/ggml/CMakeLists.txt +3 -4
- data/ext/sources/ggml/include/ggml-cuda.h +0 -3
- data/ext/sources/ggml/include/ggml-sycl.h +8 -0
- data/ext/sources/ggml/include/ggml.h +3 -1
- data/ext/sources/ggml/src/CMakeLists.txt +8 -1
- data/ext/sources/ggml/src/ggml-backend-meta.cpp +7 -4
- data/ext/sources/ggml/src/ggml-common.h +13 -2
- data/ext/sources/ggml/src/ggml-cpu/CMakeLists.txt +1 -1
- data/ext/sources/ggml/src/ggml-cpu/amx/mmq.cpp +5 -6
- data/ext/sources/ggml/src/ggml-cpu/arch/arm/quants.c +78 -4
- data/ext/sources/ggml/src/ggml-cpu/arch/x86/quants.c +142 -4
- data/ext/sources/ggml/src/ggml-cpu/arch-fallback.h +7 -2
- data/ext/sources/ggml/src/ggml-cpu/ggml-cpu.c +14 -0
- data/ext/sources/ggml/src/ggml-cpu/llamafile/sgemm.cpp +26 -19
- data/ext/sources/ggml/src/ggml-cpu/ops.cpp +129 -46
- data/ext/sources/ggml/src/ggml-cpu/quants.c +51 -0
- data/ext/sources/ggml/src/ggml-cpu/quants.h +3 -0
- data/ext/sources/ggml/src/ggml-cpu/simd-gemm.h +1 -1
- data/ext/sources/ggml/src/ggml-cpu/simd-mappings.h +11 -0
- data/ext/sources/ggml/src/ggml-cpu/vec.cpp +2 -2
- data/ext/sources/ggml/src/ggml-cuda/binbcast.cu +90 -46
- data/ext/sources/ggml/src/ggml-cuda/col2im-1d.cu +81 -0
- data/ext/sources/ggml/src/ggml-cuda/col2im-1d.cuh +3 -0
- data/ext/sources/ggml/src/ggml-cuda/common.cuh +4 -0
- data/ext/sources/ggml/src/ggml-cuda/concat.cu +33 -21
- data/ext/sources/ggml/src/ggml-cuda/conv-transpose-1d.cu +14 -12
- data/ext/sources/ggml/src/ggml-cuda/convert.cu +86 -34
- data/ext/sources/ggml/src/ggml-cuda/cpy.cu +80 -29
- data/ext/sources/ggml/src/ggml-cuda/fattn-common.cuh +9 -5
- data/ext/sources/ggml/src/ggml-cuda/fattn-mma-f16.cuh +4 -0
- data/ext/sources/ggml/src/ggml-cuda/fattn-tile.cuh +9 -5
- data/ext/sources/ggml/src/ggml-cuda/fattn.cu +27 -21
- data/ext/sources/ggml/src/ggml-cuda/gated_delta_net.cu +40 -25
- data/ext/sources/ggml/src/ggml-cuda/gated_delta_net.cuh +10 -0
- data/ext/sources/ggml/src/ggml-cuda/getrows.cu +15 -12
- data/ext/sources/ggml/src/ggml-cuda/ggml-cuda.cu +718 -1248
- data/ext/sources/ggml/src/ggml-cuda/mmq.cu +7 -0
- data/ext/sources/ggml/src/ggml-cuda/mmvq.cu +77 -40
- data/ext/sources/ggml/src/ggml-cuda/out-prod.cu +55 -12
- data/ext/sources/ggml/src/ggml-cuda/set-rows.cu +64 -4
- data/ext/sources/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_16-ncols2_2.cu +1 -0
- data/ext/sources/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_32-ncols2_2.cu +1 -0
- data/ext/sources/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_2.cu +1 -0
- data/ext/sources/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_2.cu +1 -0
- data/ext/sources/ggml/src/ggml-cuda/topk-moe.cu +7 -1
- data/ext/sources/ggml/src/ggml-cuda/vendors/hip.h +1 -0
- data/ext/sources/ggml/src/ggml-cuda/vendors/musa.h +1 -0
- data/ext/sources/ggml/src/ggml-hexagon/CMakeLists.txt +0 -5
- data/ext/sources/ggml/src/ggml-hexagon/ggml-hexagon.cpp +1634 -1293
- data/ext/sources/ggml/src/ggml-hexagon/htp/CMakeLists.txt +11 -40
- data/ext/sources/ggml/src/ggml-hexagon/htp/cmake-toolchain.cmake +13 -15
- data/ext/sources/ggml/src/ggml-hexagon/htp/concat-ops.c +1 -1
- data/ext/sources/ggml/src/ggml-hexagon/htp/flash-attn-ops.c +1749 -399
- data/ext/sources/ggml/src/ggml-hexagon/htp/flash-attn-ops.h +303 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/hex-common.h +80 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/hex-dma.h +26 -23
- data/ext/sources/ggml/src/ggml-hexagon/htp/hex-profile.h +64 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/hex-utils.h +1 -83
- data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-fa-kernels.h +555 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-mm-kernels-tiled.h +1303 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-queue.c +9 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-queue.h +27 -4
- data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-utils.h +59 -37
- data/ext/sources/ggml/src/ggml-hexagon/htp/htp-ctx.h +11 -3
- data/ext/sources/ggml/src/ggml-hexagon/htp/htp-ops.h +52 -12
- data/ext/sources/ggml/src/ggml-hexagon/htp/htp-vtcm.h +19 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/htp_iface.idl +2 -1
- data/ext/sources/ggml/src/ggml-hexagon/htp/hvx-base.h +14 -30
- data/ext/sources/ggml/src/ggml-hexagon/htp/hvx-exp.h +39 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/hvx-fa-kernels.h +232 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/hvx-mm-kernels-flat.h +1511 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/hvx-mm-kernels-tiled.h +1200 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/hvx-sigmoid.h +39 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/main.c +127 -32
- data/ext/sources/ggml/src/ggml-hexagon/htp/matmul-ops.c +3023 -4425
- data/ext/sources/ggml/src/ggml-hexagon/htp/matmul-ops.h +650 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp/rope-ops.c +48 -13
- data/ext/sources/ggml/src/ggml-hexagon/htp/ssm-conv.c +10 -9
- data/ext/sources/ggml/src/ggml-hexagon/htp/worker-pool.c +15 -3
- data/ext/sources/ggml/src/ggml-hexagon/htp/worker-pool.h +8 -0
- data/ext/sources/ggml/src/ggml-hexagon/htp-opnode.h +168 -50
- data/ext/sources/ggml/src/ggml-hexagon/libggml-htp.inf +0 -4
- data/ext/sources/ggml/src/ggml-hip/CMakeLists.txt +5 -0
- data/ext/sources/ggml/src/ggml-metal/ggml-metal-device.cpp +69 -5
- data/ext/sources/ggml/src/ggml-metal/ggml-metal-device.h +4 -1
- data/ext/sources/ggml/src/ggml-metal/ggml-metal-device.m +27 -6
- data/ext/sources/ggml/src/ggml-metal/ggml-metal-impl.h +38 -0
- data/ext/sources/ggml/src/ggml-metal/ggml-metal-ops.cpp +132 -2
- data/ext/sources/ggml/src/ggml-metal/ggml-metal-ops.h +2 -0
- data/ext/sources/ggml/src/ggml-metal/ggml-metal.metal +345 -87
- data/ext/sources/ggml/src/ggml-opencl/CMakeLists.txt +13 -0
- data/ext/sources/ggml/src/ggml-opencl/fa_tune.h +92 -0
- data/ext/sources/ggml/src/ggml-opencl/ggml-opencl.cpp +4060 -357
- data/ext/sources/ggml/src/ggml-opencl/kernels/cvt.cl +198 -0
- data/ext/sources/ggml/src/ggml-opencl/kernels/flash_attn_f16.cl +81 -41
- data/ext/sources/ggml/src/ggml-opencl/kernels/flash_attn_f32.cl +88 -39
- data/ext/sources/ggml/src/ggml-opencl/kernels/flash_attn_f32_f16.cl +1995 -96
- data/ext/sources/ggml/src/ggml-opencl/kernels/flash_attn_f32_q4_0.cl +1615 -0
- data/ext/sources/ggml/src/ggml-opencl/kernels/flash_attn_f32_q8_0.cl +1486 -0
- data/ext/sources/ggml/src/ggml-opencl/kernels/flash_attn_pre_f16.cl +156 -0
- data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_mxfp4_f32_ns.cl +74 -6
- data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q4_0_f32_ns.cl +74 -6
- data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q4_1_f32_ns.cl +74 -6
- data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q4_k_f32_ns.cl +71 -6
- data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q5_0_f32_ns.cl +74 -6
- data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q5_1_f32_ns.cl +74 -6
- data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q5_k_f32_ns.cl +74 -6
- data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q6_k_f32_ns.cl +74 -6
- data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_noshuffle_q1_0_f32.cl +94 -0
- data/ext/sources/ggml/src/ggml-opencl/kernels/gemv_noshuffle_q1_0_f32.cl +121 -0
- data/ext/sources/ggml/src/ggml-opencl/kernels/gemv_noshuffle_q8_0_f32.cl +1 -1
- data/ext/sources/ggml/src/ggml-opencl/kernels/mul_mm_q1_0_f32_l4_lm.cl +156 -0
- data/ext/sources/ggml/src/ggml-opencl/kernels/mul_mv_f16_f32_l4.cl +1149 -0
- data/ext/sources/ggml/src/ggml-opencl/kernels/mul_mv_q1_0_f32.cl +141 -0
- data/ext/sources/ggml/src/ggml-opencl/kernels/mul_mv_q1_0_f32_flat.cl +190 -0
- data/ext/sources/ggml/src/ggml-opencl/kernels/norm.cl +5 -2
- data/ext/sources/ggml/src/ggml-opencl/kernels/set_rows.cl +500 -0
- data/ext/sources/ggml/src/ggml-opencl/libdl.h +79 -0
- data/ext/sources/ggml/src/ggml-openvino/.clang-format +0 -5
- data/ext/sources/ggml/src/ggml-openvino/CMakeLists.txt +2 -4
- data/ext/sources/ggml/src/ggml-openvino/ggml-decoder.cpp +733 -130
- data/ext/sources/ggml/src/ggml-openvino/ggml-decoder.h +76 -23
- data/ext/sources/ggml/src/ggml-openvino/ggml-openvino-extra.cpp +57 -3
- data/ext/sources/ggml/src/ggml-openvino/ggml-openvino-extra.h +29 -8
- data/ext/sources/ggml/src/ggml-openvino/ggml-openvino.cpp +307 -59
- data/ext/sources/ggml/src/ggml-openvino/ggml-quants.cpp +66 -0
- data/ext/sources/ggml/src/ggml-openvino/ggml-quants.h +10 -4
- data/ext/sources/ggml/src/ggml-openvino/openvino/decoder.h +56 -16
- data/ext/sources/ggml/src/ggml-openvino/openvino/frontend.h +1 -1
- data/ext/sources/ggml/src/ggml-openvino/openvino/input_model.h +4 -4
- data/ext/sources/ggml/src/ggml-openvino/openvino/node_context.h +94 -37
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/add_id.cpp +76 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/argsort.cpp +47 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/clamp.cpp +33 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/concat.cpp +48 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/cont.cpp +8 -16
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/cpy.cpp +14 -1
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/div.cpp +146 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/flash_attn_ext.cpp +108 -21
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/gated_delta_net.cpp +282 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/gated_delta_net.hpp +65 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/get_rows.cpp +2 -9
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/glu_geglu.cpp +21 -7
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/glu_swiglu.cpp +41 -8
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/im2col.cpp +120 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/l2_norm.cpp +44 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/mul_mat_id.cpp +226 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/mulmat.cpp +19 -9
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/norm.cpp +58 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/pad.cpp +95 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/permute.cpp +58 -13
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/repeat.cpp +74 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/reshape.cpp +13 -6
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/rms_norm.cpp +1 -1
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/rope.cpp +134 -38
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/set_rows.cpp +3 -3
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/softmax.cpp +126 -49
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/ssm_conv.cpp +59 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/sum_rows.cpp +27 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/transpose.cpp +32 -1
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/unary_silu.cpp +1 -1
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/unary_softplus.cpp +38 -0
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/view.cpp +90 -25
- data/ext/sources/ggml/src/ggml-openvino/openvino/op_table.cpp +41 -23
- data/ext/sources/ggml/src/ggml-openvino/openvino/op_table.h +18 -5
- data/ext/sources/ggml/src/ggml-openvino/openvino/pass/mark_decompression_convert_constant_folding.h +1 -1
- data/ext/sources/ggml/src/ggml-openvino/openvino/translate_session.cpp +43 -40
- data/ext/sources/ggml/src/ggml-openvino/openvino/translate_session.h +5 -4
- data/ext/sources/ggml/src/ggml-openvino/openvino/utils.cpp +548 -3
- data/ext/sources/ggml/src/ggml-openvino/openvino/utils.h +28 -26
- data/ext/sources/ggml/src/ggml-openvino/utils.cpp +383 -94
- data/ext/sources/ggml/src/ggml-openvino/utils.h +11 -8
- data/ext/sources/ggml/src/ggml-quants.c +76 -0
- data/ext/sources/ggml/src/ggml-quants.h +3 -0
- data/ext/sources/ggml/src/ggml-sycl/CMakeLists.txt +5 -5
- data/ext/sources/ggml/src/ggml-sycl/backend.hpp +2 -0
- data/ext/sources/ggml/src/ggml-sycl/binbcast.cpp +12 -0
- data/ext/sources/ggml/src/ggml-sycl/col2im-1d.cpp +102 -0
- data/ext/sources/ggml/src/ggml-sycl/col2im-1d.hpp +8 -0
- data/ext/sources/ggml/src/ggml-sycl/common.cpp +6 -8
- data/ext/sources/ggml/src/ggml-sycl/common.hpp +19 -2
- data/ext/sources/ggml/src/ggml-sycl/concat.cpp +21 -1
- data/ext/sources/ggml/src/ggml-sycl/conv2d-dw.cpp +158 -0
- data/ext/sources/ggml/src/ggml-sycl/conv2d-dw.hpp +10 -0
- data/ext/sources/ggml/src/ggml-sycl/conv2d-transpose.cpp +125 -0
- data/ext/sources/ggml/src/ggml-sycl/conv2d-transpose.hpp +10 -0
- data/ext/sources/ggml/src/ggml-sycl/conv2d.cpp +150 -0
- data/ext/sources/ggml/src/ggml-sycl/conv2d.hpp +10 -0
- data/ext/sources/ggml/src/ggml-sycl/conv3d.cpp +224 -0
- data/ext/sources/ggml/src/ggml-sycl/conv3d.hpp +8 -0
- data/ext/sources/ggml/src/ggml-sycl/convert.cpp +6 -0
- data/ext/sources/ggml/src/ggml-sycl/cpy.cpp +706 -0
- data/ext/sources/ggml/src/ggml-sycl/cpy.hpp +281 -0
- data/ext/sources/ggml/src/ggml-sycl/cross_entropy_loss.cpp +255 -0
- data/ext/sources/ggml/src/ggml-sycl/cross_entropy_loss.hpp +7 -0
- data/ext/sources/ggml/src/ggml-sycl/dequantize.hpp +15 -0
- data/ext/sources/ggml/src/ggml-sycl/dmmv.cpp +492 -319
- data/ext/sources/ggml/src/ggml-sycl/dpct/helper.hpp +15 -7
- data/ext/sources/ggml/src/ggml-sycl/element_wise.cpp +215 -115
- data/ext/sources/ggml/src/ggml-sycl/element_wise.hpp +2 -0
- data/ext/sources/ggml/src/ggml-sycl/ggml-sycl.cpp +1006 -336
- data/ext/sources/ggml/src/ggml-sycl/mmvq.cpp +252 -67
- data/ext/sources/ggml/src/ggml-sycl/mmvq.hpp +17 -0
- data/ext/sources/ggml/src/ggml-sycl/norm.cpp +103 -49
- data/ext/sources/ggml/src/ggml-sycl/outprod.cpp +45 -9
- data/ext/sources/ggml/src/ggml-sycl/pool.cpp +185 -0
- data/ext/sources/ggml/src/ggml-sycl/pool.hpp +22 -0
- data/ext/sources/ggml/src/ggml-sycl/presets.hpp +3 -1
- data/ext/sources/ggml/src/ggml-sycl/set_rows.cpp +10 -2
- data/ext/sources/ggml/src/ggml-sycl/softmax.cpp +9 -10
- data/ext/sources/ggml/src/ggml-sycl/vecdotq.hpp +35 -0
- data/ext/sources/ggml/src/ggml-vulkan/CMakeLists.txt +5 -0
- data/ext/sources/ggml/src/ggml-vulkan/ggml-vulkan.cpp +833 -215
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/col2im_1d.comp +61 -0
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/conv2d_mm.comp +1 -1
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/conv3d_mm.comp +431 -0
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/diag.comp +3 -3
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/flash_attn.comp +1 -0
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/flash_attn_cm1.comp +1 -0
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/generic_unary_head.glsl +21 -19
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/get_rows_back.comp +25 -0
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/glu_head.glsl +23 -4
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/glu_main.glsl +14 -18
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/l2_norm.comp +4 -7
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vecq.comp +21 -24
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm.comp +31 -23
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_cm2.comp +6 -5
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_funcs.glsl +84 -67
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/norm.comp +10 -10
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/repeat_back.comp +3 -3
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/roll.comp +3 -3
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/tri.comp +3 -3
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/unary.comp +168 -0
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp +121 -74
- data/ext/sources/ggml/src/ggml-webgpu/ggml-webgpu-shader-lib.hpp +26 -19
- data/ext/sources/ggml/src/ggml-webgpu/ggml-webgpu.cpp +31 -36
- data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/common_decls.tmpl +16 -2
- data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/flash_attn_vec_split.wgsl +7 -7
- data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/get_rows.wgsl +21 -0
- data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/mul_mat_decls.tmpl +439 -320
- data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/mul_mat_id_vec.wgsl +2 -2
- data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/mul_mat_vec.wgsl +45 -39
- data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/mul_mat_vec_acc.tmpl +586 -465
- data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/mul_mat_vec_q_acc.tmpl +63 -69
- data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/quantize_q8.wgsl +14 -9
- data/ext/sources/ggml/src/ggml.c +36 -14
- data/ext/sources/include/whisper.h +21 -0
- data/ext/sources/src/whisper.cpp +164 -14
- data/lib/whisper/log_settable.rb +5 -8
- data/lib/whisper/model/uri.rb +0 -7
- data/sig/whisper.rbs +6 -0
- data/test/test_vad.rb +9 -0
- data/test/test_vad_context.rb +2 -2
- data/whispercpp.gemspec +1 -1
- metadata +62 -37
- data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-flash-attn-ops.c +0 -1878
- data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-matmul-ops.c +0 -2066
- data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-ops.c +0 -6
- data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-ops.h +0 -88
- data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-profile.h +0 -34
- data/ext/sources/ggml/src/ggml-hexagon/htp/vtcm-utils.h +0 -16
- data/ext/sources/ggml/src/ggml-openvino/openvino/op/unary_gelu.cpp +0 -25
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/abs.comp +0 -21
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/ceil.comp +0 -22
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/clamp.comp +0 -17
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/cos.comp +0 -17
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/elu.comp +0 -27
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/exp.comp +0 -20
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/floor.comp +0 -22
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/gelu.comp +0 -25
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/gelu_erf.comp +0 -39
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/gelu_quick.comp +0 -23
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/hardsigmoid.comp +0 -22
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/hardswish.comp +0 -22
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/leaky_relu.comp +0 -22
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/neg.comp +0 -20
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/relu.comp +0 -21
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/round.comp +0 -29
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/sgn.comp +0 -21
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/sigmoid.comp +0 -20
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/silu.comp +0 -22
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/sin.comp +0 -17
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/softplus.comp +0 -23
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/sqrt.comp +0 -17
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/square.comp +0 -17
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/step.comp +0 -22
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/tanh.comp +0 -20
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/trunc.comp +0 -22
- data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/xielu.comp +0 -35
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
|
2
2
|
|
|
3
|
+
#ifdef cl_intel_subgroups
|
|
4
|
+
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
|
|
5
|
+
#else
|
|
6
|
+
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
|
|
7
|
+
#endif
|
|
8
|
+
|
|
9
|
+
#ifdef cl_qcom_reqd_sub_group_size
|
|
10
|
+
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
|
11
|
+
#define REQD_SUBGROUP_SIZE_64 __attribute__((qcom_reqd_sub_group_size("half")))
|
|
12
|
+
#else
|
|
13
|
+
#define REQD_SUBGROUP_SIZE_64
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
// subgroup size for q1 kernels
|
|
17
|
+
#ifndef FA_SG
|
|
18
|
+
#define FA_SG 64
|
|
19
|
+
#endif
|
|
20
|
+
#ifdef cl_intel_required_subgroup_size
|
|
21
|
+
#pragma OPENCL EXTENSION cl_intel_required_subgroup_size : enable
|
|
22
|
+
#define REQD_FA_SG __attribute__((intel_reqd_sub_group_size(FA_SG)))
|
|
23
|
+
#else
|
|
24
|
+
#define REQD_FA_SG
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
#ifdef cl_khr_subgroup_shuffle
|
|
28
|
+
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
|
|
29
|
+
#define HAS_SUBGROUP_SHUFFLE 1
|
|
30
|
+
#elif defined(cl_qcom_subgroup_shuffle)
|
|
31
|
+
#pragma OPENCL EXTENSION cl_qcom_subgroup_shuffle : enable
|
|
32
|
+
#define HAS_SUBGROUP_SHUFFLE 1
|
|
33
|
+
#endif
|
|
34
|
+
|
|
3
35
|
#define ACC_TYPE float
|
|
4
36
|
#define ACC_TYPE4 float4
|
|
5
37
|
#define Q_DATA_TYPE4 float4
|
|
@@ -12,8 +44,37 @@
|
|
|
12
44
|
|
|
13
45
|
#define DK_VEC (DK/4)
|
|
14
46
|
#define DV_VEC (DV/4)
|
|
47
|
+
|
|
48
|
+
#ifndef FA_PARTIAL_FLOATS
|
|
49
|
+
#define FA_PARTIAL_FLOATS (2 + DV)
|
|
50
|
+
#endif
|
|
51
|
+
#define Q1_WG_SIZE FA_SG
|
|
52
|
+
|
|
53
|
+
// The kernels are built with -cl-finite-math-only. On some older Adreno GPUs,
|
|
54
|
+
// infinite operand can cause undefined behavior and miscompilation for exp.
|
|
55
|
+
// Therefore, a large negative value is used instead.
|
|
56
|
+
#define FA_M_INIT (-3.0e38f)
|
|
57
|
+
|
|
58
|
+
// Drop full unroll at DK>=192 — Adreno compiler host-memory budget.
|
|
59
|
+
#if DK >= 192
|
|
60
|
+
#define FA_UNROLL
|
|
61
|
+
#else
|
|
62
|
+
#define FA_UNROLL _Pragma("unroll")
|
|
63
|
+
#endif
|
|
64
|
+
|
|
65
|
+
// N_SPLIT>1 splits DK/DV across threads to cut per-thread register use.
|
|
66
|
+
#ifndef N_SPLIT
|
|
67
|
+
#define N_SPLIT 1
|
|
68
|
+
#endif
|
|
69
|
+
|
|
70
|
+
#define SPLIT_DK_VEC (DK_VEC / N_SPLIT)
|
|
71
|
+
#define SPLIT_DV_VEC (DV_VEC / N_SPLIT)
|
|
72
|
+
|
|
73
|
+
#if N_SPLIT > 1
|
|
74
|
+
#define WG_SIZE (BLOCK_M * N_SPLIT)
|
|
75
|
+
#else
|
|
15
76
|
#define WG_SIZE (BLOCK_M)
|
|
16
|
-
#
|
|
77
|
+
#endif
|
|
17
78
|
|
|
18
79
|
inline float get_alibi_slope(
|
|
19
80
|
const float max_bias, const uint h, const uint n_head_log2, const float m0, const float m1
|
|
@@ -26,9 +87,20 @@ inline float get_alibi_slope(
|
|
|
26
87
|
|
|
27
88
|
return pow(base, exph);
|
|
28
89
|
}
|
|
29
|
-
|
|
90
|
+
|
|
91
|
+
// Adreno compiler crashes when attempting to compile the entire program for DK=512,
|
|
92
|
+
// FA_DECODE_ONLY allows bypass the encoding kernel.
|
|
93
|
+
#if !defined(FA_DECODE_ONLY) && !defined(FA_MQ_ONLY)
|
|
94
|
+
#ifndef FA_TILE_NAME
|
|
95
|
+
#define FA_TILE_NAME flash_attn_f32_f16
|
|
96
|
+
#endif
|
|
97
|
+
__kernel void FA_TILE_NAME(
|
|
30
98
|
const global void * q_void, ulong q_offset,
|
|
99
|
+
#ifdef FA_K_IMG
|
|
100
|
+
__read_only image1d_buffer_t k_img, ulong k_offset_unused,
|
|
101
|
+
#else
|
|
31
102
|
const global void * k_void, ulong k_offset,
|
|
103
|
+
#endif
|
|
32
104
|
const global void * v_void, ulong v_offset,
|
|
33
105
|
global void * o_void, ulong o_offset,
|
|
34
106
|
const float scale,
|
|
@@ -54,48 +126,83 @@ __kernel void flash_attn_f32_f16(
|
|
|
54
126
|
const int mask_ne2,
|
|
55
127
|
const int mask_ne3,
|
|
56
128
|
const global void* sinks_void,
|
|
57
|
-
const ulong sinks_offset
|
|
129
|
+
const ulong sinks_offset,
|
|
130
|
+
const global void * k_pad_void,
|
|
131
|
+
const global void * v_pad_void,
|
|
132
|
+
const global void * mask_pad_void,
|
|
133
|
+
const global char * blk,
|
|
134
|
+
const int n_kv_blocks,
|
|
135
|
+
const ulong mask_pad_nb1,
|
|
136
|
+
const ulong mask_pad_nb2,
|
|
137
|
+
const ulong mask_pad_nb3
|
|
58
138
|
) {
|
|
59
139
|
const int tid = get_local_id(0);
|
|
60
140
|
const int block_q_idx = get_group_id(0);
|
|
61
141
|
const int head_batch_idx = get_global_id(1);
|
|
62
142
|
|
|
63
|
-
|
|
143
|
+
#if N_SPLIT > 1
|
|
144
|
+
const int q_lane = tid / N_SPLIT;
|
|
145
|
+
const int split_idx = tid % N_SPLIT;
|
|
146
|
+
#else
|
|
147
|
+
const int q_lane = tid;
|
|
148
|
+
const int split_idx = 0;
|
|
149
|
+
#endif
|
|
150
|
+
|
|
151
|
+
const int my_query_row = block_q_idx * BLOCK_M + q_lane;
|
|
152
|
+
const int query_valid = my_query_row < n_q;
|
|
64
153
|
|
|
65
154
|
const int batch_idx = head_batch_idx / n_head;
|
|
66
155
|
const int head_idx = head_batch_idx % n_head;
|
|
67
156
|
|
|
68
157
|
const int gqa_ratio = n_head / n_head_kv;
|
|
69
158
|
const int head_kv_idx = head_idx / gqa_ratio;
|
|
159
|
+
const int mask_head_idx = mask_void != NULL ? head_idx % mask_ne2 : 0;
|
|
160
|
+
const int mask_batch_idx = mask_void != NULL ? batch_idx % mask_ne3 : 0;
|
|
70
161
|
|
|
71
162
|
const global char* q_base = (const global char*)q_void + q_offset;
|
|
163
|
+
#ifndef FA_K_IMG
|
|
72
164
|
const global char* k_base = (const global char*)k_void + k_offset;
|
|
165
|
+
#endif
|
|
73
166
|
const global char* v_base = (const global char*)v_void + v_offset;
|
|
74
167
|
global char* o_base = (global char*)o_void + o_offset;
|
|
75
168
|
|
|
76
169
|
const global char* mask_base = NULL;
|
|
77
170
|
if (mask_void != NULL) {
|
|
78
|
-
const int mask_head_idx = head_idx % mask_ne2;
|
|
79
|
-
const int mask_batch_idx = batch_idx % mask_ne3;
|
|
80
171
|
mask_base = (const global char*)mask_void + mask_offset + mask_batch_idx * mask_nb3 + mask_head_idx * mask_nb2;
|
|
81
172
|
}
|
|
173
|
+
const global char* mask_pad_base = NULL;
|
|
174
|
+
if (mask_pad_void != NULL) {
|
|
175
|
+
mask_pad_base = (const global char*)mask_pad_void + mask_batch_idx * mask_pad_nb3 + mask_head_idx * mask_pad_nb2;
|
|
176
|
+
}
|
|
177
|
+
const global char* blk_base = NULL;
|
|
178
|
+
if (blk != NULL) {
|
|
179
|
+
const int n_q_blocks = (n_q + BLOCK_M - 1) / BLOCK_M;
|
|
180
|
+
blk_base = blk + (((mask_batch_idx * mask_ne2) + mask_head_idx) * n_q_blocks + block_q_idx) * n_kv_blocks;
|
|
181
|
+
}
|
|
82
182
|
|
|
83
|
-
ACC_TYPE4 q_priv[
|
|
84
|
-
|
|
183
|
+
ACC_TYPE4 q_priv[SPLIT_DK_VEC];
|
|
184
|
+
const int dk_off = split_idx * SPLIT_DK_VEC;
|
|
185
|
+
if (query_valid) {
|
|
85
186
|
const ulong q_row_offset = batch_idx * q_nb3 + head_idx * q_nb2 + my_query_row * q_nb1;
|
|
86
187
|
const global Q_DATA_TYPE4* q_ptr = (const global Q_DATA_TYPE4*)(q_base + q_row_offset);
|
|
87
|
-
|
|
88
|
-
for (int i = 0; i <
|
|
89
|
-
q_priv[i] = CONVERT_Q_ACC4(q_ptr[i]);
|
|
188
|
+
FA_UNROLL
|
|
189
|
+
for (int i = 0; i < SPLIT_DK_VEC; ++i) {
|
|
190
|
+
q_priv[i] = CONVERT_Q_ACC4(q_ptr[dk_off + i]);
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
FA_UNROLL
|
|
194
|
+
for (int i = 0; i < SPLIT_DK_VEC; ++i) {
|
|
195
|
+
q_priv[i] = (ACC_TYPE4)(0.0f);
|
|
90
196
|
}
|
|
91
197
|
}
|
|
92
198
|
|
|
93
|
-
ACC_TYPE4 o_acc[
|
|
94
|
-
|
|
95
|
-
for (int i = 0; i <
|
|
199
|
+
ACC_TYPE4 o_acc[SPLIT_DV_VEC];
|
|
200
|
+
FA_UNROLL
|
|
201
|
+
for (int i = 0; i < SPLIT_DV_VEC; ++i) {
|
|
96
202
|
o_acc[i] = (ACC_TYPE4)(0.0f);
|
|
97
203
|
}
|
|
98
|
-
|
|
204
|
+
|
|
205
|
+
ACC_TYPE m_i = FA_M_INIT;
|
|
99
206
|
ACC_TYPE l_i = 0.0f;
|
|
100
207
|
|
|
101
208
|
float slope = get_alibi_slope(max_bias, head_idx, n_head_log2, m0, m1);
|
|
@@ -103,86 +210,388 @@ __kernel void flash_attn_f32_f16(
|
|
|
103
210
|
__local KV_DATA_TYPE4 l_k[BLOCK_N][DK_VEC];
|
|
104
211
|
__local KV_DATA_TYPE4 l_v[BLOCK_N][DV_VEC];
|
|
105
212
|
|
|
213
|
+
#if N_SPLIT > 1 && !defined(HAS_SUBGROUP_SHUFFLE)
|
|
214
|
+
__local ACC_TYPE local_partial[BLOCK_N][WG_SIZE];
|
|
215
|
+
__local ACC_TYPE local_p[BLOCK_M][BLOCK_N];
|
|
216
|
+
__local ACC_TYPE local_softmax_scale[BLOCK_M];
|
|
217
|
+
__local ACC_TYPE local_l_inv[BLOCK_M];
|
|
218
|
+
#endif
|
|
219
|
+
|
|
106
220
|
for (int k_start = 0; k_start < n_kv; k_start += BLOCK_N) {
|
|
221
|
+
char blk_cur = 1;
|
|
222
|
+
if (blk_base != NULL) {
|
|
223
|
+
blk_cur = blk_base[k_start / BLOCK_N];
|
|
224
|
+
if (blk_cur == 0) continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const int use_kv_pad = k_pad_void != NULL && k_start + BLOCK_N > n_kv;
|
|
228
|
+
const int k_tile_start = use_kv_pad ? 0 : k_start;
|
|
229
|
+
const ulong k_tile_nb2 = use_kv_pad ? (ulong) BLOCK_N * k_nb1 : k_nb2;
|
|
230
|
+
const ulong k_tile_nb3 = use_kv_pad ? (ulong) n_head_kv * k_tile_nb2 : k_nb3;
|
|
231
|
+
const ulong v_tile_nb2 = use_kv_pad ? (ulong) BLOCK_N * v_nb1 : v_nb2;
|
|
232
|
+
const ulong v_tile_nb3 = use_kv_pad ? (ulong) n_head_kv * v_tile_nb2 : v_nb3;
|
|
233
|
+
#ifdef FA_K_IMG
|
|
234
|
+
// K via texture cache for the bulk (aligned) tiles; the ragged last
|
|
235
|
+
// tile (use_kv_pad) still reads the f32-strided pad buffer from global.
|
|
236
|
+
const global char* k_tile_base = use_kv_pad ? (const global char*) k_pad_void : (const global char*) 0;
|
|
237
|
+
const int k_pitch_px_row = (int)(k_nb1 >> 3);
|
|
238
|
+
const int k_pitch_px_head = (int)(k_nb2 >> 3);
|
|
239
|
+
const int k_pitch_px_batch = (int)(k_nb3 >> 3);
|
|
240
|
+
#else
|
|
241
|
+
const global char* k_tile_base = use_kv_pad ? (const global char*) k_pad_void : k_base;
|
|
242
|
+
#endif
|
|
243
|
+
const global char* v_tile_base = use_kv_pad ? (const global char*) v_pad_void : v_base;
|
|
244
|
+
|
|
107
245
|
for (int i = tid; i < BLOCK_N * DK_VEC; i += WG_SIZE) {
|
|
108
246
|
const int row = i / DK_VEC;
|
|
109
247
|
const int col = i % DK_VEC;
|
|
110
|
-
const int k_row_idx =
|
|
111
|
-
if (k_row_idx < n_kv) {
|
|
112
|
-
|
|
113
|
-
|
|
248
|
+
const int k_row_idx = k_tile_start + row;
|
|
249
|
+
if (use_kv_pad || k_row_idx < n_kv) {
|
|
250
|
+
#ifdef FA_K_IMG
|
|
251
|
+
if (use_kv_pad) {
|
|
252
|
+
const ulong k_row_offset = batch_idx * k_tile_nb3 + head_kv_idx * k_tile_nb2 + k_row_idx * k_nb1;
|
|
253
|
+
l_k[row][col] = ((__global KV_DATA_TYPE4*)(k_tile_base + k_row_offset))[col];
|
|
254
|
+
} else {
|
|
255
|
+
const int k_row_px = batch_idx * k_pitch_px_batch + head_kv_idx * k_pitch_px_head + k_row_idx * k_pitch_px_row;
|
|
256
|
+
l_k[row][col] = read_imageh(k_img, k_row_px + col);
|
|
257
|
+
}
|
|
258
|
+
#else
|
|
259
|
+
const ulong k_row_offset = batch_idx * k_tile_nb3 + head_kv_idx * k_tile_nb2 + k_row_idx * k_nb1;
|
|
260
|
+
l_k[row][col] = ((__global KV_DATA_TYPE4*)(k_tile_base + k_row_offset))[col];
|
|
261
|
+
#endif
|
|
262
|
+
} else {
|
|
263
|
+
l_k[row][col] = (KV_DATA_TYPE4)(0.0h);
|
|
114
264
|
}
|
|
115
265
|
}
|
|
116
266
|
for (int i = tid; i < BLOCK_N * DV_VEC; i += WG_SIZE) {
|
|
117
267
|
const int row = i / DV_VEC;
|
|
118
268
|
const int col = i % DV_VEC;
|
|
119
|
-
const int v_row_idx =
|
|
120
|
-
if (v_row_idx < n_kv) {
|
|
121
|
-
const ulong v_row_offset = batch_idx *
|
|
122
|
-
l_v[row][col] = ((__global KV_DATA_TYPE4*)(
|
|
269
|
+
const int v_row_idx = k_tile_start + row;
|
|
270
|
+
if (use_kv_pad || v_row_idx < n_kv) {
|
|
271
|
+
const ulong v_row_offset = batch_idx * v_tile_nb3 + head_kv_idx * v_tile_nb2 + v_row_idx * v_nb1;
|
|
272
|
+
l_v[row][col] = ((__global KV_DATA_TYPE4*)(v_tile_base + v_row_offset))[col];
|
|
273
|
+
} else {
|
|
274
|
+
l_v[row][col] = (KV_DATA_TYPE4)(0.0h);
|
|
123
275
|
}
|
|
124
276
|
}
|
|
125
277
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
126
278
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
279
|
+
#if N_SPLIT > 1 && defined(HAS_SUBGROUP_SHUFFLE)
|
|
280
|
+
{
|
|
281
|
+
const int dv_off = split_idx * SPLIT_DV_VEC;
|
|
282
|
+
for (int j = 0; j < BLOCK_N; j += 2) {
|
|
283
|
+
const int k_row0 = k_start + j;
|
|
284
|
+
const int k_row1 = k_start + j + 1;
|
|
130
285
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
286
|
+
ACC_TYPE partial0 = 0.0f;
|
|
287
|
+
ACC_TYPE partial1 = 0.0f;
|
|
288
|
+
FA_UNROLL
|
|
289
|
+
for (int k = 0; k < SPLIT_DK_VEC; k++) {
|
|
290
|
+
const ACC_TYPE4 qk = q_priv[k];
|
|
291
|
+
ACC_TYPE4 dot0 = qk * CONVERT_KV_ACC4(l_k[j ][dk_off + k]);
|
|
292
|
+
ACC_TYPE4 dot1 = qk * CONVERT_KV_ACC4(l_k[j+1][dk_off + k]);
|
|
293
|
+
partial0 += dot0.s0 + dot0.s1 + dot0.s2 + dot0.s3;
|
|
294
|
+
partial1 += dot1.s0 + dot1.s1 + dot1.s2 + dot1.s3;
|
|
295
|
+
}
|
|
134
296
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
297
|
+
FA_UNROLL
|
|
298
|
+
for (int step = 1; step < N_SPLIT; step <<= 1) {
|
|
299
|
+
partial0 += sub_group_shuffle_xor(partial0, step);
|
|
300
|
+
partial1 += sub_group_shuffle_xor(partial1, step);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
ACC_TYPE score0 = partial0 * scale;
|
|
304
|
+
ACC_TYPE score1 = partial1 * scale;
|
|
305
|
+
|
|
306
|
+
if (!query_valid) { score0 = FA_M_INIT; score1 = FA_M_INIT; }
|
|
307
|
+
if (is_causal) {
|
|
308
|
+
if (k_row0 > (n_kv - n_q + my_query_row)) score0 = FA_M_INIT;
|
|
309
|
+
if (k_row1 > (n_kv - n_q + my_query_row)) score1 = FA_M_INIT;
|
|
310
|
+
}
|
|
311
|
+
if (k_row0 >= n_kv) score0 = FA_M_INIT;
|
|
312
|
+
if (k_row1 >= n_kv) score1 = FA_M_INIT;
|
|
144
313
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
314
|
+
if (query_valid && mask_base != NULL && blk_cur != 2) {
|
|
315
|
+
if (use_kv_pad && mask_pad_base != NULL) {
|
|
316
|
+
const global MASK_DATA_TYPE* mask_ptr =
|
|
317
|
+
(const global MASK_DATA_TYPE*)(mask_pad_base + my_query_row * mask_pad_nb1);
|
|
318
|
+
score0 += slope * (ACC_TYPE)mask_ptr[j];
|
|
319
|
+
score1 += slope * (ACC_TYPE)mask_ptr[j + 1];
|
|
320
|
+
} else {
|
|
321
|
+
const global MASK_DATA_TYPE* mask_ptr =
|
|
322
|
+
(const global MASK_DATA_TYPE*)(mask_base + my_query_row * mask_nb1);
|
|
323
|
+
if (k_row0 < n_kv) score0 += slope * (ACC_TYPE)mask_ptr[k_row0];
|
|
324
|
+
if (k_row1 < n_kv) score1 += slope * (ACC_TYPE)mask_ptr[k_row1];
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (logit_softcap > 0.0f) {
|
|
329
|
+
score0 = logit_softcap * tanh(score0 / logit_softcap);
|
|
330
|
+
score1 = logit_softcap * tanh(score1 / logit_softcap);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const ACC_TYPE m_new = max(m_i, max(score0, score1));
|
|
334
|
+
// Whole tile masked (m_new == FA_M_INIT): force the exp() args
|
|
335
|
+
// far negative so the tile contributes 0, not exp(0)=1.
|
|
336
|
+
const ACC_TYPE m_exp = (m_new == FA_M_INIT) ? 0.0f : m_new;
|
|
337
|
+
const ACC_TYPE sp = native_exp(m_i - m_exp);
|
|
338
|
+
const ACC_TYPE p0 = native_exp(score0 - m_exp);
|
|
339
|
+
const ACC_TYPE p1 = native_exp(score1 - m_exp);
|
|
340
|
+
|
|
341
|
+
FA_UNROLL
|
|
342
|
+
for (int i = 0; i < SPLIT_DV_VEC; ++i) {
|
|
343
|
+
o_acc[i] = o_acc[i] * sp
|
|
344
|
+
+ p0 * CONVERT_KV_ACC4(l_v[j ][dv_off + i])
|
|
345
|
+
+ p1 * CONVERT_KV_ACC4(l_v[j+1][dv_off + i]);
|
|
346
|
+
}
|
|
347
|
+
l_i = l_i * sp + p0 + p1;
|
|
348
|
+
m_i = m_new;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
#elif N_SPLIT > 1
|
|
352
|
+
// N_SPLIT>1 fallback (no shuffle): 3-phase local-memory reduction.
|
|
353
|
+
// Phase 1 — partial dots for all BLOCK_N tokens.
|
|
354
|
+
for (int j = 0; j < BLOCK_N; ++j) {
|
|
355
|
+
ACC_TYPE4 dot_acc = (ACC_TYPE4)(0.0f);
|
|
356
|
+
FA_UNROLL
|
|
357
|
+
for (int k = 0; k < SPLIT_DK_VEC; k++) {
|
|
358
|
+
dot_acc = mad(q_priv[k], CONVERT_KV_ACC4(l_k[j][dk_off + k]), dot_acc);
|
|
148
359
|
}
|
|
360
|
+
local_partial[j][tid] =
|
|
361
|
+
dot_acc.s0 + dot_acc.s1 + dot_acc.s2 + dot_acc.s3;
|
|
362
|
+
}
|
|
363
|
+
barrier(CLK_LOCAL_MEM_FENCE); // 1 barrier: partial dots visible
|
|
364
|
+
|
|
365
|
+
// Phase 2 — split_idx==0 reduces partial sums and computes block softmax.
|
|
366
|
+
if (split_idx == 0) {
|
|
367
|
+
if (query_valid) {
|
|
368
|
+
ACC_TYPE m_new = m_i;
|
|
369
|
+
for (int j = 0; j < BLOCK_N; ++j) {
|
|
370
|
+
const int k_row = k_start + j;
|
|
371
|
+
ACC_TYPE score = 0.0f;
|
|
372
|
+
FA_UNROLL
|
|
373
|
+
for (int s = 0; s < N_SPLIT; s++) {
|
|
374
|
+
score += local_partial[j][q_lane * N_SPLIT + s];
|
|
375
|
+
}
|
|
376
|
+
score *= scale;
|
|
377
|
+
|
|
378
|
+
if (is_causal && k_row > (n_kv - n_q + my_query_row)) score = FA_M_INIT;
|
|
379
|
+
if (k_row >= n_kv) score = FA_M_INIT;
|
|
149
380
|
|
|
150
|
-
|
|
151
|
-
|
|
381
|
+
if (mask_base != NULL && blk_cur != 2) {
|
|
382
|
+
if (use_kv_pad && mask_pad_base != NULL) {
|
|
383
|
+
const global MASK_DATA_TYPE* mask_ptr =
|
|
384
|
+
(const global MASK_DATA_TYPE*)(mask_pad_base + my_query_row * mask_pad_nb1);
|
|
385
|
+
score += slope * (ACC_TYPE)mask_ptr[j];
|
|
386
|
+
} else {
|
|
387
|
+
const global MASK_DATA_TYPE* mask_ptr =
|
|
388
|
+
(const global MASK_DATA_TYPE*)(mask_base + my_query_row * mask_nb1);
|
|
389
|
+
if (k_row < n_kv) score += slope * (ACC_TYPE)mask_ptr[k_row];
|
|
390
|
+
}
|
|
391
|
+
}
|
|
152
392
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
393
|
+
if (logit_softcap > 0.0f) {
|
|
394
|
+
score = logit_softcap * tanh(score / logit_softcap);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
m_new = max(m_new, score);
|
|
398
|
+
local_p[q_lane][j] = score;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const ACC_TYPE m_exp = (m_new == FA_M_INIT) ? 0.0f : m_new;
|
|
402
|
+
const ACC_TYPE sp = native_exp(m_i - m_exp);
|
|
403
|
+
ACC_TYPE l_new = l_i * sp;
|
|
404
|
+
for (int j = 0; j < BLOCK_N; ++j) {
|
|
405
|
+
const ACC_TYPE p = native_exp(local_p[q_lane][j] - m_exp);
|
|
406
|
+
local_p[q_lane][j] = p;
|
|
407
|
+
l_new += p;
|
|
408
|
+
}
|
|
409
|
+
local_softmax_scale[q_lane] = sp;
|
|
410
|
+
l_i = l_new;
|
|
411
|
+
m_i = m_new;
|
|
412
|
+
} else {
|
|
413
|
+
local_softmax_scale[q_lane] = 1.0f;
|
|
414
|
+
for (int j = 0; j < BLOCK_N; ++j) local_p[q_lane][j] = 0.0f;
|
|
157
415
|
}
|
|
416
|
+
}
|
|
417
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
158
418
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
419
|
+
// Phase 3 — V accumulate using broadcast probabilities.
|
|
420
|
+
{
|
|
421
|
+
const ACC_TYPE sp_block = local_softmax_scale[q_lane];
|
|
422
|
+
const int dv_off = split_idx * SPLIT_DV_VEC;
|
|
423
|
+
FA_UNROLL
|
|
424
|
+
for (int i = 0; i < SPLIT_DV_VEC; ++i) {
|
|
425
|
+
o_acc[i] *= sp_block;
|
|
426
|
+
}
|
|
427
|
+
for (int j = 0; j < BLOCK_N; ++j) {
|
|
428
|
+
const ACC_TYPE p = local_p[q_lane][j];
|
|
429
|
+
FA_UNROLL
|
|
430
|
+
for (int i = 0; i < SPLIT_DV_VEC; ++i) {
|
|
431
|
+
o_acc[i] = mad(p, CONVERT_KV_ACC4(l_v[j][dv_off + i]), o_acc[i]);
|
|
432
|
+
}
|
|
162
433
|
}
|
|
434
|
+
}
|
|
435
|
+
#else
|
|
436
|
+
// N_SPLIT==1: j+=4 unroll. Requires BLOCK_N % 4 == 0.
|
|
437
|
+
if (query_valid) {
|
|
438
|
+
for (int j = 0; j < BLOCK_N; j += 4) {
|
|
439
|
+
const int k_row0 = k_start + j;
|
|
440
|
+
const int k_row1 = k_start + j + 1;
|
|
441
|
+
const int k_row2 = k_start + j + 2;
|
|
442
|
+
const int k_row3 = k_start + j + 3;
|
|
163
443
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
444
|
+
ACC_TYPE4 dot_acc0 = (ACC_TYPE4)(0.0f);
|
|
445
|
+
ACC_TYPE4 dot_acc1 = (ACC_TYPE4)(0.0f);
|
|
446
|
+
ACC_TYPE4 dot_acc2 = (ACC_TYPE4)(0.0f);
|
|
447
|
+
ACC_TYPE4 dot_acc3 = (ACC_TYPE4)(0.0f);
|
|
448
|
+
FA_UNROLL
|
|
449
|
+
for (int k = 0; k < DK_VEC; k++) {
|
|
450
|
+
const ACC_TYPE4 qk = q_priv[k];
|
|
451
|
+
dot_acc0 = mad(qk, CONVERT_KV_ACC4(l_k[j][k]), dot_acc0);
|
|
452
|
+
dot_acc1 = mad(qk, CONVERT_KV_ACC4(l_k[j+1][k]), dot_acc1);
|
|
453
|
+
dot_acc2 = mad(qk, CONVERT_KV_ACC4(l_k[j+2][k]), dot_acc2);
|
|
454
|
+
dot_acc3 = mad(qk, CONVERT_KV_ACC4(l_k[j+3][k]), dot_acc3);
|
|
455
|
+
}
|
|
456
|
+
ACC_TYPE s0 = (dot_acc0.s0 + dot_acc0.s1 + dot_acc0.s2 + dot_acc0.s3) * scale;
|
|
457
|
+
ACC_TYPE s1 = (dot_acc1.s0 + dot_acc1.s1 + dot_acc1.s2 + dot_acc1.s3) * scale;
|
|
458
|
+
ACC_TYPE s2 = (dot_acc2.s0 + dot_acc2.s1 + dot_acc2.s2 + dot_acc2.s3) * scale;
|
|
459
|
+
ACC_TYPE s3 = (dot_acc3.s0 + dot_acc3.s1 + dot_acc3.s2 + dot_acc3.s3) * scale;
|
|
168
460
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
461
|
+
if (is_causal) {
|
|
462
|
+
const int causal_limit = n_kv - n_q + my_query_row;
|
|
463
|
+
if (k_row0 > causal_limit) s0 = FA_M_INIT;
|
|
464
|
+
if (k_row1 > causal_limit) s1 = FA_M_INIT;
|
|
465
|
+
if (k_row2 > causal_limit) s2 = FA_M_INIT;
|
|
466
|
+
if (k_row3 > causal_limit) s3 = FA_M_INIT;
|
|
467
|
+
}
|
|
468
|
+
if (k_row0 >= n_kv) s0 = FA_M_INIT;
|
|
469
|
+
if (k_row1 >= n_kv) s1 = FA_M_INIT;
|
|
470
|
+
if (k_row2 >= n_kv) s2 = FA_M_INIT;
|
|
471
|
+
if (k_row3 >= n_kv) s3 = FA_M_INIT;
|
|
472
|
+
|
|
473
|
+
if (mask_base != NULL && blk_cur != 2) {
|
|
474
|
+
if (use_kv_pad && mask_pad_base != NULL) {
|
|
475
|
+
const global MASK_DATA_TYPE* mask_ptr = (const global MASK_DATA_TYPE*)(mask_pad_base + my_query_row * mask_pad_nb1);
|
|
476
|
+
s0 += slope * (ACC_TYPE)mask_ptr[j];
|
|
477
|
+
s1 += slope * (ACC_TYPE)mask_ptr[j + 1];
|
|
478
|
+
s2 += slope * (ACC_TYPE)mask_ptr[j + 2];
|
|
479
|
+
s3 += slope * (ACC_TYPE)mask_ptr[j + 3];
|
|
480
|
+
} else {
|
|
481
|
+
const global MASK_DATA_TYPE* mask_ptr = (const global MASK_DATA_TYPE*)(mask_base + my_query_row * mask_nb1);
|
|
482
|
+
if (k_row0 < n_kv) s0 += slope * (ACC_TYPE)mask_ptr[k_row0];
|
|
483
|
+
if (k_row1 < n_kv) s1 += slope * (ACC_TYPE)mask_ptr[k_row1];
|
|
484
|
+
if (k_row2 < n_kv) s2 += slope * (ACC_TYPE)mask_ptr[k_row2];
|
|
485
|
+
if (k_row3 < n_kv) s3 += slope * (ACC_TYPE)mask_ptr[k_row3];
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (logit_softcap > 0.0f) {
|
|
490
|
+
s0 = logit_softcap * tanh(s0 / logit_softcap);
|
|
491
|
+
s1 = logit_softcap * tanh(s1 / logit_softcap);
|
|
492
|
+
s2 = logit_softcap * tanh(s2 / logit_softcap);
|
|
493
|
+
s3 = logit_softcap * tanh(s3 / logit_softcap);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const ACC_TYPE m_new = max(m_i, max(max(s0, s1), max(s2, s3)));
|
|
497
|
+
// Whole tile masked (m_new == FA_M_INIT): force the exp() args
|
|
498
|
+
// far negative so the tile contributes 0, not exp(0)=1.
|
|
499
|
+
const ACC_TYPE m_exp = (m_new == FA_M_INIT) ? 0.0f : m_new;
|
|
500
|
+
const ACC_TYPE scale_prev = native_exp(m_i - m_exp);
|
|
501
|
+
const ACC_TYPE p0 = native_exp(s0 - m_exp);
|
|
502
|
+
const ACC_TYPE p1 = native_exp(s1 - m_exp);
|
|
503
|
+
const ACC_TYPE p2 = native_exp(s2 - m_exp);
|
|
504
|
+
const ACC_TYPE p3 = native_exp(s3 - m_exp);
|
|
505
|
+
|
|
506
|
+
FA_UNROLL
|
|
507
|
+
for (int i = 0; i < DV_VEC; ++i) {
|
|
508
|
+
o_acc[i] = mad(p3, CONVERT_KV_ACC4(l_v[j+3][i]),
|
|
509
|
+
mad(p2, CONVERT_KV_ACC4(l_v[j+2][i]),
|
|
510
|
+
mad(p1, CONVERT_KV_ACC4(l_v[j+1][i]),
|
|
511
|
+
mad(p0, CONVERT_KV_ACC4(l_v[j][i]),
|
|
512
|
+
o_acc[i] * scale_prev))));
|
|
513
|
+
}
|
|
514
|
+
l_i = l_i * scale_prev + p0 + p1 + p2 + p3;
|
|
515
|
+
m_i = m_new;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
#endif
|
|
519
|
+
// End of tile: every thread must finish reading l_k/l_v before the
|
|
520
|
+
// next iteration's load overwrites them (WAR hazard on local memory).
|
|
521
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Write output.
|
|
525
|
+
#if N_SPLIT > 1 && defined(HAS_SUBGROUP_SHUFFLE)
|
|
526
|
+
if (query_valid) {
|
|
527
|
+
ACC_TYPE sinks_sp = 1.0f;
|
|
528
|
+
if (sinks_void != NULL) {
|
|
529
|
+
const global ACC_TYPE* sinks_ptr = (const global ACC_TYPE*)((const global char*)sinks_void + sinks_offset);
|
|
530
|
+
const ACC_TYPE m_sink = sinks_ptr[head_idx];
|
|
531
|
+
const ACC_TYPE m_final = max(m_i, m_sink);
|
|
532
|
+
sinks_sp = exp(m_i - m_final);
|
|
533
|
+
l_i = l_i * sinks_sp + exp(m_sink - m_final);
|
|
534
|
+
m_i = m_final;
|
|
535
|
+
}
|
|
536
|
+
const ACC_TYPE l_inv = (l_i > 0.0f) ? (1.0f / l_i) : 0.0f;
|
|
537
|
+
const int dv_off = split_idx * SPLIT_DV_VEC;
|
|
538
|
+
const ulong o_row_offset = batch_idx * o_nb3 + my_query_row * o_nb2 + head_idx * o_nb1;
|
|
539
|
+
global O_DATA_TYPE4 *o_row = (global O_DATA_TYPE4 *)(o_base + o_row_offset);
|
|
540
|
+
if (l_inv > 0.0f) {
|
|
541
|
+
FA_UNROLL
|
|
542
|
+
for (int i = 0; i < SPLIT_DV_VEC; ++i) {
|
|
543
|
+
o_row[dv_off + i] = CONVERT_O_DATA4(o_acc[i] * sinks_sp * l_inv);
|
|
172
544
|
}
|
|
173
|
-
|
|
174
|
-
|
|
545
|
+
} else {
|
|
546
|
+
FA_UNROLL
|
|
547
|
+
for (int i = 0; i < SPLIT_DV_VEC; ++i) {
|
|
548
|
+
o_row[dv_off + i] = (O_DATA_TYPE4)(0.0f);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
#elif N_SPLIT > 1
|
|
553
|
+
if (split_idx == 0) {
|
|
554
|
+
ACC_TYPE sinks_sp = 1.0f;
|
|
555
|
+
if (query_valid && sinks_void != NULL) {
|
|
556
|
+
const global ACC_TYPE* sinks_ptr = (const global ACC_TYPE*)((const global char*)sinks_void + sinks_offset);
|
|
557
|
+
const ACC_TYPE m_sink = sinks_ptr[head_idx];
|
|
558
|
+
const ACC_TYPE m_final = max(m_i, m_sink);
|
|
559
|
+
sinks_sp = exp(m_i - m_final);
|
|
560
|
+
l_i = l_i * sinks_sp + exp(m_sink - m_final);
|
|
561
|
+
m_i = m_final;
|
|
175
562
|
}
|
|
563
|
+
local_softmax_scale[q_lane] = sinks_sp;
|
|
564
|
+
local_l_inv[q_lane] = (query_valid && l_i > 0.0f) ? (1.0f / l_i) : 0.0f;
|
|
176
565
|
}
|
|
566
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
177
567
|
|
|
178
|
-
if (
|
|
568
|
+
if (query_valid) {
|
|
569
|
+
const ACC_TYPE sinks_sp = local_softmax_scale[q_lane];
|
|
570
|
+
const ACC_TYPE l_inv = local_l_inv[q_lane];
|
|
571
|
+
const int dv_off = split_idx * SPLIT_DV_VEC;
|
|
572
|
+
const ulong o_row_offset = batch_idx * o_nb3 + my_query_row * o_nb2 + head_idx * o_nb1;
|
|
573
|
+
global O_DATA_TYPE4 *o_row = (global O_DATA_TYPE4 *)(o_base + o_row_offset);
|
|
574
|
+
if (l_inv > 0.0f) {
|
|
575
|
+
FA_UNROLL
|
|
576
|
+
for (int i = 0; i < SPLIT_DV_VEC; ++i) {
|
|
577
|
+
o_row[dv_off + i] = CONVERT_O_DATA4(o_acc[i] * sinks_sp * l_inv);
|
|
578
|
+
}
|
|
579
|
+
} else {
|
|
580
|
+
FA_UNROLL
|
|
581
|
+
for (int i = 0; i < SPLIT_DV_VEC; ++i) {
|
|
582
|
+
o_row[dv_off + i] = (O_DATA_TYPE4)(0.0f);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
#else
|
|
587
|
+
if (query_valid) {
|
|
179
588
|
if (sinks_void != NULL) {
|
|
180
589
|
const global ACC_TYPE* sinks_ptr = (const global ACC_TYPE*)((const global char*)sinks_void + sinks_offset);
|
|
181
590
|
const ACC_TYPE m_sink = sinks_ptr[head_idx];
|
|
182
591
|
const ACC_TYPE m_final = max(m_i, m_sink);
|
|
183
592
|
|
|
184
593
|
const ACC_TYPE scale_o = exp(m_i - m_final);
|
|
185
|
-
|
|
594
|
+
FA_UNROLL
|
|
186
595
|
for (int i = 0; i < DV_VEC; ++i) {
|
|
187
596
|
o_acc[i] *= scale_o;
|
|
188
597
|
}
|
|
@@ -194,19 +603,25 @@ __kernel void flash_attn_f32_f16(
|
|
|
194
603
|
global O_DATA_TYPE4 *o_row = (global O_DATA_TYPE4 *)(o_base + o_row_offset);
|
|
195
604
|
if (l_i > 0.0f) {
|
|
196
605
|
const ACC_TYPE l_inv = 1.0f / l_i;
|
|
197
|
-
|
|
606
|
+
FA_UNROLL
|
|
198
607
|
for (int i = 0; i < DV_VEC; ++i) {
|
|
199
608
|
o_row[i] = CONVERT_O_DATA4(o_acc[i] * l_inv);
|
|
200
609
|
}
|
|
201
610
|
} else {
|
|
202
|
-
|
|
611
|
+
FA_UNROLL
|
|
203
612
|
for (int i = 0; i < DV_VEC; ++i) {
|
|
204
613
|
o_row[i] = (O_DATA_TYPE4)(0.0f);
|
|
205
614
|
}
|
|
206
615
|
}
|
|
207
616
|
}
|
|
617
|
+
#endif
|
|
208
618
|
}
|
|
619
|
+
#endif // !FA_DECODE_ONLY
|
|
209
620
|
|
|
621
|
+
// allow bypassing decode kernels to avoid compiler crash for DK=512 on Adreno GPUs
|
|
622
|
+
#ifndef FA_PREFILL_ONLY
|
|
623
|
+
#ifndef FA_MQ_ONLY // q1 excluded from the MQ-only (g8) program
|
|
624
|
+
REQD_FA_SG
|
|
210
625
|
__kernel void flash_attn_f32_f16_q1(
|
|
211
626
|
const global void * q_void, ulong q_offset,
|
|
212
627
|
const global void * k_void, ulong k_offset,
|
|
@@ -247,7 +662,9 @@ __kernel void flash_attn_f32_f16_q1(
|
|
|
247
662
|
const int head_kv_idx = head_idx / gqa_ratio;
|
|
248
663
|
|
|
249
664
|
const global char* q_base = (const global char*)q_void + q_offset;
|
|
665
|
+
#ifndef FA_K_IMG
|
|
250
666
|
const global char* k_base = (const global char*)k_void + k_offset;
|
|
667
|
+
#endif
|
|
251
668
|
const global char* v_base = (const global char*)v_void + v_offset;
|
|
252
669
|
global char* o_base = (global char*)o_void + o_offset;
|
|
253
670
|
|
|
@@ -258,13 +675,16 @@ __kernel void flash_attn_f32_f16_q1(
|
|
|
258
675
|
mask_base = (const global char*)mask_void + mask_offset + mask_batch_idx * mask_nb3 + mask_head_idx * mask_nb2;
|
|
259
676
|
}
|
|
260
677
|
|
|
261
|
-
|
|
678
|
+
// Q is uniform across WG threads (n_q=1). Share via local memory to
|
|
679
|
+
// avoid per-thread q_priv[DK_VEC] dynamic-indexed private array that
|
|
680
|
+
// spills to DDR on Adreno.
|
|
681
|
+
__local ACC_TYPE4 q_shared[DK_VEC];
|
|
262
682
|
const ulong q_row_offset = batch_idx * q_nb3 + head_idx * q_nb2;
|
|
263
683
|
const global Q_DATA_TYPE4* q_ptr = (const global Q_DATA_TYPE4*)(q_base + q_row_offset);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
q_priv[i] = CONVERT_Q_ACC4(q_ptr[i]);
|
|
684
|
+
for (int i = tid; i < DK_VEC; i += Q1_WG_SIZE) {
|
|
685
|
+
q_shared[i] = CONVERT_Q_ACC4(q_ptr[i]);
|
|
267
686
|
}
|
|
687
|
+
sub_group_barrier(CLK_LOCAL_MEM_FENCE);
|
|
268
688
|
|
|
269
689
|
float slope = get_alibi_slope(max_bias, head_idx, n_head_log2, m0, m1);
|
|
270
690
|
|
|
@@ -273,14 +693,14 @@ __kernel void flash_attn_f32_f16_q1(
|
|
|
273
693
|
sinks_ptr = (const global ACC_TYPE*)((const global char*)sinks_void + sinks_offset);
|
|
274
694
|
}
|
|
275
695
|
|
|
276
|
-
ACC_TYPE m_i = (sinks_ptr != NULL) ? sinks_ptr[head_idx] :
|
|
696
|
+
ACC_TYPE m_i = (sinks_ptr != NULL) ? sinks_ptr[head_idx] : FA_M_INIT;
|
|
277
697
|
for (int k_idx = tid; k_idx < n_kv; k_idx += Q1_WG_SIZE) {
|
|
278
698
|
const ulong k_row_offset = batch_idx * k_nb3 + head_kv_idx * k_nb2 + k_idx * k_nb1;
|
|
279
699
|
const global KV_DATA_TYPE4* k_ptr = (const global KV_DATA_TYPE4*)(k_base + k_row_offset);
|
|
280
700
|
ACC_TYPE4 dot_acc = (ACC_TYPE4)(0.0f);
|
|
281
|
-
|
|
701
|
+
FA_UNROLL
|
|
282
702
|
for (int k = 0; k < DK_VEC; k++) {
|
|
283
|
-
dot_acc = mad(
|
|
703
|
+
dot_acc = mad(q_shared[k], CONVERT_KV_ACC4(k_ptr[k]), dot_acc);
|
|
284
704
|
}
|
|
285
705
|
ACC_TYPE score = (dot_acc.s0 + dot_acc.s1 + dot_acc.s2 + dot_acc.s3) * scale;
|
|
286
706
|
if (mask_base != NULL) {
|
|
@@ -293,18 +713,10 @@ __kernel void flash_attn_f32_f16_q1(
|
|
|
293
713
|
m_i = max(m_i, score);
|
|
294
714
|
}
|
|
295
715
|
|
|
296
|
-
|
|
297
|
-
local_m[tid] = m_i;
|
|
298
|
-
barrier(CLK_LOCAL_MEM_FENCE);
|
|
299
|
-
#pragma unroll
|
|
300
|
-
for (int s = Q1_WG_SIZE / 2; s > 0; s >>= 1) {
|
|
301
|
-
if (tid < s) local_m[tid] = max(local_m[tid], local_m[tid + s]);
|
|
302
|
-
barrier(CLK_LOCAL_MEM_FENCE);
|
|
303
|
-
}
|
|
304
|
-
const ACC_TYPE m_final = local_m[0];
|
|
716
|
+
const ACC_TYPE m_final = sub_group_reduce_max(m_i);
|
|
305
717
|
|
|
306
718
|
ACC_TYPE4 o_acc[DV_VEC];
|
|
307
|
-
|
|
719
|
+
FA_UNROLL
|
|
308
720
|
for (int i = 0; i < DV_VEC; ++i) o_acc[i] = (ACC_TYPE4)(0.0f);
|
|
309
721
|
ACC_TYPE l_i = 0.0f;
|
|
310
722
|
|
|
@@ -314,9 +726,9 @@ __kernel void flash_attn_f32_f16_q1(
|
|
|
314
726
|
const global KV_DATA_TYPE4* k_ptr = (const global KV_DATA_TYPE4*)(k_base + k_row_offset);
|
|
315
727
|
const global KV_DATA_TYPE4* v_ptr = (const global KV_DATA_TYPE4*)(v_base + v_row_offset);
|
|
316
728
|
ACC_TYPE4 dot_acc = (ACC_TYPE4)(0.0f);
|
|
317
|
-
|
|
729
|
+
FA_UNROLL
|
|
318
730
|
for (int k = 0; k < DK_VEC; k++) {
|
|
319
|
-
dot_acc = mad(
|
|
731
|
+
dot_acc = mad(q_shared[k], CONVERT_KV_ACC4(k_ptr[k]), dot_acc);
|
|
320
732
|
}
|
|
321
733
|
ACC_TYPE score = (dot_acc.s0 + dot_acc.s1 + dot_acc.s2 + dot_acc.s3) * scale;
|
|
322
734
|
if (mask_base != NULL) {
|
|
@@ -328,25 +740,18 @@ __kernel void flash_attn_f32_f16_q1(
|
|
|
328
740
|
}
|
|
329
741
|
const ACC_TYPE p = exp(score - m_final);
|
|
330
742
|
l_i += p;
|
|
331
|
-
|
|
743
|
+
FA_UNROLL
|
|
332
744
|
for (int i = 0; i < DV_VEC; i++) {
|
|
333
745
|
o_acc[i] = mad(p, CONVERT_KV_ACC4(v_ptr[i]), o_acc[i]);
|
|
334
746
|
}
|
|
335
747
|
}
|
|
336
748
|
|
|
337
|
-
__local ACC_TYPE local_l[Q1_WG_SIZE];
|
|
338
749
|
__local ACC_TYPE4 local_o_comp[Q1_WG_SIZE];
|
|
339
|
-
|
|
340
|
-
barrier(CLK_LOCAL_MEM_FENCE);
|
|
341
|
-
#pragma unroll
|
|
342
|
-
for (int s = Q1_WG_SIZE / 2; s > 0; s >>= 1) {
|
|
343
|
-
if (tid < s) local_l[tid] += local_l[tid + s];
|
|
344
|
-
barrier(CLK_LOCAL_MEM_FENCE);
|
|
345
|
-
}
|
|
750
|
+
const ACC_TYPE l_red = sub_group_reduce_add(l_i);
|
|
346
751
|
|
|
347
752
|
const ulong o_row_offset = batch_idx * o_nb3 + head_idx * o_nb1;
|
|
348
753
|
global O_DATA_TYPE4 *o_row = (global O_DATA_TYPE4 *)(o_base + o_row_offset);
|
|
349
|
-
ACC_TYPE l_final =
|
|
754
|
+
ACC_TYPE l_final = l_red;
|
|
350
755
|
|
|
351
756
|
if (sinks_ptr != NULL) {
|
|
352
757
|
l_final += exp(sinks_ptr[head_idx] - m_final);
|
|
@@ -356,18 +761,1512 @@ __kernel void flash_attn_f32_f16_q1(
|
|
|
356
761
|
const ACC_TYPE l_inv = 1.0f / l_final;
|
|
357
762
|
for (int i = 0; i < DV_VEC; i++) {
|
|
358
763
|
local_o_comp[tid] = o_acc[i];
|
|
359
|
-
|
|
360
|
-
|
|
764
|
+
sub_group_barrier(CLK_LOCAL_MEM_FENCE);
|
|
765
|
+
FA_UNROLL
|
|
361
766
|
for (int s = Q1_WG_SIZE / 2; s > 0; s >>= 1) {
|
|
362
767
|
if (tid < s) local_o_comp[tid] += local_o_comp[tid + s];
|
|
363
|
-
|
|
768
|
+
sub_group_barrier(CLK_LOCAL_MEM_FENCE);
|
|
364
769
|
}
|
|
365
770
|
if (tid == 0) {
|
|
366
771
|
o_row[i] = CONVERT_O_DATA4(local_o_comp[0] * l_inv);
|
|
367
772
|
}
|
|
368
773
|
}
|
|
369
774
|
} else if (tid == 0) {
|
|
370
|
-
|
|
775
|
+
FA_UNROLL
|
|
371
776
|
for (int i = 0; i < DV_VEC; ++i) o_row[i] = (O_DATA_TYPE4)(0.0f);
|
|
372
777
|
}
|
|
373
778
|
}
|
|
779
|
+
|
|
780
|
+
#endif // !FA_MQ_ONLY (q1)
|
|
781
|
+
// decode variant for large DV (e.g. Gemma-4 DK=DV=512 global layers).
|
|
782
|
+
#define VEC_NSG 4
|
|
783
|
+
#define VEC_WG_SIZE (Q1_WG_SIZE * VEC_NSG)
|
|
784
|
+
#define Q1V_DV_PER_THREAD ((DV_VEC + Q1_WG_SIZE - 1) / Q1_WG_SIZE)
|
|
785
|
+
|
|
786
|
+
// allow bypassing the kernel to avoid compiler crash for DK=512 on Adreno GPUs
|
|
787
|
+
#if !defined(FA_DECODE_MINIMAL) && !defined(FA_MQ_ONLY)
|
|
788
|
+
REQD_SUBGROUP_SIZE_64
|
|
789
|
+
__kernel void flash_attn_f32_f16_q1_vec(
|
|
790
|
+
const global void * q_void, ulong q_offset,
|
|
791
|
+
const global void * k_void, ulong k_offset,
|
|
792
|
+
const global void * v_void, ulong v_offset,
|
|
793
|
+
global void * o_void, ulong o_offset,
|
|
794
|
+
const float scale,
|
|
795
|
+
const int n_q,
|
|
796
|
+
const int n_kv,
|
|
797
|
+
const int is_causal,
|
|
798
|
+
const int n_head,
|
|
799
|
+
const ulong q_nb1, const ulong q_nb2, const ulong q_nb3,
|
|
800
|
+
const ulong k_nb1, const ulong k_nb2, const ulong k_nb3,
|
|
801
|
+
const ulong v_nb1, const ulong v_nb2, const ulong v_nb3,
|
|
802
|
+
const ulong o_nb1, const ulong o_nb2, const ulong o_nb3,
|
|
803
|
+
const float max_bias,
|
|
804
|
+
const float m0,
|
|
805
|
+
const float m1,
|
|
806
|
+
const int n_head_log2,
|
|
807
|
+
const float logit_softcap,
|
|
808
|
+
const int n_head_kv,
|
|
809
|
+
const global void* mask_void,
|
|
810
|
+
const ulong mask_offset,
|
|
811
|
+
const ulong mask_nb1,
|
|
812
|
+
const ulong mask_nb2,
|
|
813
|
+
const ulong mask_nb3,
|
|
814
|
+
const int mask_ne2,
|
|
815
|
+
const int mask_ne3,
|
|
816
|
+
const global void* sinks_void,
|
|
817
|
+
const ulong sinks_offset
|
|
818
|
+
) {
|
|
819
|
+
const int tid = get_local_id(0);
|
|
820
|
+
const int sgid = tid / Q1_WG_SIZE; // subgroup index (0..VEC_NSG-1)
|
|
821
|
+
const int tid_sg = tid % Q1_WG_SIZE; // lane within subgroup
|
|
822
|
+
const int head_batch_idx = get_global_id(1);
|
|
823
|
+
|
|
824
|
+
const int batch_idx = head_batch_idx / n_head;
|
|
825
|
+
const int head_idx = head_batch_idx % n_head;
|
|
826
|
+
|
|
827
|
+
const int gqa_ratio = n_head / n_head_kv;
|
|
828
|
+
const int head_kv_idx = head_idx / gqa_ratio;
|
|
829
|
+
|
|
830
|
+
const global char * q_base = (const global char *) q_void + q_offset;
|
|
831
|
+
const global char * k_base = (const global char *) k_void + k_offset;
|
|
832
|
+
const global char * v_base = (const global char *) v_void + v_offset;
|
|
833
|
+
global char * o_base = (global char *) o_void + o_offset;
|
|
834
|
+
|
|
835
|
+
const global char * mask_base = NULL;
|
|
836
|
+
if (mask_void != NULL) {
|
|
837
|
+
const int mask_head_idx = head_idx % mask_ne2;
|
|
838
|
+
const int mask_batch_idx = batch_idx % mask_ne3;
|
|
839
|
+
mask_base = (const global char *) mask_void + mask_offset +
|
|
840
|
+
mask_batch_idx * mask_nb3 + mask_head_idx * mask_nb2;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// Q is uniform across the WG — stage in __local once. All WG threads load.
|
|
844
|
+
__local ACC_TYPE4 q_shared[DK_VEC];
|
|
845
|
+
{
|
|
846
|
+
const ulong q_row_offset = batch_idx * q_nb3 + head_idx * q_nb2;
|
|
847
|
+
const global Q_DATA_TYPE4 * q_ptr = (const global Q_DATA_TYPE4 *) (q_base + q_row_offset);
|
|
848
|
+
for (int i = tid; i < DK_VEC; i += VEC_WG_SIZE) {
|
|
849
|
+
q_shared[i] = CONVERT_Q_ACC4(q_ptr[i]);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
853
|
+
|
|
854
|
+
const float slope = get_alibi_slope(max_bias, head_idx, n_head_log2, m0, m1);
|
|
855
|
+
|
|
856
|
+
const global ACC_TYPE * sinks_ptr = NULL;
|
|
857
|
+
if (sinks_void != NULL) {
|
|
858
|
+
sinks_ptr = (const global ACC_TYPE *) ((const global char *) sinks_void + sinks_offset);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// per-thread DV slice within its subgroup
|
|
862
|
+
// DV=512 -> 2x float4 = 32 bytes; DV=256 -> 1x float4 - no spill
|
|
863
|
+
ACC_TYPE4 o_acc[Q1V_DV_PER_THREAD];
|
|
864
|
+
#pragma unroll
|
|
865
|
+
for (int i = 0; i < Q1V_DV_PER_THREAD; ++i) o_acc[i] = (ACC_TYPE4)(0.0f);
|
|
866
|
+
|
|
867
|
+
// each subgroup independently runs the FA-2 online softmax over its slice of n_kv.
|
|
868
|
+
// sinks are not folded into per-subgroup m_i — they're added once in
|
|
869
|
+
// the cross-subgroup merge to avoid double-counting.
|
|
870
|
+
ACC_TYPE m_i = FA_M_INIT;
|
|
871
|
+
ACC_TYPE l_i = 0.0f;
|
|
872
|
+
|
|
873
|
+
const int kv_per_sg = (n_kv + VEC_NSG - 1) / VEC_NSG;
|
|
874
|
+
const int kv_start = sgid * kv_per_sg;
|
|
875
|
+
const int kv_end = min(n_kv, kv_start + kv_per_sg);
|
|
876
|
+
|
|
877
|
+
for (int k_idx = kv_start; k_idx < kv_end; ++k_idx) {
|
|
878
|
+
const ulong k_row_off = batch_idx * k_nb3 + head_kv_idx * k_nb2 + k_idx * k_nb1;
|
|
879
|
+
const ulong v_row_off = batch_idx * v_nb3 + head_kv_idx * v_nb2 + k_idx * v_nb1;
|
|
880
|
+
const global KV_DATA_TYPE4 * k_ptr = (const global KV_DATA_TYPE4 *) (k_base + k_row_off);
|
|
881
|
+
const global KV_DATA_TYPE4 * v_ptr = (const global KV_DATA_TYPE4 *) (v_base + v_row_off);
|
|
882
|
+
|
|
883
|
+
// Q*K^T: each thread accumulates its DK slice; subgroup-reduce the partial.
|
|
884
|
+
ACC_TYPE4 dot4 = (ACC_TYPE4)(0.0f);
|
|
885
|
+
for (int k = tid_sg; k < DK_VEC; k += Q1_WG_SIZE) {
|
|
886
|
+
dot4 = mad(q_shared[k], CONVERT_KV_ACC4(k_ptr[k]), dot4);
|
|
887
|
+
}
|
|
888
|
+
ACC_TYPE dot_partial = dot4.s0 + dot4.s1 + dot4.s2 + dot4.s3;
|
|
889
|
+
ACC_TYPE score = sub_group_reduce_add(dot_partial) * scale;
|
|
890
|
+
|
|
891
|
+
if (mask_base != NULL) {
|
|
892
|
+
const global MASK_DATA_TYPE * mask_ptr = (const global MASK_DATA_TYPE *) mask_base;
|
|
893
|
+
score += slope * (ACC_TYPE) mask_ptr[k_idx];
|
|
894
|
+
}
|
|
895
|
+
if (logit_softcap > 0.0f) {
|
|
896
|
+
score = logit_softcap * tanh(score / logit_softcap);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// FA-2 online update. All threads in the subgroup see the same score,
|
|
900
|
+
// so m_i and l_i evolve identically across lanes within the subgroup.
|
|
901
|
+
const ACC_TYPE m_new = max(m_i, score);
|
|
902
|
+
const ACC_TYPE scale_prev = native_exp(m_i - m_new);
|
|
903
|
+
const ACC_TYPE p = native_exp(score - m_new);
|
|
904
|
+
|
|
905
|
+
int idx = 0;
|
|
906
|
+
for (int dv_idx = tid_sg; dv_idx < DV_VEC; dv_idx += Q1_WG_SIZE, ++idx) {
|
|
907
|
+
o_acc[idx] = mad(p, CONVERT_KV_ACC4(v_ptr[dv_idx]), o_acc[idx] * scale_prev);
|
|
908
|
+
}
|
|
909
|
+
l_i = l_i * scale_prev + p;
|
|
910
|
+
m_i = m_new;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// Cross-subgroup merge via __local. Each subgroup publishes (m_i, l_i)
|
|
914
|
+
// and its o_acc slice; subgroup 0 then folds them into the final norm
|
|
915
|
+
// and writes the row.
|
|
916
|
+
__local ACC_TYPE sg_m[VEC_NSG];
|
|
917
|
+
__local ACC_TYPE sg_l[VEC_NSG];
|
|
918
|
+
__local ACC_TYPE4 sg_o[VEC_NSG][DV_VEC];
|
|
919
|
+
|
|
920
|
+
if (tid_sg == 0) {
|
|
921
|
+
sg_m[sgid] = m_i;
|
|
922
|
+
sg_l[sgid] = l_i;
|
|
923
|
+
}
|
|
924
|
+
{
|
|
925
|
+
int idx = 0;
|
|
926
|
+
for (int dv_idx = tid_sg; dv_idx < DV_VEC; dv_idx += Q1_WG_SIZE, ++idx) {
|
|
927
|
+
sg_o[sgid][dv_idx] = o_acc[idx];
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
931
|
+
|
|
932
|
+
if (sgid == 0) {
|
|
933
|
+
// m_final = max over all subgroups' m_i, plus the sink (if any).
|
|
934
|
+
ACC_TYPE m_final = sg_m[0];
|
|
935
|
+
#pragma unroll
|
|
936
|
+
for (int s = 1; s < VEC_NSG; ++s) {
|
|
937
|
+
m_final = max(m_final, sg_m[s]);
|
|
938
|
+
}
|
|
939
|
+
if (sinks_ptr != NULL) {
|
|
940
|
+
m_final = max(m_final, sinks_ptr[head_idx]);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
ACC_TYPE l_final = 0.0f;
|
|
944
|
+
#pragma unroll
|
|
945
|
+
for (int s = 0; s < VEC_NSG; ++s) {
|
|
946
|
+
l_final += sg_l[s] * native_exp(sg_m[s] - m_final);
|
|
947
|
+
}
|
|
948
|
+
if (sinks_ptr != NULL) {
|
|
949
|
+
l_final += native_exp(sinks_ptr[head_idx] - m_final);
|
|
950
|
+
}
|
|
951
|
+
const ACC_TYPE l_inv = (l_final > 0.0f) ? (1.0f / l_final) : 0.0f;
|
|
952
|
+
|
|
953
|
+
const ulong o_row_offset = batch_idx * o_nb3 + head_idx * o_nb1;
|
|
954
|
+
global O_DATA_TYPE4 * o_row = (global O_DATA_TYPE4 *) (o_base + o_row_offset);
|
|
955
|
+
|
|
956
|
+
// Each thread in subgroup 0 writes its DV slice, folding all subgroups'
|
|
957
|
+
// contributions with the rescale factor.
|
|
958
|
+
int idx = 0;
|
|
959
|
+
for (int dv_idx = tid_sg; dv_idx < DV_VEC; dv_idx += Q1_WG_SIZE, ++idx) {
|
|
960
|
+
ACC_TYPE4 o_merged = (ACC_TYPE4)(0.0f);
|
|
961
|
+
#pragma unroll
|
|
962
|
+
for (int s = 0; s < VEC_NSG; ++s) {
|
|
963
|
+
const ACC_TYPE alpha = native_exp(sg_m[s] - m_final);
|
|
964
|
+
o_merged = mad((ACC_TYPE4)(alpha), sg_o[s][dv_idx], o_merged);
|
|
965
|
+
}
|
|
966
|
+
o_row[dv_idx] = CONVERT_O_DATA4(o_merged * l_inv);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
#endif // !FA_DECODE_MINIMAL
|
|
972
|
+
|
|
973
|
+
#ifndef FA_DECODE_ONLY
|
|
974
|
+
|
|
975
|
+
// flash_attn_f32_f16_q1_local_tile
|
|
976
|
+
// one WG per (q_idx, q_head)
|
|
977
|
+
|
|
978
|
+
#define LT_KC 32
|
|
979
|
+
#define LT_WG 128
|
|
980
|
+
|
|
981
|
+
#ifndef FA_MQ_ONLY // q1_local_tile excluded from the MQ-only (g8) program
|
|
982
|
+
REQD_SUBGROUP_SIZE_64
|
|
983
|
+
__kernel void flash_attn_f32_f16_q1_local_tile(
|
|
984
|
+
const global void * q_void, ulong q_offset,
|
|
985
|
+
const global void * k_void, ulong k_offset,
|
|
986
|
+
const global void * v_void, ulong v_offset,
|
|
987
|
+
global void * o_void, ulong o_offset,
|
|
988
|
+
const float scale,
|
|
989
|
+
const int n_q,
|
|
990
|
+
const int n_kv,
|
|
991
|
+
const int is_causal,
|
|
992
|
+
const int n_head,
|
|
993
|
+
const ulong q_nb1, const ulong q_nb2, const ulong q_nb3,
|
|
994
|
+
const ulong k_nb1, const ulong k_nb2, const ulong k_nb3,
|
|
995
|
+
const ulong v_nb1, const ulong v_nb2, const ulong v_nb3,
|
|
996
|
+
const ulong o_nb1, const ulong o_nb2, const ulong o_nb3,
|
|
997
|
+
const float max_bias,
|
|
998
|
+
const float m0,
|
|
999
|
+
const float m1,
|
|
1000
|
+
const int n_head_log2,
|
|
1001
|
+
const float logit_softcap,
|
|
1002
|
+
const int n_head_kv,
|
|
1003
|
+
const global void * mask_void,
|
|
1004
|
+
const ulong mask_offset,
|
|
1005
|
+
const ulong mask_nb1,
|
|
1006
|
+
const ulong mask_nb2,
|
|
1007
|
+
const ulong mask_nb3,
|
|
1008
|
+
const int mask_ne2,
|
|
1009
|
+
const int mask_ne3,
|
|
1010
|
+
const global void * sinks_void,
|
|
1011
|
+
const ulong sinks_offset
|
|
1012
|
+
) {
|
|
1013
|
+
const int q_idx = get_global_id(0) / LT_WG;
|
|
1014
|
+
const int head_idx = get_global_id(1);
|
|
1015
|
+
const int batch_idx = get_global_id(2);
|
|
1016
|
+
const int tid = get_local_id(0);
|
|
1017
|
+
|
|
1018
|
+
const int gqa_ratio = n_head_kv > 0 ? (n_head / n_head_kv) : 1;
|
|
1019
|
+
const int head_kv_idx = head_idx / gqa_ratio;
|
|
1020
|
+
|
|
1021
|
+
const float slope = get_alibi_slope(max_bias, head_idx, n_head_log2, m0, m1);
|
|
1022
|
+
|
|
1023
|
+
__local half k_tile[LT_KC * DK]; // 32*128*2 = 8 KB at DK=128
|
|
1024
|
+
__local half v_tile[LT_KC * DV]; // 8 KB
|
|
1025
|
+
__local float red[LT_WG]; // 512 B reduction scratch
|
|
1026
|
+
__local float score_shared; // broadcast score (each K-step)
|
|
1027
|
+
|
|
1028
|
+
// Each thread owns one float of Q at index `tid` (assumes LT_WG == DK).
|
|
1029
|
+
const global char * q_row_base = (const global char *) q_void + q_offset +
|
|
1030
|
+
batch_idx * q_nb3 + head_idx * q_nb2 + (ulong) q_idx * q_nb1;
|
|
1031
|
+
float q_val = ((const global float *) q_row_base)[tid];
|
|
1032
|
+
|
|
1033
|
+
const global char * mask_base = NULL;
|
|
1034
|
+
if (mask_void != NULL) {
|
|
1035
|
+
const int mask_head_idx = head_idx % mask_ne2;
|
|
1036
|
+
const int mask_batch_idx = batch_idx % mask_ne3;
|
|
1037
|
+
mask_base = (const global char *) mask_void + mask_offset +
|
|
1038
|
+
mask_batch_idx * mask_nb3 + mask_head_idx * mask_nb2 +
|
|
1039
|
+
(ulong) q_idx * mask_nb1;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
float o_val = 0.0f;
|
|
1043
|
+
float m_i = FA_M_INIT;
|
|
1044
|
+
float l_i = 0.0f;
|
|
1045
|
+
|
|
1046
|
+
for (int kb = 0; kb < n_kv; kb += LT_KC) {
|
|
1047
|
+
const int tile_len = min(LT_KC, n_kv - kb);
|
|
1048
|
+
|
|
1049
|
+
// Stage K and V tiles into __local.
|
|
1050
|
+
for (int i = tid; i < tile_len * DK; i += LT_WG) {
|
|
1051
|
+
const int j = i / DK;
|
|
1052
|
+
const int d = i % DK;
|
|
1053
|
+
const int kv_idx = kb + j;
|
|
1054
|
+
const global char * k_row = (const global char *) k_void + k_offset +
|
|
1055
|
+
batch_idx * k_nb3 + head_kv_idx * k_nb2 +
|
|
1056
|
+
(ulong) kv_idx * k_nb1;
|
|
1057
|
+
const global char * v_row = (const global char *) v_void + v_offset +
|
|
1058
|
+
batch_idx * v_nb3 + head_kv_idx * v_nb2 +
|
|
1059
|
+
(ulong) kv_idx * v_nb1;
|
|
1060
|
+
k_tile[j * DK + d] = ((const global half *) k_row)[d];
|
|
1061
|
+
v_tile[j * DV + d] = ((const global half *) v_row)[d];
|
|
1062
|
+
}
|
|
1063
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1064
|
+
|
|
1065
|
+
for (int j = 0; j < tile_len; ++j) {
|
|
1066
|
+
const int kv_idx = kb + j;
|
|
1067
|
+
|
|
1068
|
+
// Q·K dot via __local tree-reduce.
|
|
1069
|
+
red[tid] = q_val * convert_float(k_tile[j * DK + tid]);
|
|
1070
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1071
|
+
for (int stride = LT_WG >> 1; stride > 0; stride >>= 1) {
|
|
1072
|
+
if (tid < stride) {
|
|
1073
|
+
red[tid] += red[tid + stride];
|
|
1074
|
+
}
|
|
1075
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
if (tid == 0) {
|
|
1079
|
+
float s = red[0] * scale;
|
|
1080
|
+
if (mask_base != NULL) {
|
|
1081
|
+
const global MASK_DATA_TYPE * mask_ptr = (const global MASK_DATA_TYPE *) mask_base;
|
|
1082
|
+
s += slope * (float) mask_ptr[kv_idx];
|
|
1083
|
+
}
|
|
1084
|
+
if (logit_softcap > 0.0f) {
|
|
1085
|
+
s = logit_softcap * tanh(s / logit_softcap);
|
|
1086
|
+
}
|
|
1087
|
+
score_shared = s;
|
|
1088
|
+
}
|
|
1089
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1090
|
+
|
|
1091
|
+
const float s = score_shared;
|
|
1092
|
+
const float m_new = fmax(m_i, s);
|
|
1093
|
+
const float alpha = native_exp(m_i - m_new);
|
|
1094
|
+
const float beta = native_exp(s - m_new);
|
|
1095
|
+
|
|
1096
|
+
o_val = o_val * alpha + beta * convert_float(v_tile[j * DV + tid]);
|
|
1097
|
+
l_i = l_i * alpha + beta;
|
|
1098
|
+
m_i = m_new;
|
|
1099
|
+
}
|
|
1100
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// Fold attention sinks into the running (m, l, o), if present.
|
|
1104
|
+
if (sinks_void != NULL) {
|
|
1105
|
+
const global float * sinks_ptr =
|
|
1106
|
+
(const global float *) ((const global char *) sinks_void + sinks_offset);
|
|
1107
|
+
const float m_sink = sinks_ptr[head_idx];
|
|
1108
|
+
const float m_new = fmax(m_i, m_sink);
|
|
1109
|
+
const float alpha = native_exp(m_i - m_new);
|
|
1110
|
+
const float beta = native_exp(m_sink - m_new);
|
|
1111
|
+
o_val = o_val * alpha;
|
|
1112
|
+
l_i = l_i * alpha + beta;
|
|
1113
|
+
m_i = m_new;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
const float l_inv = (l_i > 0.0f) ? (1.0f / l_i) : 0.0f;
|
|
1117
|
+
global float * o_row = (global float *) ((global char *) o_void + o_offset +
|
|
1118
|
+
batch_idx * o_nb3 + head_idx * o_nb1 +
|
|
1119
|
+
(ulong) q_idx * o_nb2);
|
|
1120
|
+
o_row[tid] = o_val * l_inv;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
// flash_attn_f32_f16_q1_local_mq_split
|
|
1124
|
+
|
|
1125
|
+
#define LMQ_WG 64
|
|
1126
|
+
#define LMQ_KC 32
|
|
1127
|
+
#define LMQ_DPL 2 // DK / LMQ_WG at DK=128
|
|
1128
|
+
|
|
1129
|
+
#endif // !FA_MQ_ONLY (q1_local_tile)
|
|
1130
|
+
#ifndef MQ_GQA
|
|
1131
|
+
#define MQ_GQA 4
|
|
1132
|
+
#endif
|
|
1133
|
+
|
|
1134
|
+
#ifndef FA_PARTIAL_FLOATS
|
|
1135
|
+
#define FA_PARTIAL_FLOATS (2 + DV)
|
|
1136
|
+
#endif
|
|
1137
|
+
|
|
1138
|
+
#ifndef FA_MQ_ONLY // q1_local_mq_split excluded from the MQ-only (g8) program
|
|
1139
|
+
REQD_SUBGROUP_SIZE_64
|
|
1140
|
+
__kernel void flash_attn_f32_f16_q1_local_mq_split(
|
|
1141
|
+
const global void * q_void, ulong q_offset,
|
|
1142
|
+
const global void * k_void, ulong k_offset,
|
|
1143
|
+
const global void * v_void, ulong v_offset,
|
|
1144
|
+
const float scale,
|
|
1145
|
+
const int n_q,
|
|
1146
|
+
const int n_kv,
|
|
1147
|
+
const int n_head,
|
|
1148
|
+
const ulong q_nb1, const ulong q_nb2, const ulong q_nb3,
|
|
1149
|
+
const ulong k_nb1, const ulong k_nb2, const ulong k_nb3,
|
|
1150
|
+
const ulong v_nb1, const ulong v_nb2, const ulong v_nb3,
|
|
1151
|
+
const float max_bias,
|
|
1152
|
+
const float m0,
|
|
1153
|
+
const float m1,
|
|
1154
|
+
const int n_head_log2,
|
|
1155
|
+
const float logit_softcap,
|
|
1156
|
+
const int n_head_kv,
|
|
1157
|
+
const global void * mask_void,
|
|
1158
|
+
const ulong mask_offset,
|
|
1159
|
+
const ulong mask_nb1,
|
|
1160
|
+
const ulong mask_nb2,
|
|
1161
|
+
const ulong mask_nb3,
|
|
1162
|
+
const int mask_ne2,
|
|
1163
|
+
const int mask_ne3,
|
|
1164
|
+
global float * partial_void,
|
|
1165
|
+
const int n_splits,
|
|
1166
|
+
const int kv_per_split
|
|
1167
|
+
) {
|
|
1168
|
+
const int tid = get_local_id(0); // 0..LMQ_WG-1
|
|
1169
|
+
const int kvhead_batch_idx = get_global_id(1);
|
|
1170
|
+
const int split_q_idx = get_global_id(2);
|
|
1171
|
+
const int split_idx = split_q_idx % n_splits;
|
|
1172
|
+
const int q_idx = split_q_idx / n_splits;
|
|
1173
|
+
|
|
1174
|
+
const int batch_idx = kvhead_batch_idx / n_head_kv;
|
|
1175
|
+
const int head_kv_idx = kvhead_batch_idx % n_head_kv;
|
|
1176
|
+
|
|
1177
|
+
const int kv_start = split_idx * kv_per_split;
|
|
1178
|
+
const int kv_end = min(kv_start + kv_per_split, n_kv);
|
|
1179
|
+
|
|
1180
|
+
const ulong record_stride = (ulong) FA_PARTIAL_FLOATS;
|
|
1181
|
+
|
|
1182
|
+
if (kv_start >= kv_end) {
|
|
1183
|
+
// Empty split — write sentinel for each Q-head so merge treats it as 0.
|
|
1184
|
+
if (tid == 0) {
|
|
1185
|
+
#pragma unroll
|
|
1186
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1187
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1188
|
+
const ulong rec_idx = ((((ulong) batch_idx * n_head + head_idx) * n_q + q_idx)
|
|
1189
|
+
* n_splits + split_idx);
|
|
1190
|
+
global float * rec = partial_void + rec_idx * record_stride;
|
|
1191
|
+
rec[0] = FA_M_INIT;
|
|
1192
|
+
rec[1] = 0.0f;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
const global char * q_base = (const global char *) q_void + q_offset;
|
|
1199
|
+
const global char * k_base = (const global char *) k_void + k_offset;
|
|
1200
|
+
const global char * v_base = (const global char *) v_void + v_offset;
|
|
1201
|
+
|
|
1202
|
+
// Stage MQ_GQA Q rows in __local (MQ_GQA × DK floats).
|
|
1203
|
+
__local float q_shared[MQ_GQA * DK];
|
|
1204
|
+
for (int i = tid; i < MQ_GQA * DK; i += LMQ_WG) {
|
|
1205
|
+
const int h = i / DK;
|
|
1206
|
+
const int d = i % DK;
|
|
1207
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1208
|
+
const ulong q_row_off = batch_idx * q_nb3 + head_idx * q_nb2 + (ulong) q_idx * q_nb1;
|
|
1209
|
+
const global float * q_ptr = (const global float *) (q_base + q_row_off);
|
|
1210
|
+
q_shared[h * DK + d] = q_ptr[d];
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
// K/V tile staging buffers (16 KB combined at DK=DV=128 KC=32).
|
|
1214
|
+
__local half k_tile[LMQ_KC * DK];
|
|
1215
|
+
__local half v_tile[LMQ_KC * DV];
|
|
1216
|
+
|
|
1217
|
+
// Per-h state held in private registers.
|
|
1218
|
+
float o_acc[MQ_GQA][LMQ_DPL];
|
|
1219
|
+
float m_i[MQ_GQA];
|
|
1220
|
+
float l_i[MQ_GQA];
|
|
1221
|
+
float slope[MQ_GQA];
|
|
1222
|
+
#pragma unroll
|
|
1223
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1224
|
+
m_i[h] = FA_M_INIT;
|
|
1225
|
+
l_i[h] = 0.0f;
|
|
1226
|
+
slope[h] = get_alibi_slope(max_bias, head_kv_idx * MQ_GQA + h, n_head_log2, m0, m1);
|
|
1227
|
+
#pragma unroll
|
|
1228
|
+
for (int p = 0; p < LMQ_DPL; ++p) o_acc[h][p] = 0.0f;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
// Per-h mask pointers.
|
|
1232
|
+
const global char * mask_base[MQ_GQA];
|
|
1233
|
+
if (mask_void != NULL) {
|
|
1234
|
+
const int mask_batch_idx = batch_idx % mask_ne3;
|
|
1235
|
+
const global char * mask_base_b = (const global char *) mask_void + mask_offset +
|
|
1236
|
+
mask_batch_idx * mask_nb3 +
|
|
1237
|
+
(ulong) q_idx * mask_nb1;
|
|
1238
|
+
#pragma unroll
|
|
1239
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1240
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1241
|
+
const int mask_head_idx = head_idx % mask_ne2;
|
|
1242
|
+
mask_base[h] = mask_base_b + mask_head_idx * mask_nb2;
|
|
1243
|
+
}
|
|
1244
|
+
} else {
|
|
1245
|
+
#pragma unroll
|
|
1246
|
+
for (int h = 0; h < MQ_GQA; ++h) mask_base[h] = NULL;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
barrier(CLK_LOCAL_MEM_FENCE); // Ensure Q staged before first dot.
|
|
1250
|
+
|
|
1251
|
+
for (int kb = kv_start; kb < kv_end; kb += LMQ_KC) {
|
|
1252
|
+
const int tile_len = min((int) LMQ_KC, kv_end - kb);
|
|
1253
|
+
|
|
1254
|
+
// Cooperative load K + V tile.
|
|
1255
|
+
for (int i = tid; i < tile_len * DK; i += LMQ_WG) {
|
|
1256
|
+
const int j = i / DK;
|
|
1257
|
+
const int d = i % DK;
|
|
1258
|
+
const int kv_idx = kb + j;
|
|
1259
|
+
const global char * k_row = k_base + batch_idx * k_nb3 + head_kv_idx * k_nb2 + (ulong) kv_idx * k_nb1;
|
|
1260
|
+
const global char * v_row = v_base + batch_idx * v_nb3 + head_kv_idx * v_nb2 + (ulong) kv_idx * v_nb1;
|
|
1261
|
+
k_tile[j * DK + d] = ((const global half *) k_row)[d];
|
|
1262
|
+
v_tile[j * DV + d] = ((const global half *) v_row)[d];
|
|
1263
|
+
}
|
|
1264
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1265
|
+
|
|
1266
|
+
// Process each cache row in the tile.
|
|
1267
|
+
for (int j = 0; j < tile_len; ++j) {
|
|
1268
|
+
const int kv_idx = kb + j;
|
|
1269
|
+
|
|
1270
|
+
// Dot product per h: lane owns LMQ_DPL D-elements at (tid*LMQ_DPL..).
|
|
1271
|
+
float score[MQ_GQA];
|
|
1272
|
+
#pragma unroll
|
|
1273
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1274
|
+
float contrib = 0.0f;
|
|
1275
|
+
#pragma unroll
|
|
1276
|
+
for (int p = 0; p < LMQ_DPL; ++p) {
|
|
1277
|
+
const int d = tid * LMQ_DPL + p;
|
|
1278
|
+
contrib += q_shared[h * DK + d] * (float) k_tile[j * DK + d];
|
|
1279
|
+
}
|
|
1280
|
+
float s = sub_group_reduce_add(contrib) * scale;
|
|
1281
|
+
if (mask_base[h] != NULL) {
|
|
1282
|
+
const global MASK_DATA_TYPE * mask_ptr = (const global MASK_DATA_TYPE *) mask_base[h];
|
|
1283
|
+
s += slope[h] * (float) mask_ptr[kv_idx];
|
|
1284
|
+
}
|
|
1285
|
+
if (logit_softcap > 0.0f) {
|
|
1286
|
+
s = logit_softcap * tanh(s / logit_softcap);
|
|
1287
|
+
}
|
|
1288
|
+
score[h] = s;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// Online softmax update + V accumulation per h.
|
|
1292
|
+
float p_h[MQ_GQA];
|
|
1293
|
+
float sp_h[MQ_GQA];
|
|
1294
|
+
#pragma unroll
|
|
1295
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1296
|
+
const float m_new = fmax(m_i[h], score[h]);
|
|
1297
|
+
sp_h[h] = native_exp(m_i[h] - m_new);
|
|
1298
|
+
p_h[h] = native_exp(score[h] - m_new);
|
|
1299
|
+
l_i[h] = l_i[h] * sp_h[h] + p_h[h];
|
|
1300
|
+
m_i[h] = m_new;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
#pragma unroll
|
|
1304
|
+
for (int p = 0; p < LMQ_DPL; ++p) {
|
|
1305
|
+
const int d = tid * LMQ_DPL + p;
|
|
1306
|
+
const float v_val = (float) v_tile[j * DV + d];
|
|
1307
|
+
#pragma unroll
|
|
1308
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1309
|
+
o_acc[h][p] = o_acc[h][p] * sp_h[h] + p_h[h] * v_val;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
barrier(CLK_LOCAL_MEM_FENCE); // Before next tile load overwrites k/v_tile.
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// write partial records: one per (h, split)
|
|
1317
|
+
#pragma unroll
|
|
1318
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1319
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1320
|
+
const ulong rec_idx = ((((ulong) batch_idx * n_head + head_idx) * n_q + q_idx)
|
|
1321
|
+
* n_splits + split_idx);
|
|
1322
|
+
global float * rec = partial_void + rec_idx * record_stride;
|
|
1323
|
+
global float * rec_o = rec + 2;
|
|
1324
|
+
|
|
1325
|
+
if (tid == 0) {
|
|
1326
|
+
rec[0] = m_i[h];
|
|
1327
|
+
rec[1] = l_i[h];
|
|
1328
|
+
}
|
|
1329
|
+
#pragma unroll
|
|
1330
|
+
for (int p = 0; p < LMQ_DPL; ++p) {
|
|
1331
|
+
const int d = tid * LMQ_DPL + p;
|
|
1332
|
+
rec_o[d] = o_acc[h][p];
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
#endif // !FA_MQ_ONLY (q1_local_mq_split)
|
|
1338
|
+
#ifndef MQ_NSG
|
|
1339
|
+
#define MQ_NSG 4
|
|
1340
|
+
#endif
|
|
1341
|
+
#define MQ_WG_SIZE (Q1_WG_SIZE * MQ_NSG)
|
|
1342
|
+
|
|
1343
|
+
REQD_SUBGROUP_SIZE_64
|
|
1344
|
+
__kernel void flash_attn_f32_f16_q1_vec_mq(
|
|
1345
|
+
const global void * q_void, ulong q_offset,
|
|
1346
|
+
const global void * k_void, ulong k_offset,
|
|
1347
|
+
const global void * v_void, ulong v_offset,
|
|
1348
|
+
global void * o_void, ulong o_offset,
|
|
1349
|
+
const float scale,
|
|
1350
|
+
const int n_q,
|
|
1351
|
+
const int n_kv,
|
|
1352
|
+
const int is_causal,
|
|
1353
|
+
const int n_head,
|
|
1354
|
+
const ulong q_nb1, const ulong q_nb2, const ulong q_nb3,
|
|
1355
|
+
const ulong k_nb1, const ulong k_nb2, const ulong k_nb3,
|
|
1356
|
+
const ulong v_nb1, const ulong v_nb2, const ulong v_nb3,
|
|
1357
|
+
const ulong o_nb1, const ulong o_nb2, const ulong o_nb3,
|
|
1358
|
+
const float max_bias,
|
|
1359
|
+
const float m0,
|
|
1360
|
+
const float m1,
|
|
1361
|
+
const int n_head_log2,
|
|
1362
|
+
const float logit_softcap,
|
|
1363
|
+
const int n_head_kv,
|
|
1364
|
+
const global void* mask_void,
|
|
1365
|
+
const ulong mask_offset,
|
|
1366
|
+
const ulong mask_nb1,
|
|
1367
|
+
const ulong mask_nb2,
|
|
1368
|
+
const ulong mask_nb3,
|
|
1369
|
+
const int mask_ne2,
|
|
1370
|
+
const int mask_ne3,
|
|
1371
|
+
const global void* sinks_void,
|
|
1372
|
+
const ulong sinks_offset
|
|
1373
|
+
) {
|
|
1374
|
+
const int tid = get_local_id(0);
|
|
1375
|
+
const int sgid = tid / Q1_WG_SIZE; // subgroup 0..MQ_NSG-1
|
|
1376
|
+
const int tid_sg = tid % Q1_WG_SIZE; // lane 0..63
|
|
1377
|
+
const int kvhead_batch_idx = get_global_id(1);
|
|
1378
|
+
|
|
1379
|
+
const int batch_idx = kvhead_batch_idx / n_head_kv;
|
|
1380
|
+
const int head_kv_idx = kvhead_batch_idx % n_head_kv;
|
|
1381
|
+
|
|
1382
|
+
const global char * q_base = (const global char *) q_void + q_offset;
|
|
1383
|
+
const global char * k_base = (const global char *) k_void + k_offset;
|
|
1384
|
+
const global char * v_base = (const global char *) v_void + v_offset;
|
|
1385
|
+
global char * o_base = (global char *) o_void + o_offset;
|
|
1386
|
+
|
|
1387
|
+
__local ACC_TYPE4 q_shared[MQ_GQA * DK_VEC];
|
|
1388
|
+
for (int i = tid; i < MQ_GQA * DK_VEC; i += MQ_WG_SIZE) {
|
|
1389
|
+
const int h = i / DK_VEC;
|
|
1390
|
+
const int k = i % DK_VEC;
|
|
1391
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1392
|
+
const ulong q_row_offset = batch_idx * q_nb3 + head_idx * q_nb2;
|
|
1393
|
+
const global Q_DATA_TYPE4 * q_ptr = (const global Q_DATA_TYPE4 *) (q_base + q_row_offset);
|
|
1394
|
+
q_shared[h * DK_VEC + k] = CONVERT_Q_ACC4(q_ptr[k]);
|
|
1395
|
+
}
|
|
1396
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1397
|
+
|
|
1398
|
+
// per-h ALiBi slope
|
|
1399
|
+
float slope[MQ_GQA];
|
|
1400
|
+
#pragma unroll
|
|
1401
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1402
|
+
slope[h] = get_alibi_slope(max_bias, head_kv_idx * MQ_GQA + h, n_head_log2, m0, m1);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// per-h mask row pointer
|
|
1406
|
+
const global char * mask_base[MQ_GQA];
|
|
1407
|
+
if (mask_void != NULL) {
|
|
1408
|
+
const int mask_batch_idx = batch_idx % mask_ne3;
|
|
1409
|
+
const global char * mask_base_b = (const global char *) mask_void + mask_offset +
|
|
1410
|
+
mask_batch_idx * mask_nb3;
|
|
1411
|
+
#pragma unroll
|
|
1412
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1413
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1414
|
+
const int mask_head_idx = head_idx % mask_ne2;
|
|
1415
|
+
mask_base[h] = mask_base_b + mask_head_idx * mask_nb2;
|
|
1416
|
+
}
|
|
1417
|
+
} else {
|
|
1418
|
+
#pragma unroll
|
|
1419
|
+
for (int h = 0; h < MQ_GQA; ++h) mask_base[h] = NULL;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
const global ACC_TYPE * sinks_ptr = NULL;
|
|
1423
|
+
if (sinks_void != NULL) {
|
|
1424
|
+
sinks_ptr = (const global ACC_TYPE *) ((const global char *) sinks_void + sinks_offset);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
// per-thread per-h DV slice.
|
|
1428
|
+
ACC_TYPE4 o_acc[MQ_GQA][Q1V_DV_PER_THREAD];
|
|
1429
|
+
ACC_TYPE m_i[MQ_GQA];
|
|
1430
|
+
ACC_TYPE l_i[MQ_GQA];
|
|
1431
|
+
#pragma unroll
|
|
1432
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1433
|
+
m_i[h] = FA_M_INIT;
|
|
1434
|
+
l_i[h] = 0.0f;
|
|
1435
|
+
#pragma unroll
|
|
1436
|
+
for (int i = 0; i < Q1V_DV_PER_THREAD; ++i) o_acc[h][i] = (ACC_TYPE4)(0.0f);
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// each subgroup independently sweeps its slice of n_kv.
|
|
1440
|
+
const int kv_per_sg = (n_kv + MQ_NSG - 1) / MQ_NSG;
|
|
1441
|
+
const int kv_start = sgid * kv_per_sg;
|
|
1442
|
+
const int kv_end = min(n_kv, kv_start + kv_per_sg);
|
|
1443
|
+
|
|
1444
|
+
for (int k_idx = kv_start; k_idx < kv_end; ++k_idx) {
|
|
1445
|
+
const ulong k_row_off = batch_idx * k_nb3 + head_kv_idx * k_nb2 + k_idx * k_nb1;
|
|
1446
|
+
const ulong v_row_off = batch_idx * v_nb3 + head_kv_idx * v_nb2 + k_idx * v_nb1;
|
|
1447
|
+
const global KV_DATA_TYPE4 * k_ptr = (const global KV_DATA_TYPE4 *) (k_base + k_row_off);
|
|
1448
|
+
const global KV_DATA_TYPE4 * v_ptr = (const global KV_DATA_TYPE4 *) (v_base + v_row_off);
|
|
1449
|
+
|
|
1450
|
+
// Q*K^T: load each K stride once, dot against all MQ_GQA Q rows.
|
|
1451
|
+
ACC_TYPE4 dot4[MQ_GQA];
|
|
1452
|
+
#pragma unroll
|
|
1453
|
+
for (int h = 0; h < MQ_GQA; ++h) dot4[h] = (ACC_TYPE4)(0.0f);
|
|
1454
|
+
for (int k = tid_sg; k < DK_VEC; k += Q1_WG_SIZE) {
|
|
1455
|
+
const ACC_TYPE4 k_vec = CONVERT_KV_ACC4(k_ptr[k]);
|
|
1456
|
+
#pragma unroll
|
|
1457
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1458
|
+
dot4[h] = mad(q_shared[h * DK_VEC + k], k_vec, dot4[h]);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
ACC_TYPE score[MQ_GQA];
|
|
1463
|
+
#pragma unroll
|
|
1464
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1465
|
+
const ACC_TYPE dot_partial = dot4[h].s0 + dot4[h].s1 + dot4[h].s2 + dot4[h].s3;
|
|
1466
|
+
ACC_TYPE s = sub_group_reduce_add(dot_partial) * scale;
|
|
1467
|
+
if (mask_base[h] != NULL) {
|
|
1468
|
+
const global MASK_DATA_TYPE * mask_ptr = (const global MASK_DATA_TYPE *) mask_base[h];
|
|
1469
|
+
s += slope[h] * (ACC_TYPE) mask_ptr[k_idx];
|
|
1470
|
+
}
|
|
1471
|
+
if (logit_softcap > 0.0f) {
|
|
1472
|
+
s = logit_softcap * tanh(s / logit_softcap);
|
|
1473
|
+
}
|
|
1474
|
+
score[h] = s;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// FA-2 online softmax update — V load amortized across MQ_GQA heads.
|
|
1478
|
+
// p, scale_prev are computed per h; the V vector is loaded once
|
|
1479
|
+
// per dv stride and reused MQ_GQA times.
|
|
1480
|
+
ACC_TYPE p_h[MQ_GQA];
|
|
1481
|
+
ACC_TYPE sp_h[MQ_GQA];
|
|
1482
|
+
#pragma unroll
|
|
1483
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1484
|
+
const ACC_TYPE m_new = max(m_i[h], score[h]);
|
|
1485
|
+
sp_h[h] = native_exp(m_i[h] - m_new);
|
|
1486
|
+
p_h[h] = native_exp(score[h] - m_new);
|
|
1487
|
+
l_i[h] = l_i[h] * sp_h[h] + p_h[h];
|
|
1488
|
+
m_i[h] = m_new;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
int idx = 0;
|
|
1492
|
+
for (int dv_idx = tid_sg; dv_idx < DV_VEC; dv_idx += Q1_WG_SIZE, ++idx) {
|
|
1493
|
+
const ACC_TYPE4 v_vec = CONVERT_KV_ACC4(v_ptr[dv_idx]);
|
|
1494
|
+
#pragma unroll
|
|
1495
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1496
|
+
o_acc[h][idx] = mad(p_h[h], v_vec, o_acc[h][idx] * sp_h[h]);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
// cross subgroup merge
|
|
1502
|
+
__local ACC_TYPE sg_m[MQ_GQA][MQ_NSG];
|
|
1503
|
+
__local ACC_TYPE sg_l[MQ_GQA][MQ_NSG];
|
|
1504
|
+
__local ACC_TYPE4 sg_o[MQ_NSG][DV_VEC];
|
|
1505
|
+
|
|
1506
|
+
if (tid_sg == 0) {
|
|
1507
|
+
#pragma unroll
|
|
1508
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1509
|
+
sg_m[h][sgid] = m_i[h];
|
|
1510
|
+
sg_l[h][sgid] = l_i[h];
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
#pragma unroll
|
|
1515
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1516
|
+
// each subgroup publishes its o_acc slice for head h.
|
|
1517
|
+
{
|
|
1518
|
+
int idx = 0;
|
|
1519
|
+
for (int dv_idx = tid_sg; dv_idx < DV_VEC; dv_idx += Q1_WG_SIZE, ++idx) {
|
|
1520
|
+
sg_o[sgid][dv_idx] = o_acc[h][idx];
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1524
|
+
|
|
1525
|
+
if (sgid == 0) {
|
|
1526
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1527
|
+
|
|
1528
|
+
ACC_TYPE m_final = sg_m[h][0];
|
|
1529
|
+
#pragma unroll
|
|
1530
|
+
for (int s = 1; s < MQ_NSG; ++s) {
|
|
1531
|
+
m_final = max(m_final, sg_m[h][s]);
|
|
1532
|
+
}
|
|
1533
|
+
if (sinks_ptr != NULL) {
|
|
1534
|
+
m_final = max(m_final, sinks_ptr[head_idx]);
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
ACC_TYPE l_final = 0.0f;
|
|
1538
|
+
#pragma unroll
|
|
1539
|
+
for (int s = 0; s < MQ_NSG; ++s) {
|
|
1540
|
+
l_final += sg_l[h][s] * native_exp(sg_m[h][s] - m_final);
|
|
1541
|
+
}
|
|
1542
|
+
if (sinks_ptr != NULL) {
|
|
1543
|
+
l_final += native_exp(sinks_ptr[head_idx] - m_final);
|
|
1544
|
+
}
|
|
1545
|
+
const ACC_TYPE l_inv = (l_final > 0.0f) ? (1.0f / l_final) : 0.0f;
|
|
1546
|
+
|
|
1547
|
+
const ulong o_row_offset = batch_idx * o_nb3 + head_idx * o_nb1;
|
|
1548
|
+
global O_DATA_TYPE4 * o_row = (global O_DATA_TYPE4 *) (o_base + o_row_offset);
|
|
1549
|
+
for (int dv_idx = tid_sg; dv_idx < DV_VEC; dv_idx += Q1_WG_SIZE) {
|
|
1550
|
+
ACC_TYPE4 o_merged = (ACC_TYPE4)(0.0f);
|
|
1551
|
+
#pragma unroll
|
|
1552
|
+
for (int s = 0; s < MQ_NSG; ++s) {
|
|
1553
|
+
const ACC_TYPE alpha = native_exp(sg_m[h][s] - m_final);
|
|
1554
|
+
o_merged = mad((ACC_TYPE4)(alpha), sg_o[s][dv_idx], o_merged);
|
|
1555
|
+
}
|
|
1556
|
+
o_row[dv_idx] = CONVERT_O_DATA4(o_merged * l_inv);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
// Barrier guards next h's overwrite of sg_o.
|
|
1560
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
#ifndef MQ_NSG_SPLIT
|
|
1565
|
+
#define MQ_NSG_SPLIT 4
|
|
1566
|
+
#endif
|
|
1567
|
+
#define MQ_SPLIT_WG_SIZE (Q1_WG_SIZE * MQ_NSG_SPLIT)
|
|
1568
|
+
|
|
1569
|
+
#ifndef FA_PARTIAL_FLOATS
|
|
1570
|
+
#define FA_PARTIAL_FLOATS (2 + DV)
|
|
1571
|
+
#endif
|
|
1572
|
+
|
|
1573
|
+
REQD_SUBGROUP_SIZE_64
|
|
1574
|
+
__kernel void flash_attn_f32_f16_q1_vec_mq_split(
|
|
1575
|
+
const global void * q_void, ulong q_offset,
|
|
1576
|
+
const global void * k_void, ulong k_offset,
|
|
1577
|
+
const global void * v_void, ulong v_offset,
|
|
1578
|
+
const float scale,
|
|
1579
|
+
const int n_q,
|
|
1580
|
+
const int n_kv,
|
|
1581
|
+
const int n_head,
|
|
1582
|
+
const ulong q_nb1, const ulong q_nb2, const ulong q_nb3,
|
|
1583
|
+
const ulong k_nb1, const ulong k_nb2, const ulong k_nb3,
|
|
1584
|
+
const ulong v_nb1, const ulong v_nb2, const ulong v_nb3,
|
|
1585
|
+
const float max_bias,
|
|
1586
|
+
const float m0,
|
|
1587
|
+
const float m1,
|
|
1588
|
+
const int n_head_log2,
|
|
1589
|
+
const float logit_softcap,
|
|
1590
|
+
const int n_head_kv,
|
|
1591
|
+
const global void * mask_void,
|
|
1592
|
+
const ulong mask_offset,
|
|
1593
|
+
const ulong mask_nb1,
|
|
1594
|
+
const ulong mask_nb2,
|
|
1595
|
+
const ulong mask_nb3,
|
|
1596
|
+
const int mask_ne2,
|
|
1597
|
+
const int mask_ne3,
|
|
1598
|
+
global float * partial_void,
|
|
1599
|
+
const int n_splits,
|
|
1600
|
+
const int kv_per_split
|
|
1601
|
+
) {
|
|
1602
|
+
const int tid = get_local_id(0);
|
|
1603
|
+
const int sgid = tid / Q1_WG_SIZE;
|
|
1604
|
+
const int tid_sg = tid % Q1_WG_SIZE;
|
|
1605
|
+
const int kvhead_batch_idx = get_global_id(1);
|
|
1606
|
+
const int split_q_idx = get_global_id(2);
|
|
1607
|
+
const int split_idx = split_q_idx % n_splits;
|
|
1608
|
+
const int q_idx = split_q_idx / n_splits;
|
|
1609
|
+
|
|
1610
|
+
const int batch_idx = kvhead_batch_idx / n_head_kv;
|
|
1611
|
+
const int head_kv_idx = kvhead_batch_idx % n_head_kv;
|
|
1612
|
+
|
|
1613
|
+
const int kv_start = split_idx * kv_per_split;
|
|
1614
|
+
const int kv_end = min(kv_start + kv_per_split, n_kv);
|
|
1615
|
+
|
|
1616
|
+
const ulong record_stride = (ulong) FA_PARTIAL_FLOATS;
|
|
1617
|
+
|
|
1618
|
+
if (kv_start >= kv_end) {
|
|
1619
|
+
// write sentinel for each of the MQ_GQA Q-heads so the
|
|
1620
|
+
// merge pass treats this slot as dropped
|
|
1621
|
+
if (tid == 0) {
|
|
1622
|
+
#pragma unroll
|
|
1623
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1624
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1625
|
+
const ulong rec_idx = ((((ulong) batch_idx * n_head + head_idx) * n_q + q_idx)
|
|
1626
|
+
* n_splits + split_idx);
|
|
1627
|
+
global float * rec = partial_void + rec_idx * record_stride;
|
|
1628
|
+
rec[0] = FA_M_INIT;
|
|
1629
|
+
rec[1] = 0.0f;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
const global char * q_base = (const global char *) q_void + q_offset;
|
|
1636
|
+
const global char * k_base = (const global char *) k_void + k_offset;
|
|
1637
|
+
const global char * v_base = (const global char *) v_void + v_offset;
|
|
1638
|
+
|
|
1639
|
+
// stage MQ_GQA Q rows in __local once (uniform across WG)
|
|
1640
|
+
__local ACC_TYPE4 q_shared[MQ_GQA * DK_VEC];
|
|
1641
|
+
for (int i = tid; i < MQ_GQA * DK_VEC; i += MQ_SPLIT_WG_SIZE) {
|
|
1642
|
+
const int h = i / DK_VEC;
|
|
1643
|
+
const int k = i % DK_VEC;
|
|
1644
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1645
|
+
const ulong q_row_offset = batch_idx * q_nb3 + head_idx * q_nb2 + (ulong) q_idx * q_nb1;
|
|
1646
|
+
const global Q_DATA_TYPE4 * q_ptr = (const global Q_DATA_TYPE4 *) (q_base + q_row_offset);
|
|
1647
|
+
q_shared[h * DK_VEC + k] = CONVERT_Q_ACC4(q_ptr[k]);
|
|
1648
|
+
}
|
|
1649
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1650
|
+
|
|
1651
|
+
float slope[MQ_GQA];
|
|
1652
|
+
#pragma unroll
|
|
1653
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1654
|
+
slope[h] = get_alibi_slope(max_bias, head_kv_idx * MQ_GQA + h, n_head_log2, m0, m1);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
const global char * mask_base[MQ_GQA];
|
|
1658
|
+
if (mask_void != NULL) {
|
|
1659
|
+
const int mask_batch_idx = batch_idx % mask_ne3;
|
|
1660
|
+
const global char * mask_base_b = (const global char *) mask_void + mask_offset +
|
|
1661
|
+
mask_batch_idx * mask_nb3 +
|
|
1662
|
+
(ulong) q_idx * mask_nb1;
|
|
1663
|
+
#pragma unroll
|
|
1664
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1665
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1666
|
+
const int mask_head_idx = head_idx % mask_ne2;
|
|
1667
|
+
mask_base[h] = mask_base_b + mask_head_idx * mask_nb2;
|
|
1668
|
+
}
|
|
1669
|
+
} else {
|
|
1670
|
+
#pragma unroll
|
|
1671
|
+
for (int h = 0; h < MQ_GQA; ++h) mask_base[h] = NULL;
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
ACC_TYPE4 o_acc[MQ_GQA][Q1V_DV_PER_THREAD];
|
|
1675
|
+
ACC_TYPE m_i[MQ_GQA];
|
|
1676
|
+
ACC_TYPE l_i[MQ_GQA];
|
|
1677
|
+
#pragma unroll
|
|
1678
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1679
|
+
m_i[h] = FA_M_INIT;
|
|
1680
|
+
l_i[h] = 0.0f;
|
|
1681
|
+
#pragma unroll
|
|
1682
|
+
for (int i = 0; i < Q1V_DV_PER_THREAD; ++i) o_acc[h][i] = (ACC_TYPE4)(0.0f);
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
// each subgroup independently sweeps its slice of the split's kv range.
|
|
1686
|
+
const int kv_len = kv_end - kv_start;
|
|
1687
|
+
const int kv_per_sg = (kv_len + MQ_NSG_SPLIT - 1) / MQ_NSG_SPLIT;
|
|
1688
|
+
const int kv_lo = kv_start + sgid * kv_per_sg;
|
|
1689
|
+
const int kv_hi = min(kv_end, kv_lo + kv_per_sg);
|
|
1690
|
+
|
|
1691
|
+
for (int k_idx = kv_lo; k_idx < kv_hi; ++k_idx) {
|
|
1692
|
+
const ulong k_row_off = batch_idx * k_nb3 + head_kv_idx * k_nb2 + k_idx * k_nb1;
|
|
1693
|
+
const ulong v_row_off = batch_idx * v_nb3 + head_kv_idx * v_nb2 + k_idx * v_nb1;
|
|
1694
|
+
const global KV_DATA_TYPE4 * k_ptr = (const global KV_DATA_TYPE4 *) (k_base + k_row_off);
|
|
1695
|
+
const global KV_DATA_TYPE4 * v_ptr = (const global KV_DATA_TYPE4 *) (v_base + v_row_off);
|
|
1696
|
+
|
|
1697
|
+
ACC_TYPE4 dot4[MQ_GQA];
|
|
1698
|
+
#pragma unroll
|
|
1699
|
+
for (int h = 0; h < MQ_GQA; ++h) dot4[h] = (ACC_TYPE4)(0.0f);
|
|
1700
|
+
for (int k = tid_sg; k < DK_VEC; k += Q1_WG_SIZE) {
|
|
1701
|
+
const ACC_TYPE4 k_vec = CONVERT_KV_ACC4(k_ptr[k]);
|
|
1702
|
+
#pragma unroll
|
|
1703
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1704
|
+
dot4[h] = mad(q_shared[h * DK_VEC + k], k_vec, dot4[h]);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
ACC_TYPE score[MQ_GQA];
|
|
1709
|
+
#pragma unroll
|
|
1710
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1711
|
+
const ACC_TYPE dot_partial = dot4[h].s0 + dot4[h].s1 + dot4[h].s2 + dot4[h].s3;
|
|
1712
|
+
ACC_TYPE s = sub_group_reduce_add(dot_partial) * scale;
|
|
1713
|
+
if (mask_base[h] != NULL) {
|
|
1714
|
+
const global MASK_DATA_TYPE * mask_ptr = (const global MASK_DATA_TYPE *) mask_base[h];
|
|
1715
|
+
s += slope[h] * (ACC_TYPE) mask_ptr[k_idx];
|
|
1716
|
+
}
|
|
1717
|
+
if (logit_softcap > 0.0f) {
|
|
1718
|
+
s = logit_softcap * tanh(s / logit_softcap);
|
|
1719
|
+
}
|
|
1720
|
+
score[h] = s;
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
ACC_TYPE p_h[MQ_GQA];
|
|
1724
|
+
ACC_TYPE sp_h[MQ_GQA];
|
|
1725
|
+
#pragma unroll
|
|
1726
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1727
|
+
const ACC_TYPE m_new = max(m_i[h], score[h]);
|
|
1728
|
+
sp_h[h] = native_exp(m_i[h] - m_new);
|
|
1729
|
+
p_h[h] = native_exp(score[h] - m_new);
|
|
1730
|
+
l_i[h] = l_i[h] * sp_h[h] + p_h[h];
|
|
1731
|
+
m_i[h] = m_new;
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
int idx = 0;
|
|
1735
|
+
for (int dv_idx = tid_sg; dv_idx < DV_VEC; dv_idx += Q1_WG_SIZE, ++idx) {
|
|
1736
|
+
const ACC_TYPE4 v_vec = CONVERT_KV_ACC4(v_ptr[dv_idx]);
|
|
1737
|
+
#pragma unroll
|
|
1738
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1739
|
+
o_acc[h][idx] = mad(p_h[h], v_vec, o_acc[h][idx] * sp_h[h]);
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
// per-h cross-subgroup merge
|
|
1745
|
+
__local ACC_TYPE sg_m[MQ_GQA][MQ_NSG_SPLIT];
|
|
1746
|
+
__local ACC_TYPE sg_l[MQ_GQA][MQ_NSG_SPLIT];
|
|
1747
|
+
__local ACC_TYPE4 sg_o[MQ_NSG_SPLIT][DV_VEC];
|
|
1748
|
+
|
|
1749
|
+
if (tid_sg == 0) {
|
|
1750
|
+
#pragma unroll
|
|
1751
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1752
|
+
sg_m[h][sgid] = m_i[h];
|
|
1753
|
+
sg_l[h][sgid] = l_i[h];
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
#pragma unroll
|
|
1758
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1759
|
+
{
|
|
1760
|
+
int idx = 0;
|
|
1761
|
+
for (int dv_idx = tid_sg; dv_idx < DV_VEC; dv_idx += Q1_WG_SIZE, ++idx) {
|
|
1762
|
+
sg_o[sgid][dv_idx] = o_acc[h][idx];
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1766
|
+
|
|
1767
|
+
if (sgid == 0) {
|
|
1768
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1769
|
+
|
|
1770
|
+
// fold per-subgroup (m, l) into split-level (m_c, l_c)
|
|
1771
|
+
ACC_TYPE m_c = sg_m[h][0];
|
|
1772
|
+
#pragma unroll
|
|
1773
|
+
for (int s = 1; s < MQ_NSG_SPLIT; ++s) {
|
|
1774
|
+
m_c = max(m_c, sg_m[h][s]);
|
|
1775
|
+
}
|
|
1776
|
+
ACC_TYPE l_c = 0.0f;
|
|
1777
|
+
#pragma unroll
|
|
1778
|
+
for (int s = 0; s < MQ_NSG_SPLIT; ++s) {
|
|
1779
|
+
l_c += sg_l[h][s] * native_exp(sg_m[h][s] - m_c);
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
const ulong rec_idx = ((((ulong) batch_idx * n_head + head_idx) * n_q + q_idx)
|
|
1783
|
+
* n_splits + split_idx);
|
|
1784
|
+
global float * rec = partial_void + rec_idx * record_stride;
|
|
1785
|
+
global float4 * rec_o = (global float4 *) (rec + 2);
|
|
1786
|
+
|
|
1787
|
+
if (tid_sg == 0) {
|
|
1788
|
+
rec[0] = (float) m_c;
|
|
1789
|
+
rec[1] = (float) l_c;
|
|
1790
|
+
}
|
|
1791
|
+
// each thread writes its DV slice of the merged O.
|
|
1792
|
+
for (int dv_idx = tid_sg; dv_idx < DV_VEC; dv_idx += Q1_WG_SIZE) {
|
|
1793
|
+
ACC_TYPE4 o_merged = (ACC_TYPE4)(0.0f);
|
|
1794
|
+
#pragma unroll
|
|
1795
|
+
for (int s = 0; s < MQ_NSG_SPLIT; ++s) {
|
|
1796
|
+
const ACC_TYPE alpha = native_exp(sg_m[h][s] - m_c);
|
|
1797
|
+
o_merged = mad((ACC_TYPE4)(alpha), sg_o[s][dv_idx], o_merged);
|
|
1798
|
+
}
|
|
1799
|
+
rec_o[dv_idx] = o_merged;
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
REQD_SUBGROUP_SIZE_64
|
|
1807
|
+
__kernel void flash_attn_f32_f16_q1_vec_mq_split_k_img(
|
|
1808
|
+
const global void * q_void, ulong q_offset,
|
|
1809
|
+
__read_only image1d_buffer_t k_img,
|
|
1810
|
+
const global void * v_void, ulong v_offset,
|
|
1811
|
+
const float scale,
|
|
1812
|
+
const int n_q,
|
|
1813
|
+
const int n_kv,
|
|
1814
|
+
const int n_head,
|
|
1815
|
+
const ulong q_nb1, const ulong q_nb2, const ulong q_nb3,
|
|
1816
|
+
const ulong k_nb1, const ulong k_nb2, const ulong k_nb3,
|
|
1817
|
+
const ulong v_nb1, const ulong v_nb2, const ulong v_nb3,
|
|
1818
|
+
const float max_bias,
|
|
1819
|
+
const float m0,
|
|
1820
|
+
const float m1,
|
|
1821
|
+
const int n_head_log2,
|
|
1822
|
+
const float logit_softcap,
|
|
1823
|
+
const int n_head_kv,
|
|
1824
|
+
const global void * mask_void,
|
|
1825
|
+
const ulong mask_offset,
|
|
1826
|
+
const ulong mask_nb1,
|
|
1827
|
+
const ulong mask_nb2,
|
|
1828
|
+
const ulong mask_nb3,
|
|
1829
|
+
const int mask_ne2,
|
|
1830
|
+
const int mask_ne3,
|
|
1831
|
+
global float * partial_void,
|
|
1832
|
+
const int n_splits,
|
|
1833
|
+
const int kv_per_split
|
|
1834
|
+
) {
|
|
1835
|
+
const int tid = get_local_id(0);
|
|
1836
|
+
const int sgid = tid / Q1_WG_SIZE;
|
|
1837
|
+
const int tid_sg = tid % Q1_WG_SIZE;
|
|
1838
|
+
const int kvhead_batch_idx = get_global_id(1);
|
|
1839
|
+
const int split_q_idx = get_global_id(2);
|
|
1840
|
+
const int split_idx = split_q_idx % n_splits;
|
|
1841
|
+
const int q_idx = split_q_idx / n_splits;
|
|
1842
|
+
|
|
1843
|
+
const int batch_idx = kvhead_batch_idx / n_head_kv;
|
|
1844
|
+
const int head_kv_idx = kvhead_batch_idx % n_head_kv;
|
|
1845
|
+
|
|
1846
|
+
const int kv_start = split_idx * kv_per_split;
|
|
1847
|
+
const int kv_end = min(kv_start + kv_per_split, n_kv);
|
|
1848
|
+
|
|
1849
|
+
const ulong record_stride = (ulong) FA_PARTIAL_FLOATS;
|
|
1850
|
+
|
|
1851
|
+
if (kv_start >= kv_end) {
|
|
1852
|
+
if (tid == 0) {
|
|
1853
|
+
#pragma unroll
|
|
1854
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1855
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1856
|
+
const ulong rec_idx = ((((ulong) batch_idx * n_head + head_idx) * n_q + q_idx)
|
|
1857
|
+
* n_splits + split_idx);
|
|
1858
|
+
global float * rec = partial_void + rec_idx * record_stride;
|
|
1859
|
+
rec[0] = FA_M_INIT;
|
|
1860
|
+
rec[1] = 0.0f;
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
const global char * q_base = (const global char *) q_void + q_offset;
|
|
1867
|
+
const global char * v_base = (const global char *) v_void + v_offset;
|
|
1868
|
+
|
|
1869
|
+
__local ACC_TYPE4 q_shared[MQ_GQA * DK_VEC];
|
|
1870
|
+
for (int i = tid; i < MQ_GQA * DK_VEC; i += MQ_SPLIT_WG_SIZE) {
|
|
1871
|
+
const int h = i / DK_VEC;
|
|
1872
|
+
const int k = i % DK_VEC;
|
|
1873
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1874
|
+
const ulong q_row_offset = batch_idx * q_nb3 + head_idx * q_nb2 + (ulong) q_idx * q_nb1;
|
|
1875
|
+
const global Q_DATA_TYPE4 * q_ptr = (const global Q_DATA_TYPE4 *) (q_base + q_row_offset);
|
|
1876
|
+
q_shared[h * DK_VEC + k] = CONVERT_Q_ACC4(q_ptr[k]);
|
|
1877
|
+
}
|
|
1878
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
1879
|
+
|
|
1880
|
+
float slope[MQ_GQA];
|
|
1881
|
+
#pragma unroll
|
|
1882
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1883
|
+
slope[h] = get_alibi_slope(max_bias, head_kv_idx * MQ_GQA + h, n_head_log2, m0, m1);
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
const global char * mask_base[MQ_GQA];
|
|
1887
|
+
if (mask_void != NULL) {
|
|
1888
|
+
const int mask_batch_idx = batch_idx % mask_ne3;
|
|
1889
|
+
const global char * mask_base_b = (const global char *) mask_void + mask_offset +
|
|
1890
|
+
mask_batch_idx * mask_nb3 +
|
|
1891
|
+
(ulong) q_idx * mask_nb1;
|
|
1892
|
+
#pragma unroll
|
|
1893
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1894
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
1895
|
+
const int mask_head_idx = head_idx % mask_ne2;
|
|
1896
|
+
mask_base[h] = mask_base_b + mask_head_idx * mask_nb2;
|
|
1897
|
+
}
|
|
1898
|
+
} else {
|
|
1899
|
+
#pragma unroll
|
|
1900
|
+
for (int h = 0; h < MQ_GQA; ++h) mask_base[h] = NULL;
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
ACC_TYPE4 o_acc[MQ_GQA][Q1V_DV_PER_THREAD];
|
|
1904
|
+
ACC_TYPE m_i[MQ_GQA];
|
|
1905
|
+
ACC_TYPE l_i[MQ_GQA];
|
|
1906
|
+
#pragma unroll
|
|
1907
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1908
|
+
m_i[h] = FA_M_INIT;
|
|
1909
|
+
l_i[h] = 0.0f;
|
|
1910
|
+
#pragma unroll
|
|
1911
|
+
for (int i = 0; i < Q1V_DV_PER_THREAD; ++i) o_acc[h][i] = (ACC_TYPE4)(0.0f);
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
// K pitches in pixel units, pixel = 1 half4 = 8 B -> byte_stride >> 3.
|
|
1915
|
+
const int pitch_px_row = (int)(k_nb1 >> 3);
|
|
1916
|
+
const int pitch_px_head = (int)(k_nb2 >> 3);
|
|
1917
|
+
const int pitch_px_batch = (int)(k_nb3 >> 3);
|
|
1918
|
+
|
|
1919
|
+
const int kv_len = kv_end - kv_start;
|
|
1920
|
+
const int kv_per_sg = (kv_len + MQ_NSG_SPLIT - 1) / MQ_NSG_SPLIT;
|
|
1921
|
+
const int kv_lo = kv_start + sgid * kv_per_sg;
|
|
1922
|
+
const int kv_hi = min(kv_end, kv_lo + kv_per_sg);
|
|
1923
|
+
|
|
1924
|
+
for (int k_idx = kv_lo; k_idx < kv_hi; ++k_idx) {
|
|
1925
|
+
const int k_row_px = batch_idx * pitch_px_batch +
|
|
1926
|
+
head_kv_idx * pitch_px_head +
|
|
1927
|
+
k_idx * pitch_px_row;
|
|
1928
|
+
|
|
1929
|
+
const ulong v_row_off = batch_idx * v_nb3 + head_kv_idx * v_nb2 + k_idx * v_nb1;
|
|
1930
|
+
const global KV_DATA_TYPE4 * v_ptr = (const global KV_DATA_TYPE4 *) (v_base + v_row_off);
|
|
1931
|
+
|
|
1932
|
+
ACC_TYPE4 dot4[MQ_GQA];
|
|
1933
|
+
#pragma unroll
|
|
1934
|
+
for (int h = 0; h < MQ_GQA; ++h) dot4[h] = (ACC_TYPE4)(0.0f);
|
|
1935
|
+
for (int k = tid_sg; k < DK_VEC; k += Q1_WG_SIZE) {
|
|
1936
|
+
const half4 k_h4 = read_imageh(k_img, k_row_px + k);
|
|
1937
|
+
const ACC_TYPE4 k_vec = CONVERT_KV_ACC4(k_h4);
|
|
1938
|
+
#pragma unroll
|
|
1939
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1940
|
+
dot4[h] = mad(q_shared[h * DK_VEC + k], k_vec, dot4[h]);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
ACC_TYPE score[MQ_GQA];
|
|
1945
|
+
#pragma unroll
|
|
1946
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1947
|
+
const ACC_TYPE dot_partial = dot4[h].s0 + dot4[h].s1 + dot4[h].s2 + dot4[h].s3;
|
|
1948
|
+
ACC_TYPE s = sub_group_reduce_add(dot_partial) * scale;
|
|
1949
|
+
if (mask_base[h] != NULL) {
|
|
1950
|
+
const global MASK_DATA_TYPE * mask_ptr = (const global MASK_DATA_TYPE *) mask_base[h];
|
|
1951
|
+
s += slope[h] * (ACC_TYPE) mask_ptr[k_idx];
|
|
1952
|
+
}
|
|
1953
|
+
if (logit_softcap > 0.0f) {
|
|
1954
|
+
s = logit_softcap * tanh(s / logit_softcap);
|
|
1955
|
+
}
|
|
1956
|
+
score[h] = s;
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
ACC_TYPE p_h[MQ_GQA];
|
|
1960
|
+
ACC_TYPE sp_h[MQ_GQA];
|
|
1961
|
+
#pragma unroll
|
|
1962
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1963
|
+
const ACC_TYPE m_new = max(m_i[h], score[h]);
|
|
1964
|
+
sp_h[h] = native_exp(m_i[h] - m_new);
|
|
1965
|
+
p_h[h] = native_exp(score[h] - m_new);
|
|
1966
|
+
l_i[h] = l_i[h] * sp_h[h] + p_h[h];
|
|
1967
|
+
m_i[h] = m_new;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
int idx = 0;
|
|
1971
|
+
for (int dv_idx = tid_sg; dv_idx < DV_VEC; dv_idx += Q1_WG_SIZE, ++idx) {
|
|
1972
|
+
const ACC_TYPE4 v_vec = CONVERT_KV_ACC4(v_ptr[dv_idx]);
|
|
1973
|
+
#pragma unroll
|
|
1974
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1975
|
+
o_acc[h][idx] = mad(p_h[h], v_vec, o_acc[h][idx] * sp_h[h]);
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
__local ACC_TYPE sg_m[MQ_GQA][MQ_NSG_SPLIT];
|
|
1981
|
+
__local ACC_TYPE sg_l[MQ_GQA][MQ_NSG_SPLIT];
|
|
1982
|
+
__local ACC_TYPE4 sg_o[MQ_NSG_SPLIT][DV_VEC];
|
|
1983
|
+
|
|
1984
|
+
if (tid_sg == 0) {
|
|
1985
|
+
#pragma unroll
|
|
1986
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1987
|
+
sg_m[h][sgid] = m_i[h];
|
|
1988
|
+
sg_l[h][sgid] = l_i[h];
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
#pragma unroll
|
|
1993
|
+
for (int h = 0; h < MQ_GQA; ++h) {
|
|
1994
|
+
{
|
|
1995
|
+
int idx = 0;
|
|
1996
|
+
for (int dv_idx = tid_sg; dv_idx < DV_VEC; dv_idx += Q1_WG_SIZE, ++idx) {
|
|
1997
|
+
sg_o[sgid][dv_idx] = o_acc[h][idx];
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
2001
|
+
|
|
2002
|
+
if (sgid == 0) {
|
|
2003
|
+
const int head_idx = head_kv_idx * MQ_GQA + h;
|
|
2004
|
+
|
|
2005
|
+
ACC_TYPE m_c = sg_m[h][0];
|
|
2006
|
+
#pragma unroll
|
|
2007
|
+
for (int s = 1; s < MQ_NSG_SPLIT; ++s) {
|
|
2008
|
+
m_c = max(m_c, sg_m[h][s]);
|
|
2009
|
+
}
|
|
2010
|
+
ACC_TYPE l_c = 0.0f;
|
|
2011
|
+
#pragma unroll
|
|
2012
|
+
for (int s = 0; s < MQ_NSG_SPLIT; ++s) {
|
|
2013
|
+
l_c += sg_l[h][s] * native_exp(sg_m[h][s] - m_c);
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
const ulong rec_idx = ((((ulong) batch_idx * n_head + head_idx) * n_q + q_idx)
|
|
2017
|
+
* n_splits + split_idx);
|
|
2018
|
+
global float * rec = partial_void + rec_idx * record_stride;
|
|
2019
|
+
global float4 * rec_o = (global float4 *) (rec + 2);
|
|
2020
|
+
|
|
2021
|
+
if (tid_sg == 0) {
|
|
2022
|
+
rec[0] = (float) m_c;
|
|
2023
|
+
rec[1] = (float) l_c;
|
|
2024
|
+
}
|
|
2025
|
+
for (int dv_idx = tid_sg; dv_idx < DV_VEC; dv_idx += Q1_WG_SIZE) {
|
|
2026
|
+
ACC_TYPE4 o_merged = (ACC_TYPE4)(0.0f);
|
|
2027
|
+
#pragma unroll
|
|
2028
|
+
for (int s = 0; s < MQ_NSG_SPLIT; ++s) {
|
|
2029
|
+
const ACC_TYPE alpha = native_exp(sg_m[h][s] - m_c);
|
|
2030
|
+
o_merged = mad((ACC_TYPE4)(alpha), sg_o[s][dv_idx], o_merged);
|
|
2031
|
+
}
|
|
2032
|
+
rec_o[dv_idx] = o_merged;
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
#endif // !FA_DECODE_ONLY
|
|
2039
|
+
|
|
2040
|
+
#ifndef FA_MQ_ONLY // q1_split + merge excluded from the MQ-only (g8) program
|
|
2041
|
+
__kernel void flash_attn_f32_f16_q1_split(
|
|
2042
|
+
const global void * q_void, ulong q_offset,
|
|
2043
|
+
const global void * k_void, ulong k_offset,
|
|
2044
|
+
const global void * v_void, ulong v_offset,
|
|
2045
|
+
const float scale,
|
|
2046
|
+
const int n_q,
|
|
2047
|
+
const int n_kv,
|
|
2048
|
+
const int n_head,
|
|
2049
|
+
const ulong q_nb1, const ulong q_nb2, const ulong q_nb3,
|
|
2050
|
+
const ulong k_nb1, const ulong k_nb2, const ulong k_nb3,
|
|
2051
|
+
const ulong v_nb1, const ulong v_nb2, const ulong v_nb3,
|
|
2052
|
+
const float max_bias,
|
|
2053
|
+
const float m0,
|
|
2054
|
+
const float m1,
|
|
2055
|
+
const int n_head_log2,
|
|
2056
|
+
const float logit_softcap,
|
|
2057
|
+
const int n_head_kv,
|
|
2058
|
+
const global void * mask_void,
|
|
2059
|
+
const ulong mask_offset,
|
|
2060
|
+
const ulong mask_nb1,
|
|
2061
|
+
const ulong mask_nb2,
|
|
2062
|
+
const ulong mask_nb3,
|
|
2063
|
+
const int mask_ne2,
|
|
2064
|
+
const int mask_ne3,
|
|
2065
|
+
global float * partial_void,
|
|
2066
|
+
const int n_splits,
|
|
2067
|
+
const int kv_per_split
|
|
2068
|
+
) {
|
|
2069
|
+
const int tid = get_local_id(0);
|
|
2070
|
+
const int head_batch_idx = get_global_id(1);
|
|
2071
|
+
const int split_q_idx = get_global_id(2);
|
|
2072
|
+
const int split_idx = split_q_idx % n_splits;
|
|
2073
|
+
const int q_idx = split_q_idx / n_splits;
|
|
2074
|
+
const int batch_idx = head_batch_idx / n_head;
|
|
2075
|
+
const int head_idx = head_batch_idx % n_head;
|
|
2076
|
+
const int gqa_ratio = n_head / n_head_kv;
|
|
2077
|
+
const int head_kv_idx = head_idx / gqa_ratio;
|
|
2078
|
+
|
|
2079
|
+
const int kv_start = split_idx * kv_per_split;
|
|
2080
|
+
const int kv_end = min(kv_start + kv_per_split, n_kv);
|
|
2081
|
+
|
|
2082
|
+
const ulong record_stride = (ulong) FA_PARTIAL_FLOATS;
|
|
2083
|
+
const ulong record_idx = ((((ulong) batch_idx * n_head + head_idx) * n_q + q_idx)
|
|
2084
|
+
* n_splits + split_idx);
|
|
2085
|
+
global float * rec = partial_void + record_idx * record_stride;
|
|
2086
|
+
global float4 * rec_o = (global float4 *) (rec + 2);
|
|
2087
|
+
|
|
2088
|
+
if (kv_start >= kv_end) {
|
|
2089
|
+
// Empty split: leave sentinel partial for merge.
|
|
2090
|
+
if (tid == 0) {
|
|
2091
|
+
rec[0] = FA_M_INIT;
|
|
2092
|
+
rec[1] = 0.0f;
|
|
2093
|
+
}
|
|
2094
|
+
return;
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
const global char * q_base = (const global char *) q_void + q_offset;
|
|
2098
|
+
const global char * k_base = (const global char *) k_void + k_offset;
|
|
2099
|
+
const global char * v_base = (const global char *) v_void + v_offset;
|
|
2100
|
+
|
|
2101
|
+
const global char * mask_base = NULL;
|
|
2102
|
+
if (mask_void != NULL) {
|
|
2103
|
+
const int mask_head_idx = head_idx % mask_ne2;
|
|
2104
|
+
const int mask_batch_idx = batch_idx % mask_ne3;
|
|
2105
|
+
mask_base = (const global char *) mask_void + mask_offset +
|
|
2106
|
+
mask_batch_idx * mask_nb3 + mask_head_idx * mask_nb2 +
|
|
2107
|
+
(ulong) q_idx * mask_nb1;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// share Q via local memory (n_q=1 per split -> uniform across WG).
|
|
2111
|
+
__local ACC_TYPE4 q_shared[DK_VEC];
|
|
2112
|
+
const ulong q_row_offset = batch_idx * q_nb3 + head_idx * q_nb2 + (ulong) q_idx * q_nb1;
|
|
2113
|
+
const global Q_DATA_TYPE4 * q_ptr = (const global Q_DATA_TYPE4 *) (q_base + q_row_offset);
|
|
2114
|
+
for (int i = tid; i < DK_VEC; i += Q1_WG_SIZE) {
|
|
2115
|
+
q_shared[i] = CONVERT_Q_ACC4(q_ptr[i]);
|
|
2116
|
+
}
|
|
2117
|
+
sub_group_barrier(CLK_LOCAL_MEM_FENCE);
|
|
2118
|
+
|
|
2119
|
+
const float slope = get_alibi_slope(max_bias, head_idx, n_head_log2, m0, m1);
|
|
2120
|
+
|
|
2121
|
+
// pass 1a — split-local max.
|
|
2122
|
+
ACC_TYPE m_i = FA_M_INIT;
|
|
2123
|
+
for (int k_idx = kv_start + tid; k_idx < kv_end; k_idx += Q1_WG_SIZE) {
|
|
2124
|
+
const ulong k_row_offset = batch_idx * k_nb3 + head_kv_idx * k_nb2 + k_idx * k_nb1;
|
|
2125
|
+
const global KV_DATA_TYPE4 * k_ptr = (const global KV_DATA_TYPE4 *) (k_base + k_row_offset);
|
|
2126
|
+
ACC_TYPE4 dot_acc = (ACC_TYPE4)(0.0f);
|
|
2127
|
+
#pragma unroll
|
|
2128
|
+
for (int k = 0; k < DK_VEC; ++k) {
|
|
2129
|
+
dot_acc = mad(q_shared[k], CONVERT_KV_ACC4(k_ptr[k]), dot_acc);
|
|
2130
|
+
}
|
|
2131
|
+
ACC_TYPE score = (dot_acc.s0 + dot_acc.s1 + dot_acc.s2 + dot_acc.s3) * scale;
|
|
2132
|
+
if (mask_base != NULL) {
|
|
2133
|
+
const global MASK_DATA_TYPE * mask_ptr = (const global MASK_DATA_TYPE *) (mask_base);
|
|
2134
|
+
score += slope * (ACC_TYPE) mask_ptr[k_idx];
|
|
2135
|
+
}
|
|
2136
|
+
if (logit_softcap > 0.0f) {
|
|
2137
|
+
score = logit_softcap * tanh(score / logit_softcap);
|
|
2138
|
+
}
|
|
2139
|
+
m_i = max(m_i, score);
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
const ACC_TYPE m_c = sub_group_reduce_max(m_i);
|
|
2143
|
+
|
|
2144
|
+
// pass 1b — softmax-weighted V accumulate.
|
|
2145
|
+
ACC_TYPE4 o_acc[DV_VEC];
|
|
2146
|
+
#pragma unroll
|
|
2147
|
+
for (int i = 0; i < DV_VEC; ++i) o_acc[i] = (ACC_TYPE4)(0.0f);
|
|
2148
|
+
ACC_TYPE l_i = 0.0f;
|
|
2149
|
+
|
|
2150
|
+
for (int k_idx = kv_start + tid; k_idx < kv_end; k_idx += Q1_WG_SIZE) {
|
|
2151
|
+
const ulong k_row_offset = batch_idx * k_nb3 + head_kv_idx * k_nb2 + k_idx * k_nb1;
|
|
2152
|
+
const ulong v_row_offset = batch_idx * v_nb3 + head_kv_idx * v_nb2 + k_idx * v_nb1;
|
|
2153
|
+
const global KV_DATA_TYPE4 * k_ptr = (const global KV_DATA_TYPE4 *) (k_base + k_row_offset);
|
|
2154
|
+
const global KV_DATA_TYPE4 * v_ptr = (const global KV_DATA_TYPE4 *) (v_base + v_row_offset);
|
|
2155
|
+
ACC_TYPE4 dot_acc = (ACC_TYPE4)(0.0f);
|
|
2156
|
+
#pragma unroll
|
|
2157
|
+
for (int k = 0; k < DK_VEC; ++k) {
|
|
2158
|
+
dot_acc = mad(q_shared[k], CONVERT_KV_ACC4(k_ptr[k]), dot_acc);
|
|
2159
|
+
}
|
|
2160
|
+
ACC_TYPE score = (dot_acc.s0 + dot_acc.s1 + dot_acc.s2 + dot_acc.s3) * scale;
|
|
2161
|
+
if (mask_base != NULL) {
|
|
2162
|
+
const global MASK_DATA_TYPE * mask_ptr = (const global MASK_DATA_TYPE *) (mask_base);
|
|
2163
|
+
score += slope * (ACC_TYPE) mask_ptr[k_idx];
|
|
2164
|
+
}
|
|
2165
|
+
if (logit_softcap > 0.0f) {
|
|
2166
|
+
score = logit_softcap * tanh(score / logit_softcap);
|
|
2167
|
+
}
|
|
2168
|
+
const ACC_TYPE p = exp(score - m_c);
|
|
2169
|
+
l_i += p;
|
|
2170
|
+
#pragma unroll
|
|
2171
|
+
for (int i = 0; i < DV_VEC; ++i) {
|
|
2172
|
+
o_acc[i] = mad(p, CONVERT_KV_ACC4(v_ptr[i]), o_acc[i]);
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
__local ACC_TYPE4 local_o[Q1_WG_SIZE];
|
|
2177
|
+
const ACC_TYPE l_c = sub_group_reduce_add(l_i);
|
|
2178
|
+
|
|
2179
|
+
if (tid == 0) {
|
|
2180
|
+
rec[0] = (float) m_c;
|
|
2181
|
+
rec[1] = (float) l_c;
|
|
2182
|
+
}
|
|
2183
|
+
for (int i = 0; i < DV_VEC; ++i) {
|
|
2184
|
+
local_o[tid] = o_acc[i];
|
|
2185
|
+
sub_group_barrier(CLK_LOCAL_MEM_FENCE);
|
|
2186
|
+
#pragma unroll
|
|
2187
|
+
for (int s = Q1_WG_SIZE / 2; s > 0; s >>= 1) {
|
|
2188
|
+
if (tid < s) local_o[tid] += local_o[tid + s];
|
|
2189
|
+
sub_group_barrier(CLK_LOCAL_MEM_FENCE);
|
|
2190
|
+
}
|
|
2191
|
+
if (tid == 0) {
|
|
2192
|
+
rec_o[i] = local_o[0];
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
// FD Pass 2: merge per-split partials into final O
|
|
2198
|
+
// empty splits drop via exp(-INF)=0.
|
|
2199
|
+
__kernel void flash_attn_f32_merge(
|
|
2200
|
+
const global float * partial_void,
|
|
2201
|
+
global void * o_void,
|
|
2202
|
+
const ulong o_offset,
|
|
2203
|
+
const int n_head,
|
|
2204
|
+
const int n_splits,
|
|
2205
|
+
const ulong o_nb1, const ulong o_nb2, const ulong o_nb3,
|
|
2206
|
+
const global void * sinks_void,
|
|
2207
|
+
const ulong sinks_offset,
|
|
2208
|
+
const int n_q
|
|
2209
|
+
) {
|
|
2210
|
+
const int lane = get_local_id(0); // 0..DV_VEC-1
|
|
2211
|
+
const int head_batch_idx = get_global_id(1);
|
|
2212
|
+
const int q_idx = get_global_id(2);
|
|
2213
|
+
const int batch_idx = head_batch_idx / n_head;
|
|
2214
|
+
const int head_idx = head_batch_idx % n_head;
|
|
2215
|
+
|
|
2216
|
+
const ulong record_stride = (ulong) FA_PARTIAL_FLOATS;
|
|
2217
|
+
const ulong record_idx_0 = (((ulong) batch_idx * n_head + head_idx) * n_q + q_idx) * n_splits;
|
|
2218
|
+
const global float * rec0 = partial_void + record_idx_0 * record_stride;
|
|
2219
|
+
|
|
2220
|
+
__local ACC_TYPE m_final_shared;
|
|
2221
|
+
__local ACC_TYPE l_final_shared;
|
|
2222
|
+
if (lane == 0) {
|
|
2223
|
+
ACC_TYPE m = FA_M_INIT;
|
|
2224
|
+
for (int c = 0; c < n_splits; ++c) {
|
|
2225
|
+
const ACC_TYPE m_c = rec0[c * record_stride + 0];
|
|
2226
|
+
m = max(m, m_c);
|
|
2227
|
+
}
|
|
2228
|
+
ACC_TYPE m_sink = 0.0f;
|
|
2229
|
+
bool has_sink = false;
|
|
2230
|
+
if (sinks_void != NULL) {
|
|
2231
|
+
const global ACC_TYPE * sinks_ptr =
|
|
2232
|
+
(const global ACC_TYPE *) ((const global char *) sinks_void + sinks_offset);
|
|
2233
|
+
m_sink = sinks_ptr[head_idx];
|
|
2234
|
+
has_sink = true;
|
|
2235
|
+
m = max(m, m_sink);
|
|
2236
|
+
}
|
|
2237
|
+
ACC_TYPE l = 0.0f;
|
|
2238
|
+
for (int c = 0; c < n_splits; ++c) {
|
|
2239
|
+
const ACC_TYPE m_c = rec0[c * record_stride + 0];
|
|
2240
|
+
const ACC_TYPE l_c = rec0[c * record_stride + 1];
|
|
2241
|
+
if (m_c > FA_M_INIT) {
|
|
2242
|
+
l += l_c * exp(m_c - m);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
if (has_sink) {
|
|
2246
|
+
l += exp(m_sink - m);
|
|
2247
|
+
}
|
|
2248
|
+
m_final_shared = m;
|
|
2249
|
+
l_final_shared = l;
|
|
2250
|
+
}
|
|
2251
|
+
barrier(CLK_LOCAL_MEM_FENCE);
|
|
2252
|
+
const ACC_TYPE m_final = m_final_shared;
|
|
2253
|
+
const ACC_TYPE l_final = l_final_shared;
|
|
2254
|
+
const ACC_TYPE l_inv = (l_final > 0.0f) ? (1.0f / l_final) : 0.0f;
|
|
2255
|
+
|
|
2256
|
+
ACC_TYPE4 o = (ACC_TYPE4)(0.0f);
|
|
2257
|
+
for (int c = 0; c < n_splits; ++c) {
|
|
2258
|
+
const global float * rec_c = rec0 + c * record_stride;
|
|
2259
|
+
const ACC_TYPE m_c = rec_c[0];
|
|
2260
|
+
if (m_c <= FA_M_INIT) continue;
|
|
2261
|
+
const global float4 * rec_oc = (const global float4 *) (rec_c + 2);
|
|
2262
|
+
const ACC_TYPE scale_c = exp(m_c - m_final);
|
|
2263
|
+
o = mad((ACC_TYPE4)(scale_c), rec_oc[lane], o);
|
|
2264
|
+
}
|
|
2265
|
+
o = o * l_inv;
|
|
2266
|
+
|
|
2267
|
+
const ulong o_row_offset = (ulong) batch_idx * o_nb3 + (ulong) q_idx * o_nb2 + (ulong) head_idx * o_nb1;
|
|
2268
|
+
global O_DATA_TYPE4 * o_row = (global O_DATA_TYPE4 *) ((global char *) o_void + o_offset + o_row_offset);
|
|
2269
|
+
o_row[lane] = CONVERT_O_DATA4(o);
|
|
2270
|
+
}
|
|
2271
|
+
#endif // !FA_MQ_ONLY (q1_split + merge)
|
|
2272
|
+
#endif // !FA_PREFILL_ONLY (decode kernels)
|