2012-01-19 38 views
44

我一直在使用英特爾的SSE內部函數,並獲得了很好的性能提升。因此,我期望AVX內部函數能夠進一步加速我的程序。不幸的是,直到現在,情況並非如此。可能我正在犯一個愚蠢的錯誤,所以如果有人能幫助我,我將非常感激。使用AVX intrinsics而不是SSE不會提高速度 - 爲什麼?

我使用Ubuntu 11.10和g ++ 4.6.1。我編譯了一個程序(見下文),同時

g++ simpleExample.cpp -O3 -march=native -o simpleExample 

測試系統具有英特爾i7-2600的CPU。

下面是代表我的問題的代碼。在我的系統,我得到的輸出

98.715 ms, b[42] = 0.900038 // Naive 
24.457 ms, b[42] = 0.900038 // SSE 
24.646 ms, b[42] = 0.900038 // AVX 

注意的是,計算的sqrt(平方根(SQRT(X)))只選擇,以保證內存帶寬沒有限制的執行速度;這只是一個例子。

simpleExample.cpp:

#include <immintrin.h> 
#include <iostream> 
#include <math.h> 
#include <sys/time.h> 

using namespace std; 

// ----------------------------------------------------------------------------- 
// This function returns the current time, expressed as seconds since the Epoch 
// ----------------------------------------------------------------------------- 
double getCurrentTime(){ 
    struct timeval curr; 
    struct timezone tz; 
    gettimeofday(&curr, &tz); 
    double tmp = static_cast<double>(curr.tv_sec) * static_cast<double>(1000000) 
      + static_cast<double>(curr.tv_usec); 
    return tmp*1e-6; 
} 

// ----------------------------------------------------------------------------- 
// Main routine 
// ----------------------------------------------------------------------------- 
int main() { 

    srand48(0);   // seed PRNG 
    double e,s;   // timestamp variables 
    float *a, *b;   // data pointers 
    float *pA,*pB;   // work pointer 
    __m128 rA,rB;   // variables for SSE 
    __m256 rA_AVX, rB_AVX; // variables for AVX 

    // define vector size 
    const int vector_size = 10000000; 

    // allocate memory 
    a = (float*) _mm_malloc (vector_size*sizeof(float),32); 
    b = (float*) _mm_malloc (vector_size*sizeof(float),32); 

    // initialize vectors // 
    for(int i=0;i<vector_size;i++) { 
    a[i]=fabs(drand48()); 
    b[i]=0.0f; 
    } 

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
// Naive implementation 
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
    s = getCurrentTime(); 
    for (int i=0; i<vector_size; i++){ 
    b[i] = sqrtf(sqrtf(sqrtf(a[i]))); 
    } 
    e = getCurrentTime(); 
    cout << (e-s)*1000 << " ms" << ", b[42] = " << b[42] << endl; 

// ----------------------------------------------------------------------------- 
    for(int i=0;i<vector_size;i++) { 
    b[i]=0.0f; 
    } 
// ----------------------------------------------------------------------------- 

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
// SSE2 implementation 
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
    pA = a; pB = b; 

    s = getCurrentTime(); 
    for (int i=0; i<vector_size; i+=4){ 
    rA = _mm_load_ps(pA); 
    rB = _mm_sqrt_ps(_mm_sqrt_ps(_mm_sqrt_ps(rA))); 
    _mm_store_ps(pB,rB); 
    pA += 4; 
    pB += 4; 
    } 
    e = getCurrentTime(); 
    cout << (e-s)*1000 << " ms" << ", b[42] = " << b[42] << endl; 

// ----------------------------------------------------------------------------- 
    for(int i=0;i<vector_size;i++) { 
    b[i]=0.0f; 
    } 
// ----------------------------------------------------------------------------- 

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
// AVX implementation 
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
    pA = a; pB = b; 

    s = getCurrentTime(); 
    for (int i=0; i<vector_size; i+=8){ 
    rA_AVX = _mm256_load_ps(pA); 
    rB_AVX = _mm256_sqrt_ps(_mm256_sqrt_ps(_mm256_sqrt_ps(rA_AVX))); 
    _mm256_store_ps(pB,rB_AVX); 
    pA += 8; 
    pB += 8; 
    } 
    e = getCurrentTime(); 
    cout << (e-s)*1000 << " ms" << ", b[42] = " << b[42] << endl; 

    _mm_free(a); 
    _mm_free(b); 

    return 0; 
} 

任何幫助表示讚賞!

回答

5

根據處理器硬件的不同,AVX指令可能會作爲SSE指令在硬件中仿真。您需要查看處理器的部件編號以獲取它的確切規格,但這是低端和高端intel處理器之間的主要區別之一,專用執行單元的數量與硬件仿真的數量之間的差異。

+0

我不知道,AVX是有史以來模擬 - 你對此有一個參考?在這種情況下,哪些CPU會特別如此? –

+19

在Sandy Bridge上,根據[指令表](http://www.agner.org/optimize/instruction_tables.pdf),第87-88頁,似乎VDIVPS/PD在端口0上執行2個微操作,而「DIVPS/PS」則只有1個microop。 'SQRT'指令將類似。由於除法單元沒有流水線,所以執行時間延長了兩倍。這表明桑迪橋實際上只有128位執行部門單位。 –

+0

@Norbert:感謝您的澄清 - 我不知道 –

42

這是因爲在Sandy Bridge處理器上,VSQRTPS(AVX指令)的週期數與SQRTPS(SSE指令)的週期數相同。請參閱Agner Fog的優化指南:instruction tables,第88頁。

平方根和除法指令不受益於AVX。另一方面,增加,乘法等等。

9

如果您有興趣增加平方根的表現,而不是VSQRTPS可以使用VRSQRTPS和牛頓迭代公式:

x0 = vrsqrtps(a) 
x1 = 0.5 * x0 * (3 - (a * x0) * x0) 

VRSQRTPS本身並沒有從AVX受益,但其他計算做。

如果23位精度足夠滿足您的需要,請使用它。

6

只是爲了完整。牛頓 - 拉夫遜(NR)實現像分部或平方根這樣的操作只有在代碼中的操作數量有限的情況下才有用。這是因爲如果您使用這些替代方法,您將在其他端口(例如乘法和附加端口)上產生更多壓力。這就是爲什麼x86架構有特殊硬件單元來處理這些操作的原因,而不是替代軟件解決方案(如NR)。我引用了Intel 64 and IA-32 Architectures Optimization Reference Manual p.556:

「在某些情況下,當分水嶺或平方根操作是隱藏這些操作的一些延遲的較大算法的一部分時,Newton-Raphson的近似會減慢執行速度「。

所以在大型算法中使用NR時要小心。其實,我在這方面有我的碩士論文,我將在這裏留下一個鏈接,以供將來參考。

此外,對於人們總是想知道某些指令的吞吐量和延遲,請看IACA。它是英特爾提供的一個非常有用的工具,可以靜態分析代碼的內核執行性能。

編輯 這裏是論文的鏈接那些有興趣誰thesis

相關問題