znalazłem następujący kod Spinlock w boost::smart_ptr
:różnica między pthread_spinlock i boost :: smart_ptr :: spinlock?
bool try_lock()
{
return (__sync_lock_test_and_set(&v_, 1) == 0);
}
void lock()
{
for (unsigned k=0; !try_lock(); ++k)
{
if (k<4)
; // spin
else if (k < 16)
__asm__ __volatile__("pause"); // was ("rep; nop" ::: "memory")
else if (k < 32 || k & 1)
sched_yield();
else
{
struct timespec rqtp;
rqtp.tv_sec = 0;
rqtp.tv_nsec = 100;
nanosleep(&rqtp, 0);
}
}
}
void unlock()
{
__sync_lock_release(&v_);
}
Więc jeśli dobrze rozumiem to poprawnie, gdy zamek jest utrzymywał wątek przychodząca będzie wykładniczo back-off, pierwszy przędzenia dziko, a następnie zatrzymując się, a następnie uzyskując pozostałą z jego fragmentu czasu, a na koniec flip-flopping pomiędzy snem a plonowaniem.
Znalazłem również implementację glibc pthread_spinlock
, która używa zespołu do wykonania blokady.
#define LOCK_PREFIX "lock;" // using an SMP machine
int pthread_spin_lock(pthread_spinlock_t *lock)
{
__asm__ ("\n"
"1:\t" LOCK_PREFIX "decl %0\n\t"
"jne 2f\n\t"
".subsection 1\n\t"
".align 16\n"
"2:\trep; nop\n\t"
"cmpl $0, %0\n\t"
"jg 1b\n\t"
"jmp 2b\n\t"
".previous"
: "=m" (*lock)
: "m" (*lock));
return 0;
}
Przyznam, że moje rozumienie zgromadzeń nie jest wspaniałe, więc nie w pełni rozumiem, co się tutaj dzieje. (Może ktoś proszę wyjaśnić co to robi?)
Jednak wpadłem kilka testów przeciwko doładowania Spinlock i glibc pthread_spinlock, a gdy istnieją więcej rdzeni niż nici, kod doładowania przewyższa kodu glibc.
Z drugiej strony, gdy istnieje więcej wątków niż rdzeni, kod glibc jest lepszy.
Dlaczego tak jest? Jaka jest różnica między tymi dwoma implementacjami spinlock, które powodują, że w każdym scenariuszu działają inaczej?
Zabawne, zrobiłem podobny test kilka lat temu i doszedłem do tego samego wniosku: 'pthread_spin_lock' jest bardziej efektywny niż manual spinlocks (linia z boost), gdy jest dużo rywalizacji. –