標 題: volatile和原子操作
時 間: thu aug 6 12:16:53 2009
點 擊: 26
所謂原子操作,就是"不可中斷的乙個或一系列操作" , 在確認乙個操作是原子的情況下,
多執行緒環境裡面,我們可以避免僅僅為保護這個操作在外圍加上效能昂貴的鎖,甚至借助
於原子操作,我們可以實現互斥鎖。
很多作業系統都為int型別提供了+-賦值的原子操作版本,比如 nt 提供了
interlockedexchange 等api, linux/unix也提供了atomic_set 等函式。
前兩天有同學問我:在x86上,g_count++ (int型別) 是否是乙個原子操作? 我的回答是
"不是的, 多個cpu的機器(smp)上面這就不是原子操作"。
今天想起,在單cpu上這個是否是原子操作呢,但是這個和編譯器有關,編譯器可能有兩種
編譯方式:
a. 多條指令版本 , 這就不是原子的
mov 暫存器 , g_count
add 暫存器, 1
mov g_count , 暫存器
b. 單指令版本, 這在單cpu的x86上就是原子的
inc g_count
只能寫程式驗證了, 讓5個執行緒每個對 g_count++ 一億次,假如是原子操作的話,結果應
該是5億:
其實還需要對 g_count 進行volatile宣告,防止編譯器對這裡不適當的優化,為了看看編
譯器對volatile的處理,我另外做了個volatile版本作為比較。
#include
#include
int g_count = 0;
dword winapi threadfunc( lpvoid lpparam )
#define thread_num 5
void main( void )
{dword dwthreadid;
handle hthread;
int i;
for (i=0;i0) 則繼續迴圈
終於發現了問題所在了, 優化以後,迴圈從i++變成了i--, 就是如下的形式:
for (i=100000000; i >0 ; i--)
g_count++;
因為將乙個數字和0比較和將其與其他數字比較更加有效率優勢,而且這裡i在迴圈體裡面
並不使用,所以vc編譯器將其變換成上面的形式,可以大大節省迴圈執行的時鐘週期。
這樣,未優化的版本有很大的機會出現 g_count == 五億 就有了解釋,是因為:
cpu對於純粹的整數運算是很快的,一億次迴圈裡面,可能只有一兩次的執行緒上下文切換
沒有優化的版本迴圈體比++操作本身更加耗時,這樣切換操作很可能出現在 for 迴圈中,
而不是 g_count++ 的三條指令之間
這裡也證明了vc6編譯器對於 ++ 的執行**是是非原子的,查了一下資料 這3條指令在
pentium以後的cpu比一條inc更快
然後再檢查沒有加volatile的優化版本
發現彙編**的迴圈體完全沒有了:
mov eax, dword ptr _g_count
push esi
add eax, 100000000 ; 05f5e100h
表示成c的**大概就是這樣: g_count+=100000000; 編譯器還是很聰明,發現這個循
環其實使用前面的語句也可以達到目的,乾脆把迴圈拿掉了,這樣因為執行緒執行時間很短
,往往乙個執行緒都執行完了其他執行緒還沒有被排程,所以結果都是5億了。
附帶以下總結:
1. 不要小看編譯器的聰明程度,上面的那些優化,我在gcc上也作了驗證,我們不要太在
意i++/++i之類的優化,要相信編譯器能做好它
2. ++的操作在單cpu的x86上也不是原子性的,所以優化多執行緒效能的兄弟不要在這裡搞過
火,老實用 interlockedincrement 吧
3. x86上,不管是否smp, 對於int(要求位址4 bytes對齊)的讀取和賦值還是原子的,不過
這個就和這個試驗無關了(risc的機器就不要這樣做了,大家還是加鎖吧)
volatile和原子操作
所謂原子操作,就是 不可中斷的乙個或一系列操作 在確認乙個操作是原子的情況下,多執行緒環境裡面,我們可以避免僅僅為保護這個操作在外圍加上效能昂貴的鎖,甚至借助於原子操作,我們可以實現互斥鎖。很多作業系統都為int型別提供了 賦值的原子操作版本,比如 nt 提供了 interlockedexchang...
原子性和volatile
所謂原子操作,就是 不可中斷的乙個或一系列操作 在確認乙個操作是原子的情況下,多執行緒環境裡面,我們可以避免僅僅為保護這個操作在外圍加上效能昂貴的鎖,甚至借助於原子操作,我們可以實現互斥鎖。很多作業系統都為int型別提供了 賦值的原子操作版本,比如 nt 提供了 interlockedexchang...
Volatile關鍵字與原子性操作
原子性操作具有不可分割性。比如 i 0 這個操作是不可分割的,那麼我們說這個操作時原子操作。再比如 i 這個操作實際是 i i 1 那麼x y 這個操作是不是原子性操作呢?答案也是否定的,該操作可以向下細分為首先讀取y的資料,再賦值給x的過程。注意 在32位平台下,對64位資料的讀取和賦值是需要通過...