Skip to content

Commit beb5b16

Browse files
authored
manually unroll block 64 to keep bf16 gemv accumulators in registers (pytorch#22346)
Differential Revision: D117225819 Pull Request resolved: pytorch#22346
1 parent 1cfcdf8 commit beb5b16

1 file changed

Lines changed: 141 additions & 17 deletions

File tree

‎kernels/optimized/blas/BlasKernel.cpp‎

Lines changed: 141 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -359,29 +359,36 @@ TARGET_ARM_BF16_ATTRIBUTE static void dot4_with_fp32_arith_bfdot(
359359
int64_t len,
360360
float* out) {
361361
constexpr int64_t kElementsPerIteration = 8;
362-
float32x4_t acc[4] = {
363-
vdupq_n_f32(0.0f),
364-
vdupq_n_f32(0.0f),
365-
vdupq_n_f32(0.0f),
366-
vdupq_n_f32(0.0f)};
362+
float32x4_t acc0 = vdupq_n_f32(0.0f);
363+
float32x4_t acc1 = vdupq_n_f32(0.0f);
364+
float32x4_t acc2 = vdupq_n_f32(0.0f);
365+
float32x4_t acc3 = vdupq_n_f32(0.0f);
367366
int64_t idx = 0;
368367
for (; idx + kElementsPerIteration <= len; idx += kElementsPerIteration) {
369368
// See NOTE[Intrinsics in bfdot variant] above.
370369
const auto v1 = vld1q_bf16(reinterpret_cast<const bfloat16_t*>(&vec1[idx]));
371-
for (int64_t j = 0; j < 4; ++j) {
372-
const auto v2 = vld1q_bf16(
373-
reinterpret_cast<const bfloat16_t*>(&vec2[j * stride2 + idx]));
374-
acc[j] = vbfdotq_f32(acc[j], v1, v2);
375-
}
370+
const auto v20 =
371+
vld1q_bf16(reinterpret_cast<const bfloat16_t*>(&vec2[idx]));
372+
const auto v21 =
373+
vld1q_bf16(reinterpret_cast<const bfloat16_t*>(&vec2[stride2 + idx]));
374+
const auto v22 = vld1q_bf16(
375+
reinterpret_cast<const bfloat16_t*>(&vec2[2 * stride2 + idx]));
376+
const auto v23 = vld1q_bf16(
377+
reinterpret_cast<const bfloat16_t*>(&vec2[3 * stride2 + idx]));
378+
acc0 = vbfdotq_f32(acc0, v1, v20);
379+
acc1 = vbfdotq_f32(acc1, v1, v21);
380+
acc2 = vbfdotq_f32(acc2, v1, v22);
381+
acc3 = vbfdotq_f32(acc3, v1, v23);
376382
}
377383
const float32x4_t sums =
378-
vpaddq_f32(vpaddq_f32(acc[0], acc[1]), vpaddq_f32(acc[2], acc[3]));
384+
vpaddq_f32(vpaddq_f32(acc0, acc1), vpaddq_f32(acc2, acc3));
379385
vst1q_f32(out, sums);
380386
for (; idx < len; ++idx) {
381-
for (int64_t j = 0; j < 4; ++j) {
382-
out[j] += static_cast<float>(vec1[idx]) *
383-
static_cast<float>(vec2[j * stride2 + idx]);
384-
}
387+
const float v1 = static_cast<float>(vec1[idx]);
388+
out[0] += v1 * static_cast<float>(vec2[idx]);
389+
out[1] += v1 * static_cast<float>(vec2[stride2 + idx]);
390+
out[2] += v1 * static_cast<float>(vec2[2 * stride2 + idx]);
391+
out[3] += v1 * static_cast<float>(vec2[3 * stride2 + idx]);
385392
}
386393
}
387394

@@ -411,6 +418,123 @@ TARGET_ARM_BF16_ATTRIBUTE static void gemv_transa_with_fp32_arith_bfdot(
411418
#endif // COMPILER_SUPPORTS_ARM_BF16_TARGET
412419

413420
#if defined(__aarch64__)
421+
C10_ALWAYS_INLINE float32x4_t
422+
initialize_gemv_acc_neon(float beta, const float* c) {
423+
if (beta == 0.0f) {
424+
return vdupq_n_f32(0.0f);
425+
}
426+
const float32x4_t value = vld1q_f32(c);
427+
return beta == 1.0f ? value : vmulq_n_f32(value, beta);
428+
}
429+
430+
template <typename b_t>
431+
C10_ALWAYS_INLINE void gemv_notrans_block64_neon(
432+
int64_t output_offset,
433+
int64_t k,
434+
float alpha,
435+
const BFloat16* a,
436+
int64_t lda,
437+
const b_t* b,
438+
float beta,
439+
float* c) {
440+
float32x4_t acc0 = initialize_gemv_acc_neon(beta, c + output_offset);
441+
float32x4_t acc1 = initialize_gemv_acc_neon(beta, c + output_offset + 4);
442+
float32x4_t acc2 = initialize_gemv_acc_neon(beta, c + output_offset + 8);
443+
float32x4_t acc3 = initialize_gemv_acc_neon(beta, c + output_offset + 12);
444+
float32x4_t acc4 = initialize_gemv_acc_neon(beta, c + output_offset + 16);
445+
float32x4_t acc5 = initialize_gemv_acc_neon(beta, c + output_offset + 20);
446+
float32x4_t acc6 = initialize_gemv_acc_neon(beta, c + output_offset + 24);
447+
float32x4_t acc7 = initialize_gemv_acc_neon(beta, c + output_offset + 28);
448+
float32x4_t acc8 = initialize_gemv_acc_neon(beta, c + output_offset + 32);
449+
float32x4_t acc9 = initialize_gemv_acc_neon(beta, c + output_offset + 36);
450+
float32x4_t acc10 = initialize_gemv_acc_neon(beta, c + output_offset + 40);
451+
float32x4_t acc11 = initialize_gemv_acc_neon(beta, c + output_offset + 44);
452+
float32x4_t acc12 = initialize_gemv_acc_neon(beta, c + output_offset + 48);
453+
float32x4_t acc13 = initialize_gemv_acc_neon(beta, c + output_offset + 52);
454+
float32x4_t acc14 = initialize_gemv_acc_neon(beta, c + output_offset + 56);
455+
float32x4_t acc15 = initialize_gemv_acc_neon(beta, c + output_offset + 60);
456+
457+
for (int64_t l = 0; l < k; ++l) {
458+
const float b_val = static_cast<float>(b[l]) * alpha;
459+
const auto* a_col =
460+
reinterpret_cast<const uint16_t*>(a + l * lda + output_offset);
461+
462+
uint16x8_t a_bf16 = vld1q_u16(a_col);
463+
acc0 = vfmaq_n_f32(
464+
acc0,
465+
vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(a_bf16), 16)),
466+
b_val);
467+
acc1 = vfmaq_n_f32(
468+
acc1, vreinterpretq_f32_u32(vshll_high_n_u16(a_bf16, 16)), b_val);
469+
a_bf16 = vld1q_u16(a_col + 8);
470+
acc2 = vfmaq_n_f32(
471+
acc2,
472+
vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(a_bf16), 16)),
473+
b_val);
474+
acc3 = vfmaq_n_f32(
475+
acc3, vreinterpretq_f32_u32(vshll_high_n_u16(a_bf16, 16)), b_val);
476+
a_bf16 = vld1q_u16(a_col + 16);
477+
acc4 = vfmaq_n_f32(
478+
acc4,
479+
vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(a_bf16), 16)),
480+
b_val);
481+
acc5 = vfmaq_n_f32(
482+
acc5, vreinterpretq_f32_u32(vshll_high_n_u16(a_bf16, 16)), b_val);
483+
a_bf16 = vld1q_u16(a_col + 24);
484+
acc6 = vfmaq_n_f32(
485+
acc6,
486+
vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(a_bf16), 16)),
487+
b_val);
488+
acc7 = vfmaq_n_f32(
489+
acc7, vreinterpretq_f32_u32(vshll_high_n_u16(a_bf16, 16)), b_val);
490+
a_bf16 = vld1q_u16(a_col + 32);
491+
acc8 = vfmaq_n_f32(
492+
acc8,
493+
vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(a_bf16), 16)),
494+
b_val);
495+
acc9 = vfmaq_n_f32(
496+
acc9, vreinterpretq_f32_u32(vshll_high_n_u16(a_bf16, 16)), b_val);
497+
a_bf16 = vld1q_u16(a_col + 40);
498+
acc10 = vfmaq_n_f32(
499+
acc10,
500+
vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(a_bf16), 16)),
501+
b_val);
502+
acc11 = vfmaq_n_f32(
503+
acc11, vreinterpretq_f32_u32(vshll_high_n_u16(a_bf16, 16)), b_val);
504+
a_bf16 = vld1q_u16(a_col + 48);
505+
acc12 = vfmaq_n_f32(
506+
acc12,
507+
vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(a_bf16), 16)),
508+
b_val);
509+
acc13 = vfmaq_n_f32(
510+
acc13, vreinterpretq_f32_u32(vshll_high_n_u16(a_bf16, 16)), b_val);
511+
a_bf16 = vld1q_u16(a_col + 56);
512+
acc14 = vfmaq_n_f32(
513+
acc14,
514+
vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(a_bf16), 16)),
515+
b_val);
516+
acc15 = vfmaq_n_f32(
517+
acc15, vreinterpretq_f32_u32(vshll_high_n_u16(a_bf16, 16)), b_val);
518+
}
519+
520+
vst1q_f32(c + output_offset, acc0);
521+
vst1q_f32(c + output_offset + 4, acc1);
522+
vst1q_f32(c + output_offset + 8, acc2);
523+
vst1q_f32(c + output_offset + 12, acc3);
524+
vst1q_f32(c + output_offset + 16, acc4);
525+
vst1q_f32(c + output_offset + 20, acc5);
526+
vst1q_f32(c + output_offset + 24, acc6);
527+
vst1q_f32(c + output_offset + 28, acc7);
528+
vst1q_f32(c + output_offset + 32, acc8);
529+
vst1q_f32(c + output_offset + 36, acc9);
530+
vst1q_f32(c + output_offset + 40, acc10);
531+
vst1q_f32(c + output_offset + 44, acc11);
532+
vst1q_f32(c + output_offset + 48, acc12);
533+
vst1q_f32(c + output_offset + 52, acc13);
534+
vst1q_f32(c + output_offset + 56, acc14);
535+
vst1q_f32(c + output_offset + 60, acc15);
536+
}
537+
414538
template <int64_t kRegisterPairs, typename b_t>
415539
C10_ALWAYS_INLINE void gemv_notrans_block_neon(
416540
int64_t output_offset,
@@ -444,7 +568,7 @@ C10_ALWAYS_INLINE void gemv_notrans_block_neon(
444568
const float32x4_t a_low =
445569
vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(a_bf16), 16));
446570
const float32x4_t a_high =
447-
vreinterpretq_f32_u32(vshll_n_u16(vget_high_u16(a_bf16), 16));
571+
vreinterpretq_f32_u32(vshll_high_n_u16(a_bf16, 16));
448572
acc[r * 2] = vfmaq_n_f32(acc[r * 2], a_low, b_val);
449573
acc[r * 2 + 1] = vfmaq_n_f32(acc[r * 2 + 1], a_high, b_val);
450574
}
@@ -468,7 +592,7 @@ void gemv_notrans_neon(
468592
float* c) {
469593
int64_t i = 0;
470594
for (; i + 64 <= m; i += 64) {
471-
gemv_notrans_block_neon<8>(i, k, alpha, a, lda, b, beta, c);
595+
gemv_notrans_block64_neon(i, k, alpha, a, lda, b, beta, c);
472596
}
473597
for (; i + 32 <= m; i += 32) {
474598
gemv_notrans_block_neon<4>(i, k, alpha, a, lda, b, beta, c);

0 commit comments

Comments
 (0)