void gemv_columnwise_stride_neon(float* result, const float* matrix, const float* vector, int m, int n, int stride) {
std::memset(result, 0, n * sizeof(float));
for (int i = 0; i < m; ++i) {
float vec_val = vector[i];
int j = 0;
for ( ; j <= n - 4; j += 4) {
float32x4_t mat_val = vld1q_f32(matrix + i * stride + j);
float32x4_t mul_val = vmulq_n_f32(mat_val, vec_val);
float32x4_t r = vld1q_f32(result + j);
vst1q_f32(result + j, vaddq_f32(r, mul_val));
}
for (; j < n; ++j) {
result[j] += matrix[i * stride + j] * vec_val;
}
}
}
此代码通过将向量元素与矩阵的每一列相乘并对结果求和来按列计算 GEMV。矩阵和向量均按行优先顺序排列。变量 result 也按行优先顺序存储结果向量。 Matrix是输入矩阵的地址,Vector是输入向量的地址,m是矩阵的行数,n是列数,stride表示行与行之间的地址偏移(即行与行之间的差值)第一行第一个元素和第二行第一个元素的位置)。我相信执行是正确的,但结果是错误的,我不确定问题出在哪里。有人可以提供提示吗?此外,是否有比这种方法更优化的按列计算 GEMV 的方法?
是否有比这种方法更优化的按列计算 GEMV 的方法?
是的。优化 GEMV 和 GEMM 以使其针对特定微架构达到最佳状态非常困难,需要大量有关管道如何处理负载使用惩罚的知识,以及适当放置数据预取以避免停顿等。
例如请参阅此处的内核列表,了解各种 Arm 平台以及不同输入数据类型和内存布局的专业化:
https://github.com/ARM-software/ComputeLibrary/tree/main/src/core/NEON/kernels/arm_gemm/kernels
这绝对不是小事。除非您纯粹将其作为学习练习,否则我会首先使用一个现成的库,该库已经针对您关心的平台进行了优化。