2011-08-04 2 views
13

gcc 인라인 어셈블리를 처음 사용하고 x86 멀티 코어 시스템에서 경쟁 조건이없는 스핀 락 (AT & T 구문 사용)을 구현할 수 있는지 궁금해졌습니다.cmpxchg를 사용하는 x86 스핀 락

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

spin_unlock: 
lock mov 0 [lock_addr] 
ret 

답변

21

당신이 올바른 생각을 가지고 있지만 ASM이 분류됩니다

cmpxchg은 즉시 피연산자 작동하지 않을 수 있습니다, 등록 만.

lockmov의 유효한 접두사가 아닙니다. 정렬 된 주소에 mov은 x86에서 원자입니다. 따라서 lock이 필요하지 않습니다. 내가 & T 구문에서 사용 했으므로

그것은 약간의 시간이되었습니다 나는 모든 것을 기억 희망 : GCC는 원자 내장 명령을 가지고

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 

주를, 그래서 당신은 실제로에 인라인 어셈블리를 사용할 필요가 없습니다 이 작업을 수행 : 보 아래 말했듯이

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'은 이미 '휘발성'으로 처리합니다. –

+0

'spin_unlock' 안의 메모리 장벽으로 사용되는 asm은 아마 메모리 clobber를 포함해야합니다. 반면에, "__sync_lock_release"는 "쓰기 장벽"을 수행하고 "0을 쓰는 것"을 설계하기 위해 설계된 것이고, asm에 관해 전혀 생각할 필요가 없으며, "약간의 이식성"이 있습니다. 명시 적으로 읽기 장벽으로 작동하지는 않습니다 (이는 대상 아키텍처에서 _incidentially _합니다).하지만 괜찮습니다. 최악의 경우는 희귀 한, 드물기는 하나의 추가 스핀을 수행하는 또 다른 스레드입니다. – Damon

3

이 메모리 버스에 덜 경쟁을 넣어 것입니다 :

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

동의하지만이 코드는 좋지 않습니다. 간단한 while (* p)는 컴파일러가 쉽게 최적화 할 수 있습니다. 몇 가지 장벽을 추가하십시오. 또한 인텔 칩에 _mm_pause()를 추가하면 성능을 크게 향상시킬 수 있습니다. –

관련 문제