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

53 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

Leave a Reply

Prove you are not a computer or die *