自己動手寫自旋鎖

自旋鎖以其高效聞名。顧名思義,自旋即如果無法成功鎖住鎖資源則會一直循環嘗試鎖,這與互斥鎖的行為較為不同(互斥鎖如果無法鎖住則會掛起等待)。但其特性也決定了其使用場景,對於簡單幾步即可完成對共享資源操作的場景,自旋鎖效率將會高於互斥量。

本文給出自旋鎖的一種x86平臺實現。

其實,gcc在4.1以後的版本就支持了內置的自旋鎖實現,而各大類UNIX系統中也提供了pthread_spin_lock這樣的自旋鎖函數。那麼為什麼還要自己實現呢?

這是由於一些歷史遺留問題和跨平臺移植共同導致的結果,例如RHEL5 32位系統,其自帶的gcc並不支持自旋鎖,也沒有pthread_spin_lock,但我們依舊需要使用自旋鎖特性,那麼只好自己動手豐衣足食了。

閒話少敘,代碼奉上:

<code>#define barrier() asm volatile ("": : :"memory")
#define cpu_relax() asm volatile ("pause\\n": : :"memory")
static inline unsigned long xchg(void *ptr, unsigned long x)
{
__asm__ __volatile__("xchg %0, %1"
:"=r" (x)
:"m" (*(volatile long *)ptr), "0"(x)
:"memory");
return x;
}

void spin_lock(void *lock)
{
unsigned long *l = (unsigned long *)lock;
while (1) {
if (!xchg(l, 1)) return;
while (*l) cpu_relax();
}
}

void spin_unlock(void *lock)
{
unsigned long *l = (unsigned long *)lock;
barrier();
*l = 0;
}


int spin_trylock(void *lock)
{
return !xchg(lock, 1);
}/<code>

可以看到,這裡我們使用了內聯彙編,且用到了彙編指令pause。

關於pause,給出pause指令的一段解釋:

Improves the performance of spin-wait loops. When executing a "spin-wait loop," a Pentium 4 or Intel Xeon processor suffers a severe performance penalty when exiting the loop because it detects a possible memory order violation. The PAUSE instruction provides a hint to the processor that the code sequence is a spin-wait loop. The processor uses this hint to avoid the memory order violation in most situations, which greatly improves processor performance. For this reason, it is recommended that a PAUSE instruction be placed in all spin-wait loops.

大意是說這個指令可以提升自旋等待的性能,這個指令將作為對處理器的一個線索或者暗示(hint),讓處理器儘量避免該指令出現時可能存在的內存亂序存儲問題。

同時這個指令也將降低自旋等待時的電力消耗。pause指令是Pentium 4處理器引入的。


上面的例子中,我們還使用了volatile關鍵字,這個關鍵字在網上有非常多的文章詳細解釋,主要是兩方面作用:

  • 告訴編譯器該關鍵字部分不可以做任何優化
  • 使用該關鍵字的變量每次必須從內存中讀取,而不能放在cache或寄存器中備份


分享到:


相關文章: