2011-03-31 35 views
8

很明顯,沒有明確的方法或某些系統調用可幫助程序員將變量放入CPU高速緩存中。如何將我的結構變量放入CPU緩存以消除主內存頁面訪問時間?選項

但我認爲某種編程風格或精心設計的 算法可以增加將變量緩存到CPU高速緩存中的可能性。

這是我的例子:

我想在由相同類型的結構,在全局主存儲器 區域宣佈 數組的末尾附加8字節結構。

該過程持續重複進行400萬次操作。這個過程需要6秒鐘,每次操作1.5 us。我認爲這個結果表明這兩個內存區域沒有被緩存。

我從cache-oblivious algorithm得到了一些線索,所以我嘗試了幾個 的方法來加強這個。到現在爲止,沒有增強。

我覺得一些巧妙的代碼可以減少流逝的時間,最多可以減少10到100 次。請告訴我方式。

------------------------------------------------------------------------- 

附加(2011-04-01)

達蒙〜感謝您發表評論!

看完您的評論後,我再次分析了我的代碼,發現我錯過了幾件事 。我附加的以下代碼是我的原始代碼的縮寫版本。

爲了準確測量每個操作的執行時間(在原始代碼中有幾種不同類型的操作),我使用clock_gettime()函數插入了時間測量代碼。我想如果我測量每個操作的執行時間並累積它們,則可避免主循環的額外成本。

在原始代碼中,時間測量代碼被一個宏函數隱藏,所以我完全忘了它。

這段代碼的運行時間差不多是6秒。但是如果我在主循環中擺脫了時間測量功能,它會變成0.1秒。

由於clock_gettime()函數支持非常高的精度(高達1納秒),在一個獨立的線程的基礎上執行,並且它需要非常大的結構,我認爲該函數導致緩存出主存儲器執行連續插入的區域。

再次感謝您的評論。爲了進一步增強,任何建議對我優化我的代碼都會非常有幫助。

我認爲分層定義的結構變量可能會導致不必要的時間成本,但首先我想知道它會變多少,然後再將其更改爲更多的C樣式代碼。


typedef struct t_ptr { 
    uint32 isleaf :1, isNextLeaf :1, ptr :30; 
    t_ptr(void) { 
     isleaf = false; 
     isNextLeaf = false; 
     ptr = NIL; 
    } 
} PTR; 

typedef struct t_key { 
    uint32 op :1, key :31; 
    t_key(void) { 
     op = OP_INS; 
     key = 0; 
    } 
} KEY; 

typedef struct t_key_pair { 
    KEY key; 
    PTR ptr; 
    t_key_pair() { 
    } 

    t_key_pair(KEY k, PTR p) { 
     key = k; 
     ptr = p; 
    } 
} KeyPair; 

typedef struct t_op { 
    KeyPair keyPair; 
    uint seq; 
    t_op() { 
     seq = 0; 
    } 
} OP; 

#define MAX_OP_LEN 4000000 
typedef struct t_opq { 
    OP ops[MAX_OP_LEN]; 
    int freeOffset; 
    int globalSeq; 
    bool queueOp(register KeyPair keyPair); 
} OpQueue; 

bool OpQueue::queueOp(register KeyPair keyPair) { 
    bool isFull = false; 
    if (freeOffset == (int) (MAX_OP_LEN - 1)) { 
     isFull = true; 
    } 
    ops[freeOffset].keyPair = keyPair; 
    ops[freeOffset].seq = globalSeq++; 
    freeOffset++; 
} 

OpQueue opQueue; 
#include <sys/time.h> 
int main() { 
    struct timespec startTime, endTime, totalTime; 
    for(int i = 0; i < 4000000; i++) { 
     clock_gettime(CLOCK_REALTIME, &startTime); 
     opQueue.queueOp(KeyPair()); 
     clock_gettime(CLOCK_REALTIME, &endTime); 
     totalTime.tv_sec += (endTime.tv_sec - startTime.tv_sec); 
     totalTime.tv_nsec += (endTime.tv_nsec - startTime.tv_nsec); 
    } 
    printf("\n elapsed time: %ld", totalTime.tv_sec * 1000000LL + totalTime.tv_nsec/1000L); 
} 
+4

8字節* 400萬大約是32 MB。如果這需要6秒,而你的CPU不是20歲,這不僅僅是緩存,還有其他的錯誤。一個合理的新CPU將使用非優化代碼依次寫出每秒數千兆字節。你是否可能每次重新分配內存? (此外,大多數情況下,緩存對於固定步長順序訪問非常有效,CPU可以自動並很好地執行此操作,而不是通過頁面邊界) – Damon 2011-03-31 13:58:41

+0

您不會碰巧push_back到std :: vector 或任何其他東西機會? - 雖然不會那麼長,但它幾何形狀的增長... – Damon 2011-03-31 14:03:28

+0

達蒙〜謝謝你的評論! 在閱讀您的評論之後,我再次分析了我的代碼,發現了我錯過的幾件事。我把錯過的東西和我的代碼的縮寫版本附加到上面。 – Nate 2011-04-01 04:58:52

回答

3

你不把結構到任何高速緩存。 CPU會自動爲你做。 CPU比這更聰明;如果你訪問順序存儲器,它會開始將內存中的東西放入緩存中,然後你會讀取它們的

實際上,對於這樣簡單的代碼應該是常識,你花在測量上的時間比執行代碼的時間多十倍(在你的情況下顯然是60倍)。因爲你對clock_gettime()有如此多的信心:我建議你連續調用它五次並存儲結果,然後打印出不同的結果。有解決方案,有精確度,還有需要多長時間才能返回當前時間,這非常糟糕。

+0

您觸摸的每一條數據都會被加載到CPU的緩存中。有極少數例外情況(如果您分配不可緩存的內存),但不太可能會意外擊中這些內存。 CPU自動緩存數據。有些事情可以或多或少有效地使用緩存,但是(我是否在這裏重複自己?)CPU觸及的每個數據都通過CPU的緩存。 – 2015-06-10 22:33:27