11/* ************************************************************************
2- * Copyright (c) 2024-2025 , Advanced Micro Devices, Inc. All rights reserved.
2+ * Copyright (c) 2026 , Advanced Micro Devices, Inc. All rights reserved.
33 *
44 * License for AMD contributions = MIT. See LICENSE for more information
55 ************************************************************************/
66
77/* ! \file fused_attn_smallseq.cpp
88 * \brief Unfused small-seq (varlen) attention: seq_q=1, max_seqlen_kv<=16, THD only.
9- * Ported from varlen_attn/attn_fwd.cpp and attn_bwd.cpp with runtime b, head_num.
109 */
1110
1211#include < hip/hip_runtime.h>
1312#include < hip/hip_bfloat16.h>
13+ #include < hip/hip_fp16.h>
1414
1515#include < cstdint>
1616#include < cstdlib>
@@ -106,30 +106,30 @@ __global__ void compute_scores_kernel(const T* Q,
106106 for (int i = 0 ; i < seq_kv; i++)
107107 results[i] = 0 .0f ;
108108 for (int dim_offset = 0 ; dim_offset < head_dim; dim_offset += block_k) {
109- if constexpr (std::is_same<T, hip_bfloat16>::value) {
109+ if constexpr (std::is_same<T, hip_bfloat16>::value || std::is_same<T, __half>::value ) {
110110 for (int k = 0 ; k < block_k / 8 ; k++) {
111111 ls_dwordx4_tmp_var = *((uint4*)&Q_ptr[dim_offset + k * 8 ]);
112- fetch_Q[k * 8 + 0 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.x )[0 ];
113- fetch_Q[k * 8 + 1 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.x )[1 ];
114- fetch_Q[k * 8 + 2 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.y )[0 ];
115- fetch_Q[k * 8 + 3 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.y )[1 ];
116- fetch_Q[k * 8 + 4 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.z )[0 ];
117- fetch_Q[k * 8 + 5 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.z )[1 ];
118- fetch_Q[k * 8 + 6 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.w )[0 ];
119- fetch_Q[k * 8 + 7 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.w )[1 ];
112+ fetch_Q[k * 8 + 0 ] = ((T *)&ls_dwordx4_tmp_var.x )[0 ];
113+ fetch_Q[k * 8 + 1 ] = ((T *)&ls_dwordx4_tmp_var.x )[1 ];
114+ fetch_Q[k * 8 + 2 ] = ((T *)&ls_dwordx4_tmp_var.y )[0 ];
115+ fetch_Q[k * 8 + 3 ] = ((T *)&ls_dwordx4_tmp_var.y )[1 ];
116+ fetch_Q[k * 8 + 4 ] = ((T *)&ls_dwordx4_tmp_var.z )[0 ];
117+ fetch_Q[k * 8 + 5 ] = ((T *)&ls_dwordx4_tmp_var.z )[1 ];
118+ fetch_Q[k * 8 + 6 ] = ((T *)&ls_dwordx4_tmp_var.w )[0 ];
119+ fetch_Q[k * 8 + 7 ] = ((T *)&ls_dwordx4_tmp_var.w )[1 ];
120120 }
121121 for (int kv_idx = 0 ; kv_idx < seq_kv; kv_idx++) {
122122 for (int k = 0 ; k < block_k / 8 ; k++) {
123123 ls_dwordx4_tmp_var =
124124 *((uint4*)&K_ptr[kv_idx * head_num * head_dim + dim_offset + k * 8 ]);
125- fetch_K[k * 8 + 0 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.x )[0 ];
126- fetch_K[k * 8 + 1 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.x )[1 ];
127- fetch_K[k * 8 + 2 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.y )[0 ];
128- fetch_K[k * 8 + 3 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.y )[1 ];
129- fetch_K[k * 8 + 4 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.z )[0 ];
130- fetch_K[k * 8 + 5 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.z )[1 ];
131- fetch_K[k * 8 + 6 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.w )[0 ];
132- fetch_K[k * 8 + 7 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.w )[1 ];
125+ fetch_K[k * 8 + 0 ] = ((T *)&ls_dwordx4_tmp_var.x )[0 ];
126+ fetch_K[k * 8 + 1 ] = ((T *)&ls_dwordx4_tmp_var.x )[1 ];
127+ fetch_K[k * 8 + 2 ] = ((T *)&ls_dwordx4_tmp_var.y )[0 ];
128+ fetch_K[k * 8 + 3 ] = ((T *)&ls_dwordx4_tmp_var.y )[1 ];
129+ fetch_K[k * 8 + 4 ] = ((T *)&ls_dwordx4_tmp_var.z )[0 ];
130+ fetch_K[k * 8 + 5 ] = ((T *)&ls_dwordx4_tmp_var.z )[1 ];
131+ fetch_K[k * 8 + 6 ] = ((T *)&ls_dwordx4_tmp_var.w )[0 ];
132+ fetch_K[k * 8 + 7 ] = ((T *)&ls_dwordx4_tmp_var.w )[1 ];
133133 }
134134#pragma unroll
135135 for (int k = 0 ; k < block_k; k++)
@@ -502,30 +502,30 @@ __global__ void compute_grad_attn_kernel(const T* grad_O,
502502 results[i] = 0 .0f ;
503503
504504 for (int dim_offset = 0 ; dim_offset < head_dim; dim_offset += block_k) {
505- if constexpr (std::is_same<T, hip_bfloat16>::value) {
505+ if constexpr (std::is_same<T, hip_bfloat16>::value || std::is_same<T, __half>::value ) {
506506 for (int k = 0 ; k < block_k / 8 ; k++) {
507507 ls_dwordx4_tmp_var = *((uint4*)&grad_O_ptr[dim_offset + k * 8 ]);
508- fetch_grad_O[k * 8 + 0 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.x )[0 ];
509- fetch_grad_O[k * 8 + 1 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.x )[1 ];
510- fetch_grad_O[k * 8 + 2 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.y )[0 ];
511- fetch_grad_O[k * 8 + 3 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.y )[1 ];
512- fetch_grad_O[k * 8 + 4 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.z )[0 ];
513- fetch_grad_O[k * 8 + 5 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.z )[1 ];
514- fetch_grad_O[k * 8 + 6 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.w )[0 ];
515- fetch_grad_O[k * 8 + 7 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.w )[1 ];
508+ fetch_grad_O[k * 8 + 0 ] = ((T *)&ls_dwordx4_tmp_var.x )[0 ];
509+ fetch_grad_O[k * 8 + 1 ] = ((T *)&ls_dwordx4_tmp_var.x )[1 ];
510+ fetch_grad_O[k * 8 + 2 ] = ((T *)&ls_dwordx4_tmp_var.y )[0 ];
511+ fetch_grad_O[k * 8 + 3 ] = ((T *)&ls_dwordx4_tmp_var.y )[1 ];
512+ fetch_grad_O[k * 8 + 4 ] = ((T *)&ls_dwordx4_tmp_var.z )[0 ];
513+ fetch_grad_O[k * 8 + 5 ] = ((T *)&ls_dwordx4_tmp_var.z )[1 ];
514+ fetch_grad_O[k * 8 + 6 ] = ((T *)&ls_dwordx4_tmp_var.w )[0 ];
515+ fetch_grad_O[k * 8 + 7 ] = ((T *)&ls_dwordx4_tmp_var.w )[1 ];
516516 }
517517 for (int kv_idx = 0 ; kv_idx < seq_kv; kv_idx++) {
518518 for (int k = 0 ; k < block_k / 8 ; k++) {
519519 ls_dwordx4_tmp_var =
520520 *((uint4*)&V_base[kv_idx * V_stride + dim_offset + k * 8 ]);
521- fetch_V[k * 8 + 0 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.x )[0 ];
522- fetch_V[k * 8 + 1 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.x )[1 ];
523- fetch_V[k * 8 + 2 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.y )[0 ];
524- fetch_V[k * 8 + 3 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.y )[1 ];
525- fetch_V[k * 8 + 4 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.z )[0 ];
526- fetch_V[k * 8 + 5 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.z )[1 ];
527- fetch_V[k * 8 + 6 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.w )[0 ];
528- fetch_V[k * 8 + 7 ] = ((hip_bfloat16 *)&ls_dwordx4_tmp_var.w )[1 ];
521+ fetch_V[k * 8 + 0 ] = ((T *)&ls_dwordx4_tmp_var.x )[0 ];
522+ fetch_V[k * 8 + 1 ] = ((T *)&ls_dwordx4_tmp_var.x )[1 ];
523+ fetch_V[k * 8 + 2 ] = ((T *)&ls_dwordx4_tmp_var.y )[0 ];
524+ fetch_V[k * 8 + 3 ] = ((T *)&ls_dwordx4_tmp_var.y )[1 ];
525+ fetch_V[k * 8 + 4 ] = ((T *)&ls_dwordx4_tmp_var.z )[0 ];
526+ fetch_V[k * 8 + 5 ] = ((T *)&ls_dwordx4_tmp_var.z )[1 ];
527+ fetch_V[k * 8 + 6 ] = ((T *)&ls_dwordx4_tmp_var.w )[0 ];
528+ fetch_V[k * 8 + 7 ] = ((T *)&ls_dwordx4_tmp_var.w )[1 ];
529529 }
530530#pragma unroll
531531 for (int k = 0 ; k < block_k; k++)
@@ -708,7 +708,7 @@ __global__ void compute_grad_qk_kernel(const T* grad_scores,
708708 head_dim +
709709 thread_head_offset + i * dwordx4_load_elt];
710710 for (int b = 0 ; b < dwordx4_load_elt; b++)
711- grad_Q_ptr[b] = ((T*)&store_dwordx4_tmp_var[i])[b] * scale;
711+ grad_Q_ptr[b] = ((T*)&store_dwordx4_tmp_var[i])[b] * T ( scale) ;
712712 }
713713#pragma unroll
714714 for (int i = 0 ; i < block_k / dwordx4_load_elt; i++) {
@@ -833,8 +833,9 @@ void fused_attn_smallseq_fwd(size_t b,
833833 size_t * workspace_size,
834834 cudaStream_t stream)
835835{
836- if (std::getenv (" NVTE_FUSED_ATTN_CK_SMALLSEQ" )) {
837- std::cout << std::endl << " attn_fwd(ck small-seq kernel): " ;
836+ const char * nvte_smallseq = std::getenv (" NVTE_LOG_CK_CONFIG" );
837+ if (nvte_smallseq && std::string (nvte_smallseq) == " 1" ) {
838+ std::cout << std::endl << " attn_fwd(small-seq kernel): " ;
838839 std::cout << " b: " << b << " , " ;
839840 std::cout << " h_q: " << h_q << " , " ;
840841 std::cout << " h_kv: " << h_kv << " , " ;
@@ -871,6 +872,38 @@ void fused_attn_smallseq_fwd(size_t b,
871872 int bi = static_cast <int >(b);
872873 int hi = static_cast <int >(h_q);
873874
875+ switch (max_seqlen_kv) {
876+ SMALLSEQ_DISPATCH_FWD_CASE (2 )
877+ SMALLSEQ_DISPATCH_FWD_CASE (3 )
878+ SMALLSEQ_DISPATCH_FWD_CASE (4 )
879+ SMALLSEQ_DISPATCH_FWD_CASE (5 )
880+ SMALLSEQ_DISPATCH_FWD_CASE (6 )
881+ SMALLSEQ_DISPATCH_FWD_CASE (7 )
882+ SMALLSEQ_DISPATCH_FWD_CASE (8 )
883+ SMALLSEQ_DISPATCH_FWD_CASE (9 )
884+ SMALLSEQ_DISPATCH_FWD_CASE (10 )
885+ SMALLSEQ_DISPATCH_FWD_CASE (11 )
886+ SMALLSEQ_DISPATCH_FWD_CASE (12 )
887+ SMALLSEQ_DISPATCH_FWD_CASE (13 )
888+ SMALLSEQ_DISPATCH_FWD_CASE (14 )
889+ SMALLSEQ_DISPATCH_FWD_CASE (15 )
890+ SMALLSEQ_DISPATCH_FWD_CASE (16 )
891+ default :
892+ NVTE_ERROR (" Unsupported max_seqlen_kv for small-seq: max_seqlen_kv <= 16." );
893+ }
894+ } else if (qkv_dtype == DType::kFloat16 ) {
895+ using T = __half;
896+ const T* Q_ptr = static_cast <const T*>(devPtrQ);
897+ const T* K_ptr = static_cast <const T*>(devPtrK);
898+ const T* V_ptr = static_cast <const T*>(devPtrV);
899+ T* O_ptr = static_cast <T*>(devPtrO);
900+ T* attn_workspace = static_cast <T*>(attn_weights_buffer);
901+ const int * cu_kv = static_cast <const int *>(devPtrCuSeqlensKV);
902+ const int * cu_kv_p = static_cast <const int *>(devPtrSeqOffsetsKV);
903+ const T* dropout_mask = nullptr ;
904+ int bi = static_cast <int >(b);
905+ int hi = static_cast <int >(h_q);
906+
874907 switch (max_seqlen_kv) {
875908 SMALLSEQ_DISPATCH_FWD_CASE (2 )
876909 SMALLSEQ_DISPATCH_FWD_CASE (3 )
@@ -891,7 +924,7 @@ void fused_attn_smallseq_fwd(size_t b,
891924 NVTE_ERROR (" Unsupported max_seqlen_kv for small-seq: max_seqlen_kv <= 16." );
892925 }
893926 } else {
894- NVTE_ERROR (" small-seq path supports only BF16 ( and optionally FP16) ." );
927+ NVTE_ERROR (" small-seq path supports only BF16 and FP16." );
895928 }
896929
897930 if (workspace_size) {
@@ -941,10 +974,6 @@ void fused_attn_smallseq_bwd(size_t b,
941974 (void )h_kv;
942975 (void )d_qk;
943976 (void )d_v;
944- NVTE_CHECK (max_seqlen_kv >= 2 && max_seqlen_kv <= 16 ,
945- " small-seq path requires 2 <= max_seqlen_kv <= 16." );
946- NVTE_CHECK (d_qk == 128 && d_v == 128 , " small-seq path currently supports head_dim 128 only." );
947- NVTE_CHECK (workspace != nullptr , " small-seq bwd requires workspace." );
948977
949978 float sqr_dk_scale = attn_scale;
950979 hipStream_t hip_stream = reinterpret_cast <hipStream_t>(stream);
@@ -967,6 +996,43 @@ void fused_attn_smallseq_bwd(size_t b,
967996 int bi = static_cast <int >(b);
968997 int hi = static_cast <int >(h_q);
969998
999+ switch (max_seqlen_kv) {
1000+ SMALLSEQ_DISPATCH_BWD_CASE (2 )
1001+ SMALLSEQ_DISPATCH_BWD_CASE (3 )
1002+ SMALLSEQ_DISPATCH_BWD_CASE (4 )
1003+ SMALLSEQ_DISPATCH_BWD_CASE (5 )
1004+ SMALLSEQ_DISPATCH_BWD_CASE (6 )
1005+ SMALLSEQ_DISPATCH_BWD_CASE (7 )
1006+ SMALLSEQ_DISPATCH_BWD_CASE (8 )
1007+ SMALLSEQ_DISPATCH_BWD_CASE (9 )
1008+ SMALLSEQ_DISPATCH_BWD_CASE (10 )
1009+ SMALLSEQ_DISPATCH_BWD_CASE (11 )
1010+ SMALLSEQ_DISPATCH_BWD_CASE (12 )
1011+ SMALLSEQ_DISPATCH_BWD_CASE (13 )
1012+ SMALLSEQ_DISPATCH_BWD_CASE (14 )
1013+ SMALLSEQ_DISPATCH_BWD_CASE (15 )
1014+ SMALLSEQ_DISPATCH_BWD_CASE (16 )
1015+ default :
1016+ NVTE_ERROR (" Unsupported max_seqlen_kv for small-seq: max_seqlen_kv <= 16." );
1017+ }
1018+ } else if (qkv_dtype == DType::kFloat16 ) {
1019+ using T = __half;
1020+ const T* Q_ptr = static_cast <const T*>(devPtrQ);
1021+ const T* K_ptr = static_cast <const T*>(devPtrK);
1022+ const T* V_ptr = static_cast <const T*>(devPtrV);
1023+ const T* O_ptr = static_cast <const T*>(devPtrO);
1024+ const T* dO_ptr = static_cast <const T*>(devPtrdO);
1025+ const T* attn_ptr = static_cast <const T*>(attn_weights);
1026+ T* dQ_ptr = static_cast <T*>(devPtrdQ);
1027+ T* dK_ptr = static_cast <T*>(devPtrdK);
1028+ T* dV_ptr = static_cast <T*>(devPtrdV);
1029+ T* workspace_ptr = static_cast <T*>(workspace);
1030+ const int * cu_kv = static_cast <const int *>(devPtrCuSeqlensKV);
1031+ const int * cu_kv_p = static_cast <const int *>(devPtrSeqOffsetsKV);
1032+ const T* dropout_mask = nullptr ;
1033+ int bi = static_cast <int >(b);
1034+ int hi = static_cast <int >(h_q);
1035+
9701036 switch (max_seqlen_kv) {
9711037 SMALLSEQ_DISPATCH_BWD_CASE (2 )
9721038 SMALLSEQ_DISPATCH_BWD_CASE (3 )
@@ -987,7 +1053,7 @@ void fused_attn_smallseq_bwd(size_t b,
9871053 NVTE_ERROR (" Unsupported max_seqlen_kv for small-seq: max_seqlen_kv <= 16." );
9881054 }
9891055 } else {
990- NVTE_ERROR (" small-seq path supports only BF16 ( and optionally FP16) ." );
1056+ NVTE_ERROR (" small-seq path supports only BF16 and FP16." );
9911057 }
9921058
9931059 if (workspace_size)
0 commit comments