Ayer publiqué this question sobre cómo escribir un spinlock rápido. Gracias a Cory Nelson parece haber encontrado un método que supera a los otros métodos discutidos en mi pregunta. Utilizo la instrucción CMPXCHG
para verificar si el bloqueo es 0 y, por lo tanto, es gratis. CMPXCHG
funciona en'BYTE', WORD
y DWORD
. Supongo que la instrucción funcionaría más rápido en BYTE
. Pero escribí un bloqueo de la implementación de cada uno de los tipos de datos:cmpxchg para WORD más rápido que para 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
}
}
El bloqueo a continuación, se puso a prueba utilizando el siguiente pseudo-código (tenga en cuenta que la LCM-puntero siempre señalará a una divisible dirección por 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);
}
He obtenido los siguientes resultados medidos en mseg en un procesador con 2 núcleos físicos capaces de ejecutar 4 hilos (Ivy Bridge).
1 thread 2 threads 4 threads
8-bit 200 700 3200
16-bit 200 500 1400
32-bit 200 900 3400
Los datos sugieren que todas las funciones tardan la misma cantidad de tiempo en ejecutarse. Pero cuando múltiples hilos tienen que verificar si lck == 0
usando un 16-bit puede ser significativamente más rápido. ¿Porqué es eso? Supongo que no tiene nada que ver con la alineación del lck
?
Gracias de antemano.
'Sé que no hay mucha diferencia, pero como un spinlock es un objeto muy usado' - haven 'utilizó explícitamente uno solo en más de 30 años de desarrollo de software multiproceso. –
Intenta mover la instrucción 'pausa 'DENTRO del bucle giratorio en lugar de fuera del bucle. Las instrucciones de 16 bits requieren bytes de prefijo 0x66/0x67 adicionales haciéndolos un poco más grandes/más lentos que las instrucciones de 8 o 32 bits. Por lo tanto, es posible que la sobrecarga adicional disminuya la velocidad del ciclo lo suficiente como para reducir la contención en el caso de 16 bits. –
No me sorprendería si estos bloqueos conducen a la corrupción aleatoria, ya que modifican ebx (un registro de guardado de llamadas) sin guardar y restaurarlo, lo que podría corromper algún valor que una persona que llama espera que se preserve. Use edx en su lugar. –