Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Imagine there's 1234 threads and 16 CPUs. One thread acquires the spinlock, then the OS does a task switch. Now you've 16 CPUs, each running one of the remaining 1233 threads, all spinning in a remarkably pointless way for however long it takes for the OS to give CPU time back to the only thread that can release the spinlock. This means the entire OS can basically lock up (with all CPUs going flat out) for a few seconds. This is seriously retarded; so how do you fix it?</p> <p>You fix it by not using spinlocks in user-space. Spinlocks should only ever be used if/when task switches can be disabled; and only the kernel should be able to disable task switches.</p> <p>More specifically, you need to use a mutex. Now the mutex may spin initially before giving up and making the thread wait for the lock, and (for typical/low contention cases) this does help, but it'd still be a mutex and is not a spinlock.</p> <p>Next; for sane software, what matters (for performance) is avoiding lock contention, and then making sure that the uncontended case is fast (and a good mutex won't cause a task switch if there's no contention). You are measuring the contended/irrelevant case.</p> <p>Finally; your lock is bad. To avoid excessive use of the <code>lock</code> prefix you should test if you might be able to acquire without any <code>lock</code> prefix, and only when you might be able to acquire should you use the <code>lock</code> prefix. Intel (and probably lots of other people) call this strategy "test; then (test and set)". In addition you've failed to understand the purpose of <code>pause</code> (or "rep nop" for assemblers that are so bad that they don't support 10 year old instructions).</p> <p>A half decent spinlock might look something like:</p> <pre><code>acquire: lock bts dword [myLock],0 ;Optimistically attempt to acquire jnc .acquired ;It was acquired! .retry: pause cmp dword [myLock],0 ;Should we attempt to acquire again? jne .retry ; no, don't use `lock` lock bts dword [myLock],0 ;Attempt to acquire jc .retry ;It wasn't acquired, so go back to waiting .acquired: ret release: mov dword [myLock],0 ;No lock prefix needed here as "myLock" is aligned ret </code></pre> <p>Also note that if you've failed to adequately minimise the chance of lock contention, then you do need to care about "fairness" and should not be using a spinlock. The problem with "unfair" spinlocks is that some tasks might be lucky and always get the lock, and some tasks may be unlucky and never get the lock because the lucky tasks have always got it. This has always been a problem for heavily contended locks, but for modern NUMA systems it's become a much more likely problem. In this case, at a minimum you should be using a ticket lock.</p> <p>The basic idea of a ticket lock is to ensure that tasks acquire the lock in the order they arrive (and not some "possibly extremely bad" random order). For completeness, a ticket lock might look like this:</p> <pre><code>acquire: mov eax,1 lock xadd [myLock],eax ;myTicket = currentTicket, currentTicket++ cmp [myLock+4],eax ;Is it my turn? je .acquired ; yes .retry: pause cmp [myLock+4],eax ;Is it my turn? jne .retry ; no, wait .acquired: ret release: lock inc dword [myLock+4] ret </code></pre> <p><strong>tl;dr;</strong> You shouldn't be using the wrong tool for the job (spinlocks) to begin with; but if you insist on using the wrong tool then at least get the wrong tool implemented properly... :-)</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload