在PC處理器中,往往沒有像嵌入式系統那樣含有一塊多核共用的快取(一般是SRAM)。也就是說我們在PC處理器的多核共用變數都是統一被映射到記憶體(DDR)中去的。那麼這就會導致一系列的問題。
首先,由於每個核都有自己的獨立的L1 Cache(包括資料和指令),而多個核共用一個L2 Cache,以及更高層級的Cache。那麼在某一個核中對一個多核共用變數做讀寫操作的話,若採用一般指令進行寫,那麼這變數的值僅僅會在那個核的L1 Cache中折騰,而可能不會影響到儲存空間中的那個真正的對象。
然後,這也會導致在某一個核中,你每次對該共用變數進行讀操作時,其值可能不是最新的,即,在你剛讀好這個變數時它可能已經被其它核給修改了。
因此,這得出一個結論:你不能用普通的寫操作來修改多核共用變數;在你讀多核共用變數前必須確保其它核沒有再次修改它。
我們對多核共用變數進行修改時,會採取具有鎖匯流排特性的指令。在x86中有:xchg、cmpxchg等,除此以外還有帶有LOCK首碼的基本算術運算操作。在Blackfin561 Dual-Core DSP中,可以使用testset指令;ARMv7以前的處理器可以用SWP和SWPB,在ARMv7架構中加入了更強大的LDREX和STREX對,形成LL-SC作為原子操作指令對。
因此,如果我們將多核共用變數作為同步鎖的話,那麼用SWAP、TEST AND SET等指令對其進行讀、寫同時操作,並且加鎖匯流排。如果我們在操作一個多核共用的資料結構,那麼我們在操作前應該上鎖,然後每次對必要結果的修改應該用上述帶有鎖匯流排的指令進行操作。還有一種是用普通的寫指令將結果寫到Cache後,然後再無效化當前核的整個Cache。但是這個做法也很耗時間,而且原本已經進Cache的資料也都被清空了。
當然,在很多時候,我們為了圖個方便,會把相同的資料結構賦值多份給每個核,呵呵。雖然這樣會比較浪費空間,但也是一種手段。
下面給各位看一下加鎖匯流排和沒有鎖匯流排的執行區別:
#include <stdio.h></p><p>#define INTRIN_RDTSC(tick)__asm__("rdtsc" : "=A"(tick))</p><p>int main(void)<br />{<br /> register int a = 100;<br /> int m = 10;<br /> unsigned long long beginTicks, endTicks;<br /> // register type swap<br /> INTRIN_RDTSC(beginTicks);</p><p> for(int i=0; i<10000; i++)<br /> __asm__ volatile("xchg %0, %1" : : "r"(a), "r"(m));</p><p> INTRIN_RDTSC(endTicks);</p><p> printf("The register type swap is: %llu/r/n", endTicks - beginTicks);</p><p> // memory type swap<br /> INTRIN_RDTSC(beginTicks);</p><p> for(int i=0; i<10000; i++)<br /> __asm__ volatile("xchg %0, (%1)" : : "r"(a), "r"(&m));</p><p> INTRIN_RDTSC(endTicks);</p><p> printf("The memory type swap is: %llu/r/n", endTicks - beginTicks);</p><p> // normal add<br /> INTRIN_RDTSC(beginTicks);</p><p> for(int i=0; i<10000; i++)<br /> __asm__ volatile("add %0, (%1)" : : "r"(a), "r"(&m));</p><p> INTRIN_RDTSC(endTicks);</p><p> printf("The normal add swap is: %llu/r/n", endTicks - beginTicks);</p><p> // lock add<br /> INTRIN_RDTSC(beginTicks);</p><p> for(int i=0; i<10000; i++)<br /> __asm__ volatile("lock add %0, (%1)" : : "r"(a), "r"(&m));</p><p> INTRIN_RDTSC(endTicks);</p><p> printf("The lock add is: %llu/r/n", endTicks - beginTicks);<br />}<br />
上述代碼,使用LLVM1.5,GNU99編譯選項編譯