ARMv7-a平台基于Neon的4x4 int32_t矩阵乘法问题求助
ARMv7-A平台Neon实现4x4 int32矩阵乘法的修正方案
问题概述
在ARMv7-A平台尝试用Neon指令实现4x4 int32_t矩阵乘法时,改编的代码计算结果与常规实现不符。因ARMv7不支持vfmaq_laneq_s32指令,需调整实现逻辑。
错误结果对比:
Neon矩阵:
538 612 686 760
650 740 830 920
762 868 974 1080
874 996 1118 1240正确矩阵:
250 260 270 280
618 644 670 696
986 1028 1070 1112
1354 1412 1470 1528
错误代码分析
原代码的核心问题在于矩阵维度处理错误:
- 加载B矩阵时按行读取,但矩阵乘法需要使用B的列元素,原代码未正确提取列数据
- 乘加逻辑错误:将A的所有行与B的某一行元素相乘累加,不符合矩阵乘法
C[i][j] = sum(A[i][k] * B[k][j])的计算规则
修正后的Neon实现代码
#include <arm_neon.h> #include <stdio.h> #include <stdint.h> #include <string.h> void multiply_matrix_neon(int32_t *A, int32_t *B, int32_t *C, int n) { // 加载A的所有行(行优先存储) int32x4_t A0 = vld1q_s32(A); int32x4_t A1 = vld1q_s32(A + 4); int32x4_t A2 = vld1q_s32(A + 8); int32x4_t A3 = vld1q_s32(A + 12); // 初始化C的四个行累加器 int32x4_t C0 = vmovq_n_s32(0); int32x4_t C1 = vmovq_n_s32(0); int32x4_t C2 = vmovq_n_s32(0); int32x4_t C3 = vmovq_n_s32(0); // 遍历B的每一列,计算A的行与B的列的点积 for (int k = 0; k < n; k++) { // 提取B的第k列的元素,广播为4元素向量(适配ARMv7指令集) int32_t b_col = B[k * n]; int32x4_t B_col0 = vdupq_n_s32(b_col); b_col = B[k * n + 1]; int32x4_t B_col1 = vdupq_n_s32(b_col); b_col = B[k * n + 2]; int32x4_t B_col2 = vdupq_n_s32(b_col); b_col = B[k * n + 3]; int32x4_t B_col3 = vdupq_n_s32(b_col); // 乘加运算:C的行 = 原C行 + A的行 * B的当前列广播向量 C0 = vmlaq_s32(C0, A0, B_col0); C1 = vmlaq_s32(C1, A0, B_col1); C2 = vmlaq_s32(C2, A0, B_col2); C3 = vmlaq_s32(C3, A0, B_col3); C0 = vmlaq_s32(C0, A1, B_col0); C1 = vmlaq_s32(C1, A1, B_col1); C2 = vmlaq_s32(C2, A1, B_col2); C3 = vmlaq_s32(C3, A1, B_col3); C0 = vmlaq_s32(C0, A2, B_col0); C1 = vmlaq_s32(C1, A2, B_col1); C2 = vmlaq_s32(C2, A2, B_col2); C3 = vmlaq_s32(C3, A2, B_col3); C0 = vmlaq_s32(C0, A3, B_col0); C1 = vmlaq_s32(C1, A3, B_col1); C2 = vmlaq_s32(C2, A3, B_col2); C3 = vmlaq_s32(C3, A3, B_col3); } // 将结果存储到C矩阵(行优先) vst1q_s32(C, C0); vst1q_s32(C + 4, C1); vst1q_s32(C + 8, C2); vst1q_s32(C + 12, C3); } void multiply_matrix_normal(int32_t *A, int32_t *B, int32_t *result, int n) { int32_t temp[16] = {0}; for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { for (int k = 0; k < n; ++k) { temp[i * n + j] += A[i * n + k] * B[k * n + j]; } } } memcpy(result, temp, n * n * sizeof(int32_t)); } void print_matrix(int32_t* matrix, int n) { for (size_t i = 0; i < n; i++) { for (size_t j = 0; j < n; j++) { printf("%d ", matrix[i * n + j]); } printf("\n"); } printf("\n"); } int main() { int32_t A[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; int32_t B[16] = { 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }; int32_t neon[16] = {0}; multiply_matrix_neon(A, B, neon, 4); printf("Neon matrix:\n"); print_matrix(neon, 4); int32_t normal[16] = {0}; multiply_matrix_normal(A, B, normal, 4); printf("Normal matrix:\n"); print_matrix(normal, 4); return 0; }
关键修正点
- 列元素广播:使用
vdupq_n_s32将B的单个列元素广播为4元素向量,适配ARMv7的Neon指令集(避免使用ARMv8的vfmaq_laneq_s32) - 正确乘加逻辑:遍历B的每一列,将A的每一行与当前列的广播向量相乘后累加,严格遵循矩阵乘法的点积规则
- 结果存储对齐:按行优先顺序存储计算结果,与常规实现的内存布局保持一致
验证结果
修正后的Neon实现输出将与常规实现完全一致,符合矩阵乘法的正确结果。
内容的提问来源于stack exchange,提问作者stripedapus
相关产品推荐
相关产品推荐

