pthread mutex vs pthread spinlock

Update 06/16/2009: On several occasions, commentators to this article pointed out that this post is somewhat incomplete. Therefore, before I continue, I would like to make some things clear.

Before considering the code below, please note that spinlocks are totally useless on uni-processor computers. This is due to a nature of spinlocks, which I will describe in future posts.

Also, it is not the intent of this article to show that spinlocks better than mutexes in all circumstances. Spinlocks perform better than mutexes in this particular setup, on multi-processor computer. It does not mean that spinlocks are good for you. Apply your own judgment when considering using materials presented in this article for your needs.

In this article I would like to demonstrate you how spinlocks are more efficient than mutexes. I’ll demonstrate a program that does the following.

It builds a list of integers and then starts two threads, each removing entries from the list. Since both threads work with the same list at the same time, list has to be protected with synchronization mechanism of some kind.

Depending on whether the program has been compiled with USE_SPINLOCK or not, it will use a spinlock or a mutex to protect the list. In any case, it will display number of seconds that has passed from the moment threads has been started, until they finish.

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#include <sys/time.h>

#include <list>

#define LOOPS 10000000

using namespace std;

list<int> the_list;

#ifdef USE_SPINLOCK
pthread_spinlock_t spinlock;
#else
pthread_mutex_t mutex;
#endif

pid_t gettid() { return syscall( __NR_gettid ); }

void *consumer(void *ptr)
{
    int i;

    printf("Consumer TID %lu\n", (unsigned long)gettid());

    while (1)
    {
#ifdef USE_SPINLOCK
        pthread_spin_lock(&spinlock);
#else
        pthread_mutex_lock(&mutex);
#endif

        if (the_list.empty())
        {
#ifdef USE_SPINLOCK
            pthread_spin_unlock(&spinlock);
#else
            pthread_mutex_unlock(&mutex);
#endif
            break;
        }

        i = the_list.front();
        the_list.pop_front();

#ifdef USE_SPINLOCK
        pthread_spin_unlock(&spinlock);
#else
        pthread_mutex_unlock(&mutex);
#endif
    }

    return NULL;
}

int main()
{
    int i;
    pthread_t thr1, thr2;
    struct timeval tv1, tv2;

#ifdef USE_SPINLOCK
    pthread_spin_init(&spinlock, 0);
#else
    pthread_mutex_init(&mutex, NULL);
#endif

    // Creating the list content...
    for (i = 0; i < LOOPS; i++)
        the_list.push_back(i);

    // Measuring time before starting the threads...
    gettimeofday(&tv1, NULL);

    pthread_create(&thr1, NULL, consumer, NULL);
    pthread_create(&thr2, NULL, consumer, NULL);

    pthread_join(thr1, NULL);
    pthread_join(thr2, NULL);

    // Measuring time after threads finished...
    gettimeofday(&tv2, NULL);

    if (tv1.tv_usec > tv2.tv_usec)
    {
        tv2.tv_sec--;
        tv2.tv_usec += 1000000;
    }

    printf("Result - %ld.%ld\n", tv2.tv_sec - tv1.tv_sec,
        tv2.tv_usec - tv1.tv_usec);

#ifdef USE_SPINLOCK
    pthread_spin_destroy(&spinlock);
#else
    pthread_mutex_destroy(&mutex);
#endif

    return 0;
}

Lets see how this little program works in greater detail. main() starts with initialization of a mutex or a spinlock, in lines 67-71. Next it initializes the list. Number of entries it will push into the list depends on LOOPS definition from line 10. Finally, in lines 77-81 it measures current time and spawns two threads. Then it waits for both threads to finish their job.

The thread, between lines 24 and 56, runs in a loop and pops entries from the list. Once the list is empty, thread exits. Each iteration of the loop it locks the mutex/spinlock and then unlocks it (lines 32-36 and 51-55).

Once both threads are over, main() will measure the time again and print difference between two measurements. This difference is how we measure effectiveness. The less time it took to run, the better.

Lets run the code and see how it performs, first with mutex and then with spinlock.

~/lab/mutex-vs-spinlock --> g++ -Wall -pthread main.cc
~/lab/mutex-vs-spinlock --> ./a.out
Consumer TID 19602
Consumer TID 19603
Result - 7.99794
~/lab/mutex-vs-spinlock --> g++ -DUSE_SPINLOCK -Wall -pthread main.cc
~/lab/mutex-vs-spinlock --> ./a.out
Consumer TID 19610
Consumer TID 19611
Result - 2.587424
~/lab/mutex-vs-spinlock -->

As we can see, the picture is clear. We can see that using spinlock, it took 2.5 seconds to complete the test. However when used mutex, it took almost 8 seconds to complete. This is 3 times more time.

This proves that the spinlock is more effective in term of CPU consumption and speed.

Did you know that you can receive periodical updates with the latest articles that I write right into your email box? Alternatively, you subscribe to the RSS feed!

Want to know how? Check out
Subscribe page

58 Comments

  1. shiva says:

    then in application programming we can use only spinlocks.why i should use mutex.can u give one example advantage of nutex over spinlocks

  2. crowsnet says:

    it is sad to use one particular example as generic proof.

    Only if the time sending a thread to sleep and waking it again (mutex) exceeds time spent busy waiting (spinning) pthread spinlock is better than pthread mutex.

    In other words:
    -If the work done in a guarded code block takes a long time, spinlocks are way less effective than mutexes.
    -For very short guarded work, like in your example, spinlocks are more effective.

    Most modern mutexes implement a series of locking mechanisms:
    1. optimistically try to own the lock
    2. if 1. fails, spin for a small time and try to own the lock every spin
    3. if 2. fails, send thread to sleep till lock available

  3. […] find lots of benchmarks that all come to the conclusion that spinlocks are better. (like this, this or this) What these benchmarks have in common is that they measure code in which there is nothing […]

  4. […] le quali giungono tutti alla epilogo le quali a esse spinlock sono migliori. (appena chequesto,questoquesto) Ciò le quali questi parametri hanno quanto a dozzinale è le quali misurano il regole […]

  5. […] 제안에 따라이 기사 에 따라 Alexander Sandler의 블로그에있는 pthread mutex vs pthread spinlock Linux의 Alex는 #ifdef를 사용하여 동작을 테스트하기 위해 spinlock& mutexes를 […]

  6. Jason says:

    It depends on what your trying to do. For example, if sharing a network socket, if thread A locks, thread B may have to wait in the milliseconds or even seconds. If two threads are compressing a large video, thread B might only have to wait in the nanoseconds for thread A to write it’s progress into a space in global memory that each thread can access. In the case of the compression example this wait time can be comparable to the time that it takes to lock a mutex and perform a context switch, so the spin lock’s speed can make a large difference. In the case of the network resource the few nanoseconds saved with a spin lock won’t make a perceivable difference, yet will leave thread B’s processor spinning at 100% when it could otherwise spend all that time doing other things.

Leave a Reply

Prove you are not a computer or die *