行列の乗算を高速化するために、GCCベクター拡張子(https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html)を使用しようとしています。この考え方は、SIMD命令を使用して4つの浮動小数点数を一度に増やして加算することです。最小限の作業例を以下に示します。この例は、(M = 10、K = 12)行列に(K = 12、N = 12)行列を乗算するとうまく動作します。しかし、パラメータを変更すると(N = 9など)、セグメント化エラーが発生します。GCCベクター拡張でのメモリーアライメントの問題
これはメモリアライメントの問題によるものだと思われます。私の理解では、16バイト(この場合はfloat4)のベクトルにSIMDを使用する場合、ターゲットメモリアドレスは16の倍数でなければなりません。SIMD命令によるメモリアラインメントの問題については既に議論があります。 (例えば、Relationship between SSE vectorization and Memory alignment)。以下の例では、& B(0,0)0x810e10あるとき、& B(1,0)は、私の質問がある16
の倍数ではありません。これは、0x810e34ある
- がそれです私がメモリアライメントの問題のセグメンテーションを取得しているのは本当ですか?
- 問題を簡単に解決する方法を教えてもらえますか?私は1つの配列の代わりに2次元配列を使うことを考えましたが、残りのコードを変更しないようにこれをしたくありません。
最小実施例私の理解では
#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;
}
ありがとうございました!あなたの提案に基づいて私のコードを修正するのは余りにも忙しいですが、私はあとでフォローアップ投稿を書くつもりです。 – user3127171