現代cpu的合併寫技術對程式的影響

2022-07-09 20:12:12 字數 4210 閱讀 8767

對於現代cpu而言,效能瓶頸則是對於記憶體的訪問。cpu的速度往往都比主存的高至少兩個數量級。因此cpu都引入了l1_cache與l2_cache,更加高階的cpu還加入了l3_cache.很顯然,這個技術引起了下乙個問題:

如果乙個cpu在執行的時候需要訪問的記憶體都不在cache中,cpu必須要通過記憶體匯流排到主存中取,那麼在資料返回到cpu這段時間內(這段時間大致為cpu執行成百上千條指令的時間,至少兩個資料量級)幹什麼呢? 答案是cpu會繼續執行其他的符合條件的指令。比如cpu有乙個指令序列 指令1  指令2  指令3 …, 在指令1時需要訪問主存,在資料返回前cpu會繼續後續的和指令1在邏輯關係上沒有依賴的」獨立指令」,cpu一般是依賴指令間的記憶體引用關係來判斷的指令間的」獨立關係」,具體細節可參見各cpu的文件。這也是導致cpu亂序執行指令的根源之一。

以上方案是cpu對於讀取資料延遲所做的效能補救的辦法。對於寫資料則會顯得更加複雜一點:

當cpu執行儲存指令時,它會首先試圖將資料寫到離cpu最近的l1_cache, 如果此時cpu出現l1未命中,則會訪問下一級快取。速度上l1_cache基本能和cpu持平,其他的均明顯低於cpu,l2_cache的速度大約比cpu慢20-30倍,而且還存在l2_cache不命中的情況,又需要更多的週期去主存讀取。其實在l1_cache未命中以後,cpu就會使用乙個另外的緩衝區,叫做合併寫儲存緩衝區。這一技術稱為合併寫入技術。在請求l2_cache快取行的所有權尚未完成時,cpu會把待寫入的資料寫入到合併寫儲存緩衝區,該緩衝區大小和乙個cache line大小,一般都是64位元組。這個緩衝區允許cpu在寫入或者讀取該緩衝區資料的同時繼續執行其他指令,這就緩解了cpu寫資料時cache miss時的效能影響。

當後續的寫操作需要修改相同的快取行時,這些緩衝區變得非常有趣。在將後續的寫操作提交到l2快取之前,可以進行緩衝區寫合併。 這些64位元組的緩衝區維護了乙個64位的字段,每更新乙個位元組就會設定對應的位,來表示將緩衝區交換到外部快取時哪些資料是有效的。當然,如果程式讀取已被寫入到該緩衝區的某些資料,那麼在讀取快取資料之前會先去讀取本緩衝區的。

經過上述步驟後,緩衝區的資料還是會在某個延時的時刻更新到外部的快取(l2_cache).如果我們能在緩衝區傳輸到快取之前將其盡可能填滿,這樣的效果就會提高各級傳輸匯流排的效率,以提高程式效能。

從下面這個具體的例子來看吧:

下面一段測試**,從**本身就能看出它的基本邏輯。

#include

#include

#include

#include

#include

static const int iterations = int_max;

static const int items = 1<<24;

static int mask;

static int arraya[1<<24];

static int arrayb[1<<24];

static int arrayc[1<<24];

static int arrayd[1<<24];

static int arraye[1<<24];

static int arrayf[1<<24];

static int arrayg[1<<24];

static int arrayh[1<<24];

double run_one_case_for_8()

double start_time;

double end_time;

struct timeval start;

struct timeval end;

int i = iterations;

gettimeofday(&start, null);

while(--i != 0)

int slot = i & mask;

int value = i;

arraya[slot] = value;

arrayb[slot] = value;

arrayc[slot] = value;

arrayd[slot] = value;

arraye[slot] = value;

arrayf[slot] = value;

arrayg[slot] = value;

arrayh[slot] = value;

gettimeofday(&end, null);

start_time = (double)start.tv_sec + (double)start.tv_usec/1000000.0;

end_time = (double)end.tv_sec + (double)end.tv_usec/1000000.0;

return end_time - start_time;

double run_two_case_for_4()

double start_time;

double end_time;

struct timeval start;

struct timeval end;

int i = iterations;

gettimeofday(&start, null);

while(--i != 0)

int slot = i & mask;

int value = i;

arraya[slot] = value;

arrayb[slot] = value;

arrayc[slot] = value;

arrayd[slot] = value;

i = iterations;

while(--i != 0)

int slot = i & mask;

int value = i;

arrayg[slot] = value;

arraye[slot] = value;

arrayf[slot] = value;

arrayh[slot] = value;

gettimeofday(&end, null);

start_time = (double)start.tv_sec + (double)start.tv_usec/1000000.0;

end_time = (double)end.tv_sec + (double)end.tv_usec/1000000.0;

return end_time - start_time;

int main()

mask = items -1;

int i;

printf("test begin---->\n");

for(i=0;i<3;i++)

printf(" %d, run_one_case_for_8: %lf\n", i, run_one_case_for_8());

printf(" %d, run_two_case_for_4: %lf\n", i, run_two_case_for_4());

printf("test end");

return 0;

相信很多人會認為run_two_case_for_4 的執行時間肯定要比run_one_case_for_8的長,因為至少前者多了一遍迴圈的i++操作。但是事實卻不是這樣:下面是執行的截圖:

測試環境: fedora 20 64bits, 4g ddr3記憶體,cpu:inter® core™ i7-3610qm cpu @2.30ghz.

結果是令人吃驚的,他們的效能差距居然達到了1倍,太神奇了。

原理:上面提到的合併寫存入緩衝區離cpu很近,容量為64位元組,很小了,估計很貴。數量也是有限的,我這款cpu它的個數為4。個數時依賴cpu模型的,intel的cpu在同一時刻只能拿到4個。

因此,run_one_case_for_8函式中連續寫入8個不同位置的記憶體,那麼當4個資料寫滿了合併寫緩衝時,cpu就要等待合併寫緩衝區更新到l2cache中,因此cpu就被強制暫停了。然而在run_two_case_for_4函式中是每次寫入4個不同位置的記憶體,可以很好的利用合併寫緩衝區,因合併寫緩衝區滿到引起的cpu暫停的次數會大大減少,當然如果每次寫入的記憶體位置數目小於4,也是一樣的。雖然多了一次迴圈的i++操作(實際上你可能會問,i++也是會寫入記憶體的啊,其實i這個變數儲存在了暫存器上), 但是它們之間的效能差距依然非常大。

從上面的例子可以看出,這些cpu底層特性對程式設計師並不是透明的。程式的稍微改變會帶來顯著的效能提公升。對於儲存密集型的程式,更應當考慮到此到特性。

分享我寫的CPU測試程式,看看你的CPU強勁嗎?

你是否很想知道自己的cpu到底效能如何?你是否覺得那些測試軟體太麻煩了?你是否覺得如果有免費的測試軟體就太好了?這個程式可以測試cpu整數效能 浮點數效能 快取記憶體效能和多工處理效能 還有什麼好想的呢?完全免費,綠色免安裝,簡單明瞭 記得測完之後把資料放上來,提供給大家作個比對 戀花蝶email ...

最近的cpu技術

很久沒有接觸這些了,先了解一下,以去選購一台筆記本。新名詞 首批推出的core duo處理器型號為t2x00,包括t2300 t2700等六款產品。這些處理器都是採用65奈米工藝製造,全部配備2mb共享式二級快取,前端匯流排頻率為667mhz,核心頻率從1.66ghz到2.33ghz tdp功耗為3...

cpu對程式設計的影響

由於cpu設計的不同,所以在cpu之間通訊的時候有些地方需要注意,常見的主要是位元組序和位元組對齊問題.幾乎所有的機器上,多位元組物件都被儲存為連續的位元組序列。現在有兩顆不同架構的cpu晶元,分別是低有效位被優先儲存的cpu1 小端序 和高有效位被優先儲存的cpu2 大端序 記憶體資料 0x804...