Skip to content

Commit e9d492a

Browse files
CrispStrobeclaude
andcommitted
perf(firered): link OpenMP + vectorizable dot — ~4x faster AED decoder
The firered-asr target never linked OpenMP, so cpu_matmul_bt's `#pragma omp parallel for` were no-ops and the AED beam decoder ran single-threaded (~1443 ms/token at beam 5 on an RTX A1000's CPU). Two fixes: 1. Link firered-asr with OpenMP::OpenMP_CXX so the matmuls actually parallelize (the pragmas were already there). 2. Replace the double-accumulate dot in cpu_matmul_bt with cpu_dot() — four independent float accumulator chains the compiler can vectorize under strict FP. Float accumulation over K~1280 is within ASR-logit tolerance. Decoder: 1443 -> 365 ms/token (3.95x). issue19-5s 0.2x -> 0.6x RT; issue19-35s 223 s -> 111 s (2x wall). Transcripts byte-identical. Decoder is now partially bandwidth-bound (reads ~1.5 GB F32 weights per beam) — batching the beams is the next lever. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent b146243 commit e9d492a

2 files changed

Lines changed: 32 additions & 13 deletions

File tree

src/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,13 @@ set_target_properties(firered-asr PROPERTIES POSITION_INDEPENDENT_CODE ON)
13601360
target_include_directories(firered-asr PUBLIC .)
13611361
target_compile_features (firered-asr PUBLIC cxx_std_17)
13621362
target_link_libraries (firered-asr PUBLIC crispasr-core ggml ggml-base ggml-cpu)
1363+
# firered_asr.cpp's CPU decoder/encoder matmuls (cpu_matmul_bt) are
1364+
# `#pragma omp parallel for`, but the target never linked OpenMP — so the
1365+
# pragmas were no-ops and the AED beam decoder ran single-threaded
1366+
# (~1.4 s/token at beam 5). Link OpenMP so the matmuls actually parallelize.
1367+
if (OpenMP_CXX_FOUND)
1368+
target_link_libraries(firered-asr PUBLIC OpenMP::OpenMP_CXX)
1369+
endif()
13631370
if(NOT MSVC)
13641371
target_compile_options(firered-asr PRIVATE -O3)
13651372
endif()

src/firered_asr.cpp

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -692,27 +692,39 @@ static void read_f32_vec(ggml_tensor* t, std::vector<float>& out) {
692692

693693
// CPU matmul: C = A @ B^T where A is [M,K], B is [N,K] → C is [M,N]
694694
// (B stored as [N,K] row-major, like ggml weight [K,N] with ne[0]=K)
695+
// Dot product of two length-K float vectors using four independent
696+
// accumulator chains so the compiler can vectorize the reduction even under
697+
// strict FP (no -ffast-math / /fp:fast needed). Float accumulation over
698+
// K~1280 is well within tolerance for ASR logits; the old double-accumulate
699+
// form forced scalar, non-vectorized code and dominated the decoder.
700+
static inline float cpu_dot(const float* a, const float* b, int K) {
701+
float s0 = 0, s1 = 0, s2 = 0, s3 = 0;
702+
int k = 0;
703+
for (; k + 4 <= K; k += 4) {
704+
s0 += a[k + 0] * b[k + 0];
705+
s1 += a[k + 1] * b[k + 1];
706+
s2 += a[k + 2] * b[k + 2];
707+
s3 += a[k + 3] * b[k + 3];
708+
}
709+
float s = (s0 + s1) + (s2 + s3);
710+
for (; k < K; k++)
711+
s += a[k] * b[k];
712+
return s;
713+
}
714+
695715
static void cpu_matmul_bt(const float* A, const float* B, float* C, int M, int K, int N) {
696716
if (M == 1) {
697717
// Single-vector × matrix: parallelize over output dimension N.
698718
// This is the decoder hot path (one token per step).
699719
#pragma omp parallel for schedule(static)
700-
for (int n = 0; n < N; n++) {
701-
double s = 0;
702-
const float* brow = B + n * K;
703-
for (int k = 0; k < K; k++)
704-
s += (double)A[k] * (double)brow[k];
705-
C[n] = (float)s;
706-
}
720+
for (int n = 0; n < N; n++)
721+
C[n] = cpu_dot(A, B + (size_t)n * K, K);
707722
} else {
708723
#pragma omp parallel for schedule(static)
709724
for (int m = 0; m < M; m++) {
710-
for (int n = 0; n < N; n++) {
711-
double s = 0;
712-
for (int k = 0; k < K; k++)
713-
s += (double)A[m * K + k] * (double)B[n * K + k];
714-
C[m * N + n] = (float)s;
715-
}
725+
const float* arow = A + (size_t)m * K;
726+
for (int n = 0; n < N; n++)
727+
C[(size_t)m * N + n] = cpu_dot(arow, B + (size_t)n * K, K);
716728
}
717729
}
718730
}

0 commit comments

Comments
 (0)