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.
then in application programming we can use only spinlocks.why i should use mutex.can u give one example advantage of nutex over spinlocks
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
[…] 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 […]
[…] 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 […]
[…] 제안에 따라이 기사 에 따라 Alexander Sandler의 블로그에있는 pthread mutex vs pthread spinlock Linux의 Alex는 #ifdef를 사용하여 동작을 테스트하기 위해 spinlock& mutexes를 […]
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.