2016-04-29 8 views
1

私は組み込みのsimdサポートGCCを使っていくつかのカーネルを書くことを実験しています。私は、セグメンテーション違反私がavx_dot内の温度に初めてアクセスを得る-Fast-Mathでコンパイルすると、AVXコードのセグメンテーションが壊れますか?

g++ -O3 -march=corei7-avx dotprod.cc -ffast-math -o dotprod 

:付きでコンパイルされたとき妙に

#include <time.h> 
#include <stdio.h> 
#include <assert.h> 
#include <stdint.h> 
#include <stdlib.h> 
#include <unistd.h> 

// define rtdsc instruction 
static __inline__ uint64_t tick(void) { 
    uint32_t hi, lo; 
    __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); 
    return ((uint64_t)lo)|(((uint64_t)hi)<<32); 
} 

// AVX dot product 
float avx_dot(float* __restrict__ ans, float* __restrict__ A, float* __restrict__ B, int N, ssize_t nprod, ssize_t shift) { 
    assert(N % 32 == 0 && "N not divisible by 32"); 
    const int VECTOR_SIZE = 8; 

    typedef float vec 
     __attribute__ ((vector_size (sizeof(float) * VECTOR_SIZE))); 

    N /= VECTOR_SIZE; 

    for (ssize_t ii=0; ii < nprod; ii++) { 
     vec *Av = (vec*)A; 
     vec *Bv = (vec*)(B + ii*shift); 

     vec temp[4] = {0,0,0,0}; 
     for(int jj = 0; jj < N; jj += 4) { 
      temp[0] += Av[jj+0] * Bv[jj+0]; 
      temp[1] += Av[jj+1] * Bv[jj+1]; 
      temp[2] += Av[jj+2] * Bv[jj+2]; 
      temp[3] += Av[jj+3] * Bv[jj+3]; 
     } 

     union { 
      vec tempv; 
      float tempf[VECTOR_SIZE]; 
     }; 

     tempv = temp[0] + temp[1] + temp[2] + temp[3]; 

     ans[ii] = 0; 
     for(int jj = 0; jj < VECTOR_SIZE; ++jj) { 
      ans[ii] += tempf[jj]; 
     } 
    } 
} 

int main(int argc, const char *argv[]) { 
    const ssize_t NITER = 1000; 
    const ssize_t DECIM = atoi(argv[2]); 
    const ssize_t DOTPROD = atoi(argv[3]); 
    ssize_t size = atoi(argv[1]); 

    float* A; posix_memalign((void**)&A, 128, size*sizeof(float)); 
    float* B; posix_memalign((void**)&B, 128, (size+(DOTPROD-1)*DECIM)*sizeof(float)); 

    srand(time(NULL)); 
    for (ssize_t ii=0; ii < size;     ii++) A[ii] = rand(); 
    for (ssize_t ii=0; ii < size+(DOTPROD-1)*DECIM; ii++) B[ii] = rand(); 

    printf("# size: %i nproducts: %i shift: %i\n", size, DOTPROD, DECIM); 
    printf("# iter answer cycles seconds samprate\n"); 
    float results[DOTPROD]; 
    for (ssize_t ii=0; ii < NITER; ii++) { 
     uint64_t beg = tick(); 
     avx_dot(results, A, B, size, DOTPROD, DECIM); 
     uint64_t end = tick(); 

     float ans = 0; 
     for (ssize_t jj=0; jj < DOTPROD; jj++) { 
      ans += results[jj]; 
     } 

     double CLOCK = 3300e6; 
     uint64_t cycles = end-beg; 
     double seconds = (double)cycles/CLOCK; 
     double samprate = (size*DOTPROD)/seconds; 

     printf("%-5zd %f %lli %.3e %e\n", ii, ans, (unsigned long long)cycles, seconds, samprate); 
    } 

    return 0; 
} 

、:私はAVXドット積カーネルをベンチマークこのコードを持っています。しかし、コンパイル時に:

g++ -O3 -march=corei7-avx dotprod.cc -o dotprod 

IE、-ffast-math onを指定しないと、うまく動作します。私は高速計算が私が信じるメモリアクセスに影響を与えてはならないので非常に困惑しているので、segfaultがどこから来ているのか分かりません。

私は上の実行している:

CentOS Linux release 7.2.1511 
gcc version 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC) 

誰もが自分のマシン上でこの動作を確認し、何が起こっているかについていくつかの光を当てることができますか?

+0

は、ASMで興味深いものがありますか? – harold

+0

私は、必ずしも生の組立では最高のわけではありませんが、 –

+0

をgccの下で実行してみてください。また、 '-march = sandybridge'もあります。 IDKは '-march = corei7-avx'とどのように違うのですか?ところで、もしあなたが何とかgccにあなたのデータが整列していると言っていない限り、自動ベクトル化からの不具合を得るべきではありません。 ( 'vmovaps'は、他の命令のAVXメモリオペランドとは異なり、アラインメントのないアドレスではフォールトを起こしますが、' vmovups'は同じ性能を持っています)。 '_mm256_loadu_ps'と' _mm256_load_ps'の組み込み関数は、アライメント情報をコンパイラに伝えるために存在します。 –

答えて

3

私のランダムな推測は、データのロード(失敗した命令である.... vmovaps(%のRCX)、%ymm4 ...%RCX = 0x603228とBVは、0x603228に配置され、読取文書を失敗することを考慮すると、データ・アラインメントでありますその命令上では16バイトのアライメントの必要性が明らかになる)。

さらなる調査:Bvのが原因このラインには、Bに8つのバイトをオフセット(およびAVXは16バイトアライメントを必要とする)されたときに

問題が起こります。

vec *Bv = (vec*)(B + ii*shift); 


./dotprod-fast 64 10 10 
A=0x1125080 
B=0x1125200 
# size: 64 nproducts: 10 shift: 10 
# iter answer cycles seconds samprate 
Av=0x1125080 
Bv=0x1125200 
Av=0x1125080 
Bv=0x1125228 
Segmentation fault (core dumped) 
+2

'ymm'形式の場合、32バイトのアライメントが必要ですが、そうかもしれません。 –

+1

アライメントされたロード/ストア命令のみがAVXのアラインメント要件を満たしています。 AVXの主な機能の1つは、メモリオペランドにアライメントが必要ないため、データがアライメントされていない場合でも、 'vaddps ymm0、ymm1、[mem]'は安全です。キャッシュラインの境界を越えるためのペナルティがありますが、実行時にデータが通常は整列されている場合は、できるだけ早くアライメントされたケースを維持し、ハードウェアが小さなスローダウンでアライメントされていないケースを処理できるようにします。 –

関連する問題