接上文:linux編程的108種奇淫巧計-15(減少複製)
上回提到了用movntdq指令寫xmm寄存器的方式,可以獲得效能上的收益,並且指出在預設的情況下,在發生寫入的時候,需要先從低層次儲存層次refill到高層次儲存層次(可以簡單認為從記憶體refill進cache),然後再cache中更新,再write back,特彆強調的是,這是預設的方式。
實際上,高一級儲存層次,向低一級儲存層次寫入有兩種方式,一種是預設的write back,一種是write through。而我們使用的movntdq指令就是write through方式,必須通過彙編的方式來實現,進階語言通常沒有這個效果。怎麼理解write back和write through的區別呢?
對於write back,因為我們知道機器儲存層次分為多層,為了保證一致性,必須每一層相同的資料都是一致的,不可能分秒都是一致的,但是在不影響計算邏輯的前提下,需要有一致性的保證,那麼write back方式在寫的時候有兩種情況:A)當寫入的地址恰好在cache的某條cache line中,那麼直接在cache中寫入資料,並且標記該cache line為dirty,此後不發生對記憶體實際地址的寫回,直到發生cache被替換策略時,才將dirty的這條cache line寫入到記憶體中。B)當寫入的地址恰好不在cache中(任何一條cache line都不match),首先需要執行一次refill操作,將記憶體中的資料讀入到cache line中,然後將資料寫入該cache line,此後不發生對記憶體位址的寫回,直到發生cache被替換策略時,才將dirty的這條cache lien寫入記憶體。
舉個例子:
某個程式需要執行這樣幾條命令,按順序如下:
(1)在地址0x00FFFF00處寫入64個位元組的資料[PENNYLIANG...]
(2)其他命令(64個位元組保持在cache中,沒有被替換掉)
(3)讀取0x00FFFF00處的64個位元組的資料
(4) 某條命令(該命令將[PENNYLIANG...]這段資料換出了cache(L1 cache只有32k,必然會有被換出的可能)
(5)讀取0x00FFFF00處的64個位元組的資料
(6) 某條命令(該命令將[PENNYLIANG...]這段資料又一次換出了cache
(7) 在地址0x00FFFF00處寫入64個位元組的資料[奇淫巧計...]
(8) 某條命令(該命令將[奇淫巧計...]這段資料又一次換出了cache
從此前介紹的內容,我們不難得到這樣幾個結論:
1)在命令(4)之前,寫入的資料總是保持在cache中,命令3從cache中讀取的[PENNYLIANG...]
2) 命令(4)導致了cache中資料和記憶體的一次同步
3) 命令(5)發生了一次cache read miss,需要從記憶體中refill[PENNYLIANG...]到cache中。
4)命令(7)發生了一次cache write miss,需要兩次記憶體訪問實現寫入,第一次refill,另一次在指令(8)時write back。
不難得到,write back在程式局部性好的情況下,是非常有利的,只要cache 命中了,就可以只在cache中轉,而不發生寫回,比如指令(3)就直接在cache中得到,如果對這個資料多次寫,那麼直接在cache中發生,根本不會每次寫都體現在記憶體上。
在理解了write back後,就很容易理解write through了,write through直接從cache寫穿到記憶體,只有單向的過程,沒有記憶體到cache的refill,相當於減少了低層儲存層次資料向高層儲存層次的複製過程,在大規模資料寫入的時候才能體現威力。
我們這裡舉得例子是cache,或者說L1 cache和記憶體的關係,廣義上看是高層次儲存結構和低層次儲存結構的關係,記憶體和磁碟也可以構成這樣一個write back和write through的情況,可以看到在小資料量的情況下write back總是最好的,而當發生大量資料寫入時write through的方式會更有優勢,以後我們會給出這個例子。
參考閱讀:
http://www-903.ibm.com/kr/techinfo/xseries/download/write_back_vs_write_through.pdf
http://en.wikipedia.org/wiki/CPU_cache