最近研究boost的无锁队列,测试了一下性能,发现无锁队列比有锁的还要慢
testqueue.cpp
#include <time.h>
#include <boost/thread/thread.hpp>
#include <boost/lockfree/queue.hpp>
#include <iostream>
#include <boost/atomic.hpp>
boost::atomic_int producer_count(0);
boost::atomic_int consumer_count(0);
boost::lockfree::queue<int> queue(128);//无锁的多生产者多消费者队列
const int iterations = 100000;
const int producer_thread_count = 1;
const int consumer_thread_count = 1;
void producer(void)
{
for (int i = 0; i != iterations; ++i) {
int value = ++producer_count;
//std::cout<<"product value:"<<value<<std::endl;
while (!queue.push(value))
;
}
}
boost::atomic<bool> done (false);
void consumer(void)
{
int value;
while (!done) {
while (queue.pop(value))
{
// std::cout<<"customer value:"<<value<<std::endl;
++consumer_count;
}
}
while (queue.pop(value))
{
// std::cout<<"customer value:"<<value<<std::endl;
++consumer_count;
}
}
int main(int argc, char* argv[])
{
using namespace std;
cout << "boost::lockfree::queue is ";
if (!queue.is_lock_free())
cout << "not ";
cout << "lockfree" << endl;
boost::thread_group producer_threads, consumer_threads;//线程组
for (int i = 0; i != producer_thread_count; ++i)
producer_threads.create_thread(producer);
for (int i = 0; i != consumer_thread_count; ++i)
consumer_threads.create_thread(consumer);
producer_threads.join_all();
done = true;
consumer_threads.join_all();
cout << "produced " << producer_count << " objects." << endl;
cout << "consumed " << consumer_count << " objects." << endl;
//getchar();
}
#include <time.h>
#include <boost/thread/thread.hpp>
#include <boost/lockfree/queue.hpp>
#include <iostream>
#include <queue>
#include <boost/atomic.hpp>
boost::atomic_int producer_count(0);
boost::atomic_int consumer_count(0);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*初始化互斥锁*/
std::queue<int> queue2;//无锁的多生产者多消费者队列
const int iterations = 100000;
const int producer_thread_count = 1;
const int consumer_thread_count = 1;
void producer(void)
{
for (int i = 0; i != iterations; ++i) {
int value = ++producer_count;
//std::cout<<"product value:"<<value<<std::endl;
pthread_mutex_lock(&mutex); //互斥锁
queue2.push(value);
pthread_mutex_unlock(&mutex); //互斥锁
}
}
boost::atomic<bool> done (false);
void consumer(void)
{
int value;
while (!done) {
pthread_mutex_lock(&mutex); //互斥锁
int value = 0;
if(!queue2.empty()) {
value = queue2.front();
queue2.pop();
}
pthread_mutex_unlock(&mutex); //互斥锁
{
// std::cout<<"customer value:"<<value<<std::endl;
++consumer_count;
}
}
pthread_mutex_lock(&mutex); //互斥锁
while (!queue2.empty()) {
int value = 0;
value = queue2.front();
queue2.pop();
{
// std::cout<<"customer value:"<<value<<std::endl;
++consumer_count;
}
}
pthread_mutex_unlock(&mutex); //互斥锁
}
int main(int argc, char* argv[])
{
using namespace std;
cout << "boost::lockfree::queue is ";
//if (!queue.is_lock_free())
// cout << "not ";
cout << "lockfree" << endl;
pthread_mutex_init(&mutex, NULL);
boost::thread_group producer_threads, consumer_threads;//线程组
for (int i = 0; i != producer_thread_count; ++i)
producer_threads.create_thread(producer);
for (int i = 0; i != consumer_thread_count; ++i)
consumer_threads.create_thread(consumer);
producer_threads.join_all();
done = true;
consumer_threads.join_all();
pthread_mutex_destroy(&mutex);
cout << "produced " << producer_count << " objects." << endl;
cout << "consumed " << consumer_count << " objects." << endl;
//getchar();
}
time ./testqueue
boost::lockfree::queue is lockfree
produced 100000 objects.
consumed 100000 objects.
real 0m0.054s
user 0m0.103s
sys 0m0.002s
time ./testqueue2
boost::lockfree::queue is lockfree
produced 100000 objects.
consumed 100000 objects.
real 0m0.024s
user 0m0.032s
sys 0m0.008s
无锁比有锁慢了一倍,如果将线程数都调整为4,2个速度都差不多
看另外地方写的原因:
https://blog.csdn.net/cws1214/article/details/47680773?utm_source=blogxgwz8
无锁队列与有锁队列的性能测试
这里测试的无锁列队由 MidiShare 实现的,而有锁队列是通过 pthread_mutex 与 c++ 的 STL list 共同实现。这里只列出测试结果。
对于存储相同的数据的情况下,从主线程 enque 并从子线程 deque ,计算每秒钟 enque/deque 的次数,当然二者基本上是相同的。
无锁队列的性能在 150w -200w 次入队操作,这个性能已经无法再有任何提高,因为每次入队出队操作都是硬件级的互斥。而对于有锁队列,根据每次加解锁之间处理入队的次数的不同,有以下的结果:
lock();for(k=0;k<x;i++,k++);unlock() | 结果(次/s) |
x=1 | 40 万 |
x=10 | 190 万 |
x=128 | 350 万 |
x=1000 | 400 万 |
x=10000 | 396 万 |
这说明通过对锁之间的数据进行批处理,可以极大的提高系统的性能,而使用原子操作,则无法实现批处理上的改进。
4 结论
通过上面的无锁和有锁的性能测试,可以得出这样的结论,对于 CAS 实现的硬件级的互斥,其单次操作性能比相同条件下的应用层的较为高效,但当多个线程并发时,硬件级的互斥引入的代价与应用层的锁争用同样令人惋惜。因此如果纯粹希望通过使用 CAS 无锁算法及相关数据结构而带来程序性能的大量提升是不可能的,硬件级原子操作使应用层操作变慢,而且无法再度优化。相反通过对有锁多线程程序的良好设计,可以使程序性能没有任何下降,可以实现高度的并发性。
但是我们也要看到应用层无锁的好处,比如不需要程序员再去考虑死锁、优先级反转等棘手的问题,因此在对应用程序不太复杂,而对性能要求稍高时,可以采用有锁多线程。而程序较为复杂,性能要求满足使用的情况下,可以使用应用级无锁算法。
至于如何对多线程的工作模式进行更好的调度,可以参考文献 [5] ,文献介绍了一种较好的线程间合作的工作模式,当然前提是机器的处理器个数较多,足以支持多组线程并行的工作。如果处理器个数较,较多的线程之间在各个核心上来回调度增加了系统上下文切换的开销,会导致系统整体性能下降。
可能无锁使用的是硬件层的原子锁,所有线程都会不可避免竞争这个原子锁,性能可能有限,而应用层的锁会有所优化