Wczoraj opublikowałem this question o tym, jak napisać szybki spinlock. Dzięki Cory Nelson wydaje mi się, że znalazłem metodę, która przewyższa inne metody omówione w moim pytaniu. Korzystam z instrukcji CMPXCHG
, aby sprawdzić, czy blokada wynosi 0, a co za tym idzie, jest wolna. CMPXCHG
działa na "OTE", WORD
i DWORD
. Zakładam, że instrukcja działałaby szybciej na BYTE
. Ale napisałem blokadę wykonawczego każdego z typów danych:cmpxchg dla WORD szybciej niż dla BYTE
inline void spin_lock_8(char* lck)
{
__asm
{
mov ebx, lck ;move lck pointer into ebx
xor cl, cl ;set CL to 0
inc cl ;increment CL to 1
pause ;
spin_loop:
xor al, al ;set AL to 0
lock cmpxchg byte ptr [ebx], cl ;compare AL to CL. If equal ZF is set and CL is loaded into address pointed to by ebx
jnz spin_loop ;jump to spin_loop if ZF
}
}
inline void spin_lock_16(short* lck)
{
__asm
{
mov ebx, lck
xor cx, cx
inc cx
pause
spin_loop:
xor ax, ax
lock cmpxchg word ptr [ebx], cx
jnz spin_loop
}
}
inline void spin_lock_32(int* lck)
{
__asm
{
mov ebx, lck
xor ecx, ecx
inc ecx
pause
spin_loop:
xor eax, eax
lock cmpxchg dword ptr [ebx], ecx
jnz spin_loop
}
}
inline spin_unlock(<anyType>* lck)
{
__asm
{
mov ebx, lck
mov <byte/word/dword> ptr [ebx], 0
}
}
Zamek został następnie przetestowany za pomocą następującego pseudo-kodu (należy pamiętać, że LCM-wskaźnik zawsze będzie wskazywać na podzielna adresowej przez 4):
<int/short/char>* lck;
threadFunc()
{
loop 10,000,000 times
{
spin_lock_8/16/32 (lck);
spin_unlock(lck);
}
}
main()
{
lck = (char/short/int*)_aligned_malloc(4, 4);//Ensures memory alignment
start 1 thread running threadFunc and measure time;
start 2 threads running threadFunc and measure time;
start 4 threads running threadFunc and measure time;
_aligned_free(lck);
}
Otrzymałem następujące wyniki zmierzone w msecs na procesorze z 2 rdzeniami fizycznymi zdolnymi do uruchomienia 4 wątków (Ivy Bridge).
1 thread 2 threads 4 threads
8-bit 200 700 3200
16-bit 200 500 1400
32-bit 200 900 3400
Dane sugerują, że wszystkie funkcje ma taką samą ilość czasu na wykonanie. Ale gdy wiele wątków musi sprawdzić, czy lck == 0
przy użyciu 16-bitów może być znacznie szybsze. Dlaczego? Nie sądzę, że ma to coś wspólnego z wyrównaniem lck
?
Z góry dziękuję.
"Wiem, że to nie jest wielka różnica, ale jako spinlock to mocno wykorzystywany obiekt" - oaza Nie używał jednoznacznie w ciągu ponad 30 lat rozwoju oprogramowania wielowątkowego. –
Spróbuj przesunąć instrukcję 'pause' PRZED pętlą wirowania, a nie poza pętlą. Wersje 16-bitowe wymagają dodatkowych bajtów prefiksu 0x66/0x67, co czyni je nieco większymi/wolniejszymi niż instrukcje 8- lub 32-bitowe. Więc może to być dodatkowe obciążenie spowalniające pętlę na tyle, aby zmniejszyć rywalizację w przypadku 16-bitowym. –
Nie zdziwiłbym się, gdyby te blokady prowadziły do przypadkowego uszkodzenia, ponieważ modyfikują ebx (rejestr oszczędzania) bez zapisywania i przywracania, co może uszkodzić pewną wartość, którą rozmówca spodziewa się zachować. Zamiast tego użyj edx. –