@@ -74,8 +74,10 @@ void _q_at_k_gemm(
7474 accum_t * qk_data) {
7575 ET_CHECK_MSG (q_data.dtype == k_data.dtype , " q and k must have same dtype" );
7676 ET_CHECK_MSG (
77- q_data.dtype == ScalarType::Char || q_data.dtype == ScalarType::Float,
78- " q and k must be either int8 or float" );
77+ q_data.dtype == ScalarType::Char || q_data.dtype == ScalarType::Float ||
78+ q_data.dtype == ScalarType::Half ||
79+ q_data.dtype == ScalarType::BFloat16,
80+ " q and k must be int8, float, half, or bfloat16" );
7981 if (q_data.dtype == ScalarType::Char) {
8082 if constexpr (std::is_same<accum_t , float >::value) {
8183 int a_stride_m_tmp, b_stride_n_tmp;
@@ -103,6 +105,46 @@ void _q_at_k_gemm(
103105 ET_CHECK_MSG (
104106 false , " Accumulation in dtype other than float not supported yet" );
105107 }
108+ } else if (
109+ q_data.dtype == ScalarType::BFloat16 ||
110+ q_data.dtype == ScalarType::Half) {
111+ if constexpr (std::is_same<accum_t , float >::value) {
112+ if (q_data.dtype == ScalarType::BFloat16) {
113+ using rt = ::executorch::aten::BFloat16;
114+ ::executorch::cpublas::gemm (
115+ ::executorch::cpublas::TransposeType::Transpose,
116+ ::executorch::cpublas::TransposeType::NoTranspose,
117+ k_n,
118+ q_m,
119+ qk_k,
120+ 1 .0f ,
121+ static_cast <const rt*>(k_data.data),
122+ k_stride_n,
123+ static_cast<const rt*>(q_data.data),
124+ q_stride_m,
125+ 0.0f,
126+ qk_data,
127+ k_n);
128+ } else {
129+ using rt = ::executorch::aten::Half;
130+ ::executorch::cpublas::gemm (
131+ ::executorch::cpublas::TransposeType::Transpose,
132+ ::executorch::cpublas::TransposeType::NoTranspose,
133+ k_n,
134+ q_m,
135+ qk_k,
136+ 1 .0f ,
137+ static_cast <const rt*>(k_data.data),
138+ k_stride_n,
139+ static_cast<const rt*>(q_data.data),
140+ q_stride_m,
141+ 0.0f,
142+ qk_data,
143+ k_n);
144+ }
145+ } else {
146+ ET_CHECK_MSG (false , " Reduced-precision q@k requires float accumulation" );
147+ }
106148 } else {
107149 ::executorch::cpublas::gemm (
108150 ::executorch::cpublas::TransposeType::Transpose,
@@ -251,7 +293,7 @@ void _qk_at_v_gemm(
251293 const int64_t m,
252294 const int64_t n,
253295 const int64_t k,
254- const accum_t * qk_data,
296+ const void * qk_data,
255297 const int64_t qk_stride_m,
256298 const MaybeQuantizedMatrixData& v_data,
257299 const int64_t v_stride_n,
@@ -261,14 +303,15 @@ void _qk_at_v_gemm(
261303 accum_t * buf_qdq_ptr) {
262304 if (v_data.dtype == ScalarType::Char) {
263305 if constexpr (std::is_same<accum_t , float >::value) {
306+ const float * qk = static_cast <const float *>(qk_data);
264307 if (m > 4 ) {
265308 // For larger batch sizes, dequantize and use BLAS for better
266309 // performance
267310 dequant_and_gemm (
268311 m,
269312 n,
270313 k,
271- const_cast <float *>(qk_data ),
314+ const_cast <float *>(qk ),
272315 qk_stride_m,
273316 v_data,
274317 v_stride_n,
@@ -286,7 +329,7 @@ void _qk_at_v_gemm(
286329 m,
287330 n,
288331 k,
289- qk_data ,
332+ qk ,
290333 qk_stride_m /* lhs_stride_m*/ ,
291334 static_cast <const int8_t *>(v_data.data ),
292335 v_stride_n /* rhs_stride_n*/ ,
@@ -301,6 +344,48 @@ void _qk_at_v_gemm(
301344 ET_CHECK_MSG (
302345 false , " Accumulation in dtype other than float not supported yet" );
303346 }
347+ } else if (
348+ v_data.dtype == ScalarType::BFloat16 ||
349+ v_data.dtype == ScalarType::Half) {
350+ // qk has been cast to the activation dtype (see qk_reduced_data); both
351+ // operands are reduced precision and accumulate into the float output.
352+ if constexpr (std::is_same<accum_t , float >::value) {
353+ if (v_data.dtype == ScalarType::BFloat16) {
354+ using rt = ::executorch::aten::BFloat16;
355+ ::executorch::cpublas::gemm (
356+ ::executorch::cpublas::TransposeType::NoTranspose,
357+ ::executorch::cpublas::TransposeType::NoTranspose,
358+ n,
359+ m,
360+ k,
361+ 1 .0f ,
362+ static_cast <const rt*>(v_data.data),
363+ v_stride_n,
364+ static_cast<const rt*>(qk_data),
365+ qk_stride_m,
366+ beta,
367+ o_data,
368+ o_stride_m);
369+ } else {
370+ using rt = ::executorch::aten::Half;
371+ ::executorch::cpublas::gemm (
372+ ::executorch::cpublas::TransposeType::NoTranspose,
373+ ::executorch::cpublas::TransposeType::NoTranspose,
374+ n,
375+ m,
376+ k,
377+ 1 .0f ,
378+ static_cast <const rt*>(v_data.data),
379+ v_stride_n,
380+ static_cast<const rt*>(qk_data),
381+ qk_stride_m,
382+ beta,
383+ o_data,
384+ o_stride_m);
385+ }
386+ } else {
387+ ET_CHECK_MSG (false , " Reduced-precision qk@v requires float accumulation" );
388+ }
304389 } else {
305390 ::executorch::cpublas::gemm (
306391 ::executorch::cpublas::TransposeType::NoTranspose,
@@ -311,7 +396,7 @@ void _qk_at_v_gemm(
311396 static_cast <accum_t >(1 ),
312397 static_cast<const accum_t*>(v_data.data),
313398 v_stride_n,
314- qk_data,
399+ static_cast< const accum_t*>( qk_data) ,
315400 qk_stride_m,
316401 beta,
317402 o_data,
@@ -572,11 +657,9 @@ void cpu_flash_attention(
572657 constexpr bool is_reduced_type =
573658 ::executorch::runtime::is_reduced_floating_point_v<scalar_t >;
574659
575- ET_CHECK_MSG (
576- !is_reduced_type, " FlashAttention does not support reduced types." );
577- // Figure out mixed precision a little later
578- // using accum_t = at::opmath_type<scalar_t>;
579- using accum_t = scalar_t ;
660+ // Reduced-precision (bf16/fp16) activations accumulate in float: the two
661+ // matmuls run as reduced-in/float-out gemms and the softmax stays in float.
662+ using accum_t = std::conditional_t <is_reduced_type, float , scalar_t >;
580663 using Vec = vec::Vectorized<accum_t >;
581664 accum_t scaling_factor = static_cast <accum_t >(calculate_scale (query, scale));
582665
@@ -774,7 +857,19 @@ void cpu_flash_attention(
774857 } else {
775858 buf = scratch.get ();
776859 }
860+ std::unique_ptr<char []> allocated_buf_reduced;
777861 void * buf_reduced = nullptr ;
862+ if (is_reduced_type) {
863+ int64_t size_reduced_bytes =
864+ qSplitSize * kvSplitSize * num_thread * sizeof (scalar_t );
865+ Result<void *> scratch_reduced = ctx.allocate_temp (size_reduced_bytes, 64 );
866+ if (!scratch_reduced.ok ()) {
867+ allocated_buf_reduced = std::make_unique<char []>(size_reduced_bytes);
868+ buf_reduced = allocated_buf_reduced.get ();
869+ } else {
870+ buf_reduced = scratch_reduced.get ();
871+ }
872+ }
778873 int64_t size_per_thread_qdq_vec = kvSplitSize * headSize;
779874 // Lets align size_per_thread_qdq_vec to 64 bytes, for coalesced cache reads,
780875 // by padding with right number of per thread elements
@@ -1015,18 +1110,16 @@ void cpu_flash_attention(
10151110 if (tmp_max == -std::numeric_limits<accum_t >::infinity ()) {
10161111 // to avoid `nan = exp2f(-inf - (-inf))`
10171112 fill_stub (
1018- conditional_data_ptr (qk_data, qk_reduced_data) +
1019- row * kvBlockSize,
1020- static_cast <scalar_t >(0 ),
1113+ qk_data + row * kvBlockSize,
1114+ static_cast <accum_t >(0 ),
10211115 kvBlockSize);
10221116 } else {
10231117 // qk <- exp(qk - max) and sum per row
10241118 tmp_sum = tmp_max;
10251119 _exp_reduce_sum_fusion_kernel (
10261120 qk_data + row * kvBlockSize,
10271121 kvBlockSize,
1028- conditional_data_ptr (qk_data, qk_reduced_data) +
1029- row * kvBlockSize,
1122+ qk_data + row * kvBlockSize,
10301123 tmp_sum);
10311124 // exp_tmp <- exp(max[row] - max)
10321125 exp_tmp = std::exp (qk_max_data[row] - tmp_max);
@@ -1068,12 +1161,24 @@ void cpu_flash_attention(
10681161 headSize,
10691162 v_quant_params_StrideN,
10701163 value.scalar_type ());
1164+ // For reduced-precision activations the attention weights are cast to
1165+ // the activation dtype so that Softmax(q @ k.T) @ v runs as a
1166+ // reduced-in/float-out gemm matching the value matrix.
1167+ const void * qk_gemm_data = qk_data;
1168+ if constexpr (is_reduced_type) {
1169+ if (!is_quantized_sdpa) {
1170+ for (int64_t idx = 0 ; idx < qBlockSize * kvBlockSize; ++idx) {
1171+ qk_reduced_data[idx] = static_cast <scalar_t >(qk_data[idx]);
1172+ }
1173+ qk_gemm_data = qk_reduced_data;
1174+ }
1175+ }
10711176 // Calculate Softmax(q @ k.T) @ v
10721177 _qk_at_v_gemm<accum_t >(
10731178 qBlockSize,
10741179 headSize,
10751180 kvBlockSize,
1076- qk_data ,
1181+ qk_gemm_data ,
10771182 kvBlockSize,
10781183 v_sub_matrix_data,
10791184 vStrideN,
@@ -1086,12 +1191,20 @@ void cpu_flash_attention(
10861191 // reorder MHA output with strides
10871192 for (int64_t row = 0 ; row < qBlockSize; ++row) {
10881193 accum_t sum_reciprocal = 1 / qk_sum_data[row];
1089- vec::map<scalar_t >(
1090- [sum_reciprocal](Vec x) { return x * Vec (sum_reciprocal); },
1091- out_data + i * oStrideB + j * oStrideH + m * oStrideM +
1092- row * oStrideM,
1093- dst_data + row * headSize,
1094- headSize);
1194+ scalar_t * out_row = out_data + i * oStrideB + j * oStrideH +
1195+ m * oStrideM + row * oStrideM;
1196+ const accum_t * dst_row = dst_data + row * headSize;
1197+ if constexpr (is_reduced_type) {
1198+ for (int64_t d = 0 ; d < headSize; ++d) {
1199+ out_row[d] = static_cast <scalar_t >(dst_row[d] * sum_reciprocal);
1200+ }
1201+ } else {
1202+ vec::map<scalar_t >(
1203+ [sum_reciprocal](Vec x) { return x * Vec (sum_reciprocal); },
1204+ out_row,
1205+ dst_row,
1206+ headSize);
1207+ }
10951208 }
10961209 // Move to the next query
10971210 data_index_step (i, batchSize, j, num_head, k, qSlice);
0 commit comments