2011-08-04 114 views
13

我是使用gcc內聯彙編的新手,並且想知道在x86多核計算機上是否可以實現自旋鎖(無競態條件)(使用AT & T語法):x86自旋鎖使用cmpxchg

 
spin_lock: 
mov 0 eax 
lock cmpxchg 1 [lock_addr] 
jnz spin_lock 
ret 

spin_unlock: 
lock mov 0 [lock_addr] 
ret 

回答

21

你有正確的想法,但你的彙編被打破:

cmpxchg不能立即數工作,只註冊。

lock不是mov的有效前綴。 mov到一個對齊的地址在x86上是原子的,所以你不需要lock

它已經有一段時間,因爲我已經使用AT & T語法,希望我記得一切:

spin_lock: 
xorl %ecx, %ecx 
incl %ecx 
spin_lock_retry: 
xorl %eax, %eax 
lock; cmpxchgl %ecx, (lock_addr) 
jnz spin_lock_retry 
ret 

spin_unlock: 
movl $0 (lock_addr) 
ret 

注意,GCC有原子建宏,所以你實際上並不需要使用內聯彙編來做到這一點:

void spin_lock(int *p) 
{ 
    while(!__sync_bool_compare_and_swap(p, 0, 1)); 
} 

void spin_unlock(int volatile *p) 
{ 
    asm volatile (""); // acts as a memory barrier. 
    *p = 0; 
} 

博下面說,鎖定指令產生費用:你使用任何人都必須刷新緩存並鎖定您的系統的內存總線,它可以是相當昂貴的,如果你有足夠的CPU。即使沒有很多的CPU,它仍然容易,值得各地優化:

void spin_lock(int volatile *p) 
{ 
    while(!__sync_bool_compare_and_swap(p, 0, 1)) 
    { 
     while(*p) _mm_pause(); 
    } 
} 

pause指令是對超線程CPU的性能至關重要,當你有一個不斷旋轉這樣的代碼 - 它可以讓第二個線程執行而第一個線程正在旋轉。在不支持pause的CPU上,它被視爲nop

+0

應爲無效的用spin_lock參數()也聲明揮發性? – ManRow

+1

編號'__sync_bool_compare_and_swap'已將其視爲'volatile'。 –

+0

'spin_unlock'內用作內存屏障的asm應該包含內存clobber。另一方面,雖然'__sync_lock_release'被設計用來做「寫屏障,寫0」的東西,根本不需要考慮asm,它甚至「有點便攜」。它沒有明確地作爲讀取障礙(它在目標體系結構上完成),但沒關係。最糟糕的事情發生的另一個線程在罕見的情況下做一個額外的旋轉。 – Damon

3

這將少放爭內存總線上:

void spin_lock(int *p) 
{ 
    while(!__sync_bool_compare_and_swap(p, 0, 1)) while(*p); 
} 
+0

同意,但這段代碼不太好。一個簡單的while(* p)可以很容易地被編譯器優化。增加一些障礙。另外,爲英特爾芯片添加_mm_pause()可顯着提高性能。 –