2013-04-22 100 views
-1

我測試了以下7個函數,我不明白爲什麼combine7比combine5好。由於它們僅在「()」的位置不同。爲什麼combine7的性能比combine5好?

有人可以向我解釋嗎?

這裏是我的代碼:

#include "Common.h" 

#define PLUS 
#ifdef PLUS 
#define INDENT 0 
#define OP + 
#else 
#define INDENT 1 
#define OP * 
#endif 
typedef int data_t; 
typedef struct 
{ 
    long int len; 
    data_t *data; 
}vec_rec, *vec_ptr; 
vec_ptr new_vec(long int len) 
{ 
    vec_ptr result = (vec_ptr)malloc(sizeof(vec_rec)); //Allocate header structure 
    if(!result) return NULL; 
    result->len = len; 
    if(len > 0)  //Allocate array 
    { 
     data_t* data = (data_t*)calloc(len, sizeof(data_t)); 
     if(!data) 
     { 
      free((void*)result); 
      return NULL; 
     } 
     result->data = data; 
    } 
    else result->data = NULL; 
    return result; 
} 
int get_vec_element(vec_ptr v, long int index, data_t * dest) 
{ 
    if(index < 0 || index >= v->len) return 0; 
    *dest = v->data[index]; 
    return 1; 
} 
long int vec_length(vec_ptr v) 
{ 
    return v->len; 
} 
data_t* get_vec_start(vec_ptr v) 
{ 
    return v->data; 
} 

void combine5(vec_ptr v, data_t* dest) 
{ 
    long int i; 
    long int length = vec_length(v); 
    long int limit = length - 1; 
    data_t* data = get_vec_start(v); 
    data_t acc = INDENT; 

    for(i = 0; i < limit; i += 2) 
    { 
     acc = (acc OP data[i]) OP data[i + 1]; 
    } 
    for(; i < length; i++) 
     acc = acc OP data[i]; 
    *dest = acc; 
} 

void combine7(vec_ptr v, data_t* dest) 
{ 
    long int i; 
    long int length = vec_length(v); 
    long int limit = length - 1; 
    data_t* data = get_vec_start(v); 
    data_t acc = INDENT; 

    for(i = 0; i < limit; i += 2) 
    { 
     acc = acc OP (data[i] OP data[i + 1]); 
    } 
    for(; i < length; i++) 
     acc = acc OP data[i]; 
    *dest = acc; 
} 

std::mt19937 gen; 
int roll_die() { 

    std::uniform_int_distribution<> dist(1, 6); 

    return dist(gen); 

} 

int main() 
{ 
    const size_t len = 10000000; 
    auto vec_pointer = new_vec(len); 

    std::generate(vec_pointer->data, vec_pointer->data + vec_pointer->len, roll_die); 
    std::cout << "Initialized datas..." << std::endl; 
    /*std::copy(vec_pointer->data, vec_pointer->data + vec_pointer->len, 
     std::ostream_iterator<int>(std::cout, "\t"));*/ 

    data_t dest = 0; 

    utility::CStopwatch stopwatch5; 
    combine5(vec_pointer, &dest); 
    std::cout << "combine5 elapsed time(microseconds): " << stopwatch5.NowInMicro() << std::endl; 

    utility::CStopwatch stopwatch7; 
    combine7(vec_pointer, &dest); 
    std::cout << "combine7 elapsed time(microseconds): " << stopwatch7.NowInMicro() << std::endl; 
} 

這裏是我的結果:

Initialized datas... 
combine5 elapsed time(microseconds): 16934 
combine7 elapsed time(microseconds): 14858 
+0

請提出具體問題。有(7 2)這個答案。 – djechlin 2013-04-22 15:30:34

+1

猜猜6有一個錯字?不應該是'acc0 = acc0 OP data [i]; acc1 = acc1 OP data [i + 1];'? – RedX 2013-04-22 15:33:08

+0

您使用的是哪種優化級別?我認爲在適當優化的情況下(例如循環取消),時間差異會更小。 BUt也許我希望有很多表單編譯器? – ted 2013-04-22 15:34:33

回答

1

acc = (acc OP data[i]) OP data[i + 1];自然是慢於
acc = acc OP (data[i] OP data[i + 1]);

因爲在第一種情況下嘗試訪問數據元素數據[i]和在不同的操作數據[I + 1]和它導致顯著在第二種情況下,您嘗試通過(data[i] OP data[i + 1])的操作同時訪問它們,因爲它們是相鄰的存儲器位置,並且彼此迭代的速度比在不同時刻訪問它們要快。

0

如果你是在尋找是什麼使不同的功能進行不同真正感興趣的,它可能是一個想法,分析編譯器生成的彙編代碼。這些功能足夠簡單,可以在組裝時讀取,即使是通常不熟悉的功能。

在功能3取消引用在每次迭代中:

for(i = 0; i < length; i ++) 
{ 
    *dest = *dest OP data[i]; 
} 

在功能4,只在最後解除引用:

for(i = 0; i < length; i ++) 
{ 
    acc = acc OP data[i]; 
} 
*dest = acc; 

功能5,是因爲更快它僅迭代迭代次數的一半。參見:Loop unwinding

+0

我仍然想知道爲什麼解引用會有所作爲,我認爲優化編譯器只會使用寄存器來添加循環內部的內容,並將結果寫回循環/函數結尾? – ted 2013-04-22 15:37:10

+0

@ted優化器可以做很多事情,但由於我們沒有告訴任何關於優化級別的事情,我認爲解引用似乎是一個合乎邏輯的罪魁禍首。我從MSVC2010中得到的示例程序集輸出顯示,如果未啓用優化,它確實會在循環外部移動解引用。但是,是的,它應該是一個小小的變化,而且只有在沒有優化的情況下才能看到。 :) – 2013-04-22 15:41:11

0

爲什麼這些應該有很大的不同(當然,fatih_k的解釋並不能說服我)。由於您的操作符是可交換的,因此編譯器可能想要更改順序(取決於編譯器標誌)。您是否嘗試過使用不同的編譯器標誌(特別是優化標誌)和不同的編譯器(clang,gcc,icpc)?

另外,以下循環體的形式如何?

{ 
    acc *= data[i]; 
    acc *= data[i+1]; 
} 

附註:避免那些糟糕的宏。改爲寫模板代碼。

相關問題