【发布时间】:2017-01-20 09:17:17
【问题描述】:
我正在尝试使用 GCC 向量扩展 (https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html) 来加速矩阵乘法。这个想法是使用 SIMD 指令一次将四个浮点数相乘和相加。下面列出了一个最小的工作示例。该示例在将 (M=10,K=12) 矩阵乘以 (K=12,N=12) 矩阵时工作正常。但是,当我更改参数(例如 N=9)时,出现分段错误。
我怀疑这是由于内存对齐问题。据我了解,当对 16 字节的向量(在本例中为 float4)使用 SIMD 时,目标内存地址应该是 16 的倍数。已经讨论过 SIMD 指令的内存对齐问题。 (例如Relationship between SSE vectorization and Memory alignment)。在下面的示例中,当 &b(0,0) 为 0x810e10 时,&b(1,0) 为 0x810e34,它不是 16 的倍数。
我的问题是,
- 我确实遇到了内存对齐问题的段错误吗?
- 谁能告诉我如何轻松解决问题?我曾想过使用二维数组而不是一个数组,但我不想这样做,以免更改其余代码。
最小的工作示例
#include <iostream>
#include <cstdlib>
#include <stdio.h>
#include <cstring>
#include <assert.h>
#include <algorithm>
using namespace std;
typedef float float4 __attribute__((vector_size (16)));
static inline void * alloc64(size_t sz) {
void * a = 0;
if (posix_memalign(&a, 64, sz) != 0) {
perror("posix_memalign");
exit(1);
}
return a;
}
struct Mat {
size_t m,n;
float * a;
Mat(size_t m_, size_t n_, float f) {
m = m_;
n = n_;
a = (float*) malloc(sizeof(float) * m * n);
fill(a,a + m * n,f);
}
/* a(i,j) */
float& operator()(long i, long j) {
return a[i * n + j];
}
};
Mat operator* (Mat a, Mat b) {
Mat c(a.m, b.n,0);
assert(a.n == b.m);
for (long i = 0; i < a.m; i++) {
for(long k = 0; k < a.n; k++){
float aa = a(i,k);
float4 a4 = {aa,aa,aa,aa};
long j;
for (j = 0; j <= b.n-4; j+=4) {
*((float4 *)&c(i,j)) = *((float4 *)&c(i,j)) + a4 * (*(float4 *)&b(k,j));
}
while(j < b.n){
c(i,j) += aa * b(k,j);
j++;
}
}
}
return c;
}
const int M = 10;
const int K = 12;
const int N = 12;
int main(){
Mat a(M,K,1);
Mat b(K,N,1);
Mat c = a * b;
for(int i = 0; i < M; i++){
for(int j = 0; j < N; j++)
cout << c(i,j) << " ";
cout << endl;
}
cout << endl;
}
【问题讨论】: