2012-09-28 27 views
2

我想了解嚴格別名對C99性能的影響。我的目標是優化矢量點產品,這在我的程序中佔用了大量時間(對其進行了剖析!)。我認爲別名可能是問題,但下面的代碼並沒有顯示標準方法和嚴格別名版本之間的任何實質性區別,即使是大小爲1億的向量。我也嘗試使用局部變量來避免別名,並得到相似的結果。GCC C99中指針的嚴格別名示例,沒有性能差異

發生了什麼事?

我在OSX 10.7.4上使用gcc-4.7。結果以微秒爲單位。

$ /usr/local/bin/gcc-4.7 -fstrict-aliasing -Wall -std=c99 -O3 -o restrict restrict.c 
$ ./restrict 
sum: 100000000 69542 
sum2: 100000000 70432 
sum3: 100000000 70372 
sum4: 100000000 69891 
$ /usr/local/bin/gcc-4.7 -Wall -std=c99 -O0 -fno-strict-aliasing -o restrict restrict.c 
$ ./restrict 
sum: 100000000 258487 
sum2: 100000000 261349 
sum3: 100000000 258829 
sum4: 100000000 258129 

restrict.c(注意,這個程序需要幾百MB RAM):

#include <stdlib.h> 
#include <stdio.h> 
#include <time.h> 
#include <sys/time.h> 
#include <unistd.h> 

/* original */ 
long sum(int *x, int *y, int n) 
{ 
    long i, s = 0; 

    for(i = 0 ; i < n ; i++) 
     s += x[i] * y[i]; 

    return s; 
} 

/* restrict */ 
long sum2(int *restrict x, int *restrict y, int n) 
{ 
    long i, s = 0; 

    for(i = 0 ; i < n ; i++) 
     s += x[i] * y[i]; 

    return s; 
} 

/* local restrict */ 
long sum3(int *x, int *y, int n) 
{ 
    int *restrict xr = x; 
    int *restrict yr = y; 
    long i, s = 0; 

    for(i = 0 ; i < n ; i++) 
     s += xr[i] * yr[i]; 

    return s; 
} 

/* use local variables */ 
long sum4(int *x, int *y, int n) 
{ 
    int xr, yr; 
    long i, s = 0; 

    for(i = 0 ; i < n ; i++) 
    { 
     xr = x[i]; 
     yr = y[i]; 
     s += xr * yr; 
    } 

    return s; 
} 

int main(void) 
{ 
    struct timeval tp1, tp2; 
    struct timezone tzp; 

    long i, n = 1e8L, s; 
    int *x = malloc(sizeof(int) * n); 
    int *y = malloc(sizeof(int) * n); 
    long elapsed1; 

    for(i = 0 ; i < n ; i++) 
     x[i] = y[i] = 1; 

    gettimeofday(&tp1, &tzp); 
    s = sum(x, y, n); 
    gettimeofday(&tp2, &tzp); 
    elapsed1 = (tp2.tv_sec - tp1.tv_sec) * 1e6 
     + (tp2.tv_usec - tp1.tv_usec); 
    printf("sum:\t%ld\t%ld\n", s, elapsed1); 

    gettimeofday(&tp1, &tzp); 
    s = sum2(x, y, n); 
    gettimeofday(&tp2, &tzp); 
    elapsed1 = (tp2.tv_sec - tp1.tv_sec) * 1e6 
     + (tp2.tv_usec - tp1.tv_usec); 
    printf("sum2:\t%ld\t%ld\n", s, elapsed1); 

    gettimeofday(&tp1, &tzp); 
    s = sum3(x, y, n); 
    gettimeofday(&tp2, &tzp); 
    elapsed1 = (tp2.tv_sec - tp1.tv_sec) * 1e6 
     + (tp2.tv_usec - tp1.tv_usec); 
    printf("sum3:\t%ld\t%ld\n", s, elapsed1); 

    gettimeofday(&tp1, &tzp); 
    s = sum3(x, y, n); 
    gettimeofday(&tp2, &tzp); 
    elapsed1 = (tp2.tv_sec - tp1.tv_sec) * 1e6 
     + (tp2.tv_usec - tp1.tv_usec); 
    printf("sum4:\t%ld\t%ld\n", s, elapsed1); 

    return EXIT_SUCCESS; 
} 

回答

1

即興:

  • 沒有嚴格別名規則,編譯器可能只是簡單地生成優化的代碼,可以完成比預期稍微不同的事情。

  • 禁止嚴格的別名規則會導致代碼更快,這是不對的。

  • 如果確實如此,這是沒有一個給定的優化代碼實際上顯示不同的結果。這很大程度上取決於實際的數據訪問模式,通常甚至是處理器/緩存體系結構。

關於您的示例代碼,我會說,混疊是不相干(用於發射的代碼,至少),因爲從未有裏面的sumXXX數組元素的任何寫訪問功能。如果你傳遞兩次相同的向量,你可能會得到稍微好一點的性能(或者相反),從熱緩存和較小的緩存足跡中可能會有一些好處,可能會由於冗餘負載將預取預測器關閉而受到損失,跟蹤一如往常:使用一個分析器

+1

sehe是絕對正確的,別名是在這種情況下無關緊要,因爲沒有寫訪問。以防萬一你真的想知道某個地方是否是別名問題,你只需要看看彙編輸出,尤其是。在負載和存儲在那裏。 – flolo