2016-04-29 40 views
1

我正在嘗試使用GCCs內建simd支持編寫幾個內核。當編譯用-ffast-math編譯AVX代碼段錯誤?

#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; 
} 

奇怪,:

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

我得到一個segfault我第一次訪問內avx_dot臨時我有了這個代碼基準的AVX​​積內核。但是,編譯時:

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

IE,沒有-ffast-math,它運行良好。我很困惑,因爲快速數學不應該影響我相信的內存訪問,所以我不知道段錯誤來自哪裏。

我上運行:

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下運行它,並向我們展示它錯誤的insn。另外,還有'-march = sandybridge'。 IDK與'-march = corei7-avx'不同。順便說一句,你不應該從自動矢量化中得到錯誤,除非你以某種方式告訴gcc你的數據是一致的,但事實證明它不是。 ('vmovaps'將在未對齊的地址上出錯,不像AVX內存操作數指向其他指令。'vmovups'具有相同的性能。)哦,你確實是這樣做的,我想這就是typedef。 '_mm256_loadu_ps'和'_mm256_load_ps'內在函數存在以將對齊信息傳遞給編譯器。 –

回答

3

我的隨機猜測是數據對齊,考慮到它無法加載數據(失敗的指令是.... vmovaps(%rcx),%ymm4 ...%rcx = 0x603228和Bv位於0x603228,並閱讀文檔在該指令上顯示需要16個字節對齊)。

進一步調查:

問題發生在BV偏移8個字節到B,由於這條線(和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

它需要32個字節對​​齊'ymm'形式,但是,你可能是對的。 –

+1

只有對齊的加載/存儲指令在AVX中具有對齊要求。主要的AVX特性之一是內存操作數不需要對齊,所以'vaddps ymm0,ymm1,[mem]'是安全的,即使數據有時可能不對齊也是如此。對於跨越緩存行邊界有一個懲罰,但如果數據通常在運行時對齊,那麼保持對齊的情況儘可能快並且讓硬件處理未對齊的情況只需要一個小的緩慢。 –