Spinlock versus Semaphore

77,532

Solution 1

Spinlock and semaphore differ mainly in four things:

1. What they are
A spinlock is one possible implementation of a lock, namely one that is implemented by busy waiting ("spinning"). A semaphore is a generalization of a lock (or, the other way around, a lock is a special case of a semaphore). Usually, but not necessarily, spinlocks are only valid within one process whereas semaphores can be used to synchronize between different processes, too.

A lock works for mutual exclusion, that is one thread at a time can acquire the lock and proceed with a "critical section" of code. Usually, this means code that modifies some data shared by several threads.
A semaphore has a counter and will allow itself being acquired by one or several threads, depending on what value you post to it, and (in some implementations) depending on what its maximum allowable value is.

Insofar, one can consider a lock a special case of a semaphore with a maximum value of 1.

2. What they do
As stated above, a spinlock is a lock, and therefore a mutual exclusion (strictly 1 to 1) mechanism. It works by repeatedly querying and/or modifying a memory location, usually in an atomic manner. This means that acquiring a spinlock is a "busy" operation that possibly burns CPU cycles for a long time (maybe forever!) while it effectively achieves "nothing".
The main incentive for such an approach is the fact that a context switch has an overhead equivalent to spinning a few hundred (or maybe thousand) times, so if a lock can be acquired by burning a few cycles spinning, this may overall very well be more efficient. Also, for realtime applications it may not be acceptable to block and wait for the scheduler to come back to them at some far away time in the future.

A semaphore, by contrast, either does not spin at all, or only spins for a very short time (as an optimization to avoid the syscall overhead). If a semaphore cannot be acquired, it blocks, giving up CPU time to a different thread that is ready to run. This may of course mean that a few milliseconds pass before your thread is scheduled again, but if this is no problem (usually it isn't) then it can be a very efficient, CPU-conservative approach.

3. How they behave in presence of congestion
It is a common misconception that spinlocks or lock-free algorithms are "generally faster", or that they are only useful for "very short tasks" (ideally, no synchronization object should be held for longer than absolutely necessary, ever).
The one important difference is how the different approaches behave in presence of congestion.

A well-designed system normally has low or no congestion (this means not all threads try to acquire the lock at the exact same time). For example, one would normally not write code that acquires a lock, then loads half a megabyte of zip-compressed data from the network, decodes and parses the data, and finally modifies a shared reference (append data to a container, etc.) before releasing the lock. Instead, one would acquire the lock only for the purpose of accessing the shared resource.
Since this means that there is considerably more work outside the critical section than inside it, naturally the likelihood for a thread being inside the critical section is relatively low, and thus few threads are contending for the lock at the same time. Of course every now and then two threads will try to acquire the lock at the same time (if this couldn't happen you wouldn't need a lock!), but this is rather the exception than the rule in a "healthy" system.

In such a case, a spinlock greatly outperforms a semaphore because if there is no lock congestion, the overhead of acquiring the spinlock is a mere dozen cycles as compared to hundreds/thousands of cycles for a context switch or 10-20 million cycles for losing the remainder of a time slice.

On the other hand, given high congestion, or if the lock is being held for lengthy periods (sometimes you just can't help it!), a spinlock will burn insane amounts of CPU cycles for achieving nothing.
A semaphore (or mutex) is a much better choice in this case, as it allows a different thread to run useful tasks during that time. Or, if no other thread has something useful to do, it allows the operating system to throttle down the CPU and reduce heat / conserve energy.

Also, on a single-core system, a spinlock will be quite inefficient in presence of lock congestion, as a spinning thread will waste its complete time waiting for a state change that cannot possibly happen (not until the releasing thread is scheduled, which isn't happening while the waiting thread is running!). Therefore, given any amount of contention, acquiring the lock takes around 1 1/2 time slices in the best case (assuming the releasing thread is the next one being scheduled), which is not very good behaviour.

4. How they're implemented
A semaphore will nowadays typically wrap sys_futex under Linux (optionally with a spinlock that exits after a few attempts).
A spinlock is typically implemented using atomic operations, and without using anything provided by the operating system. In the past, this meant using either compiler intrinsics or non-portable assembler instructions. Meanwhile both C++11 and C11 have atomic operations as part of the language, so apart from the general difficulty of writing provably correct lock-free code, it is now possible to implement lock-free code in an entirely portable and (almost) painless way.

Solution 2

very simply, a semaphore is a "yielding" synchronisation object, a spinlock is a 'busywait' one. (there's a little more to semaphores in that they synchronise several threads, unlike a mutex or guard or monitor or critical section that protects a code region from a single thread)

You'd use a semaphore in more circumstances, but use a spinlock where you are going to lock for a very short time - there is a cost to locking especially if you lock a lot. In such cases it can be more efficient to spinlock for a little while waiting for the protected resource to become unlocked. Obviously there is a performance hit if you spin for too long.

typically if you spin for longer than a thread quantum, then you should use a semaphore.

Solution 3

Over and above what Yoav Aviram and gbjbaanb said, the other key point used to be that you would never use a spin-lock on a single-CPU machine, whereas a semaphore would make sense on such a machine. Nowadays, you are frequently hard-pressed to find a machine without multiple cores, or hyperthreading, or equivalent, but in the circumstances that you have just a single CPU, you should use semaphores. (I trust the reason is obvious. If the single CPU is busy waiting for something else to release the spin-lock, but it is running on the only CPU, the lock is unlikely to be released until the current process or thread is preempted by the O/S, which might take a while and nothing useful happens until the preemption occurs.)

Solution 4

From Linux Device Drivers by Rubinni

Unlike semaphores, spinlocks may be used in code that cannot sleep, such as interrupt handlers

Solution 5

I am not a kernel expert but here are few points:

Even uniprocessor machine can use spin-locks if kernel preemption is enabled while compiling the kernel. If kernel preemption is disabled then spin-lock (perhaps) expands to a void statement.

Also, when we are trying to compare Semaphore vs Spin-lock, I believe semaphore refers to the one used in kernel - NOT the one used for IPC (userland).

Basically, spin-lock shall be used if critical section is small (smaller than the overhead of sleep/wake-up) and critical section does not call anything that can sleep! A semaphore shall be used if critical section is bigger and it can sleep.

Raman Chalotra.

Share:
77,532
iankits
Author by

iankits

Digi-prenuer! :-)

Updated on July 08, 2022

Comments

  • iankits
    iankits almost 2 years

    What are the basic differences between a semaphore & spin-lock?

    When would we use a semaphore over a spin-lock?

  • Richard T
    Richard T over 15 years
    All multi-threading CPUs need a spinlock instruction ("test and set") and it's always implemented as a single instruction in hardware because there would otherwise always be a race condition in which more than one thread thought it "owned" the protected resource.
  • gbjbaanb
    gbjbaanb over 15 years
    I'm not sure you understand semaphores... see what Dijkstra said: cs.cf.ac.uk/Dave/C/node26.html
  • Nils Pipenbrinck
    Nils Pipenbrinck over 15 years
    I'd like to second how important it is not to use spinlocks on single threaded systems. They are a the ticked to priority inversion problems. And trust me: You don't want to debug these kind of bugs.
  • Greg Rogers
    Greg Rogers over 15 years
    POSIX makes a distinction between a semaphore shared by threads, and a semaphore shared by processes.
  • Prof. Falken
    Prof. Falken almost 14 years
    spinlocks are all over in the Linux kernel, regardless if you have one ore more CPUs. What do you mean exactly?
  • Jonathan Leffler
    Jonathan Leffler almost 14 years
    @Amigable: by definition, a spinlock means that the current thread on the CPU is waiting for something else to release the locked object. If the only active thing that can change the lock is the current CPU, the lock will not be freed by spinning. If something else - a DMA transfer or other I/O controller can release the lock, all well and good. But spinning when nothing else can release the lock is not very sensible - you might as well yield the CPU to another process now as wait to be preempted.
  • Prof. Falken
    Prof. Falken almost 14 years
    I may very well be wrong, but I was under the impression that a re-entrant (single CPU) Linux kernel may interrupt a running spin lock.
  • Jonathan Leffler
    Jonathan Leffler almost 14 years
    @Amigable: there's a chance I'm wrong too, but I think I'm close to the classic definition of a spinlock. With pre-emptive scheduling, a process might spin on a lock until the end of its time slice, or until an interrupt causes it to yield, but if another process must provide the condition that allows the spinlock to lock, a spinlock is not a good idea on a single CPU machine. The system I work on has spinlocks and has a configurable upper bound on the number of spins before it goes into a non-busy wait mode. This is a user-level spin-lock; there might be a difference down in the kernel.
  • Johan Bezem
    Johan Bezem over 12 years
    Semaphores are for inter-process synchronization, not communication.
  • Hibou57
    Hibou57 over 10 years
    Seems to be a copy/paste of this ;-) : what is the difference between spin locks and semaphores?
  • Hibou57
    Hibou57 over 10 years
    “Also, on a single-core system, a spinlock will be quite inefficient in presence of lock congestion, as a spinning thread will waste its complete time waiting for a state change that cannot possibly happen” : there is also (at least on Linux) the spin_trylock, which returns immediately with an error code, if the lock could not be acquired. A spin‑lock is not always that harsh. But using spin_trylock requires, for an application, to be properly designed that way (probably a queue of pending operations, and here, selecting the next one, leaving the actual on the queue).
  • MikeMB
    MikeMB about 9 years
    Sorry, I downvoted your answer - while spinlocks might often not be a good idea performance wise, almost any modern system I know of (be it single or multi-CPU, server or embedded) uses some kind of non-cooperative multitasking, where the blocking task doesn't have to yield manually. So a general statement that spinlocks cannot be used on single-CPUs is just wrong.
  • Jonathan Leffler
    Jonathan Leffler about 9 years
    @MikeMB: Where do I say "spinlock cannot be used on single CPUs"? I say you "you would never use a spin-lock on a single-CPU machine" (note that "would" is very different from "could"!) and describe what happens if you do ("if the single CPU is busy waiting for something else to release the spin-lock, but it is running on the only CPU, the lock is unlikely to be released"). I should perhaps add "until the current process or thread is pre-empted" -- but the main point remains: if you do use a spinlock on a single CPU machine, your process can spin on the lock for a long time.
  • MikeMB
    MikeMB about 9 years
    You write "but in the circumstances that you have just a single CPU, you MUST use semaphores." If you remove the must and add the note you suggested, I'd happily remove my downvote. Also I'm not sure whether that still quallifies as a spinlock but I've seen implementations that call yield befor they try to aquire the lock again. I completely agree that it is usually not a good idea though.
  • Jonathan Leffler
    Jonathan Leffler about 9 years
    @MikeMB: I've revised the answer to cover the points we discussed, which are also largely covered in the commentary from years past.
  • Jorge Bellon
    Jorge Bellon over 7 years
    Blocking mutexes and semaphores are not only useful in single-thread environments but also if there is oversubscription, that is, the number of threads a program (or multiple programs sharing the system) creates is higher than the number of hardware resources. In these cases, blocking your thread allows the others to be able to use CPU time in a useful manner. In addition, if hardware supports hyperthreading, the other thread could make use of the execution units that are being used to perform the idle loop.