关键词:
Brief
我们使用https://github.com/Qihoo360/evpp项目中的EventLoop::QueueInLoop(...)
函数来做这个性能测试。我们通过该函数能够将一个仿函数执行体从一个线程调度到另一个线程中执行。这是一个典型的生产者和消费者问题。
我们用一个队列来保存这种仿函数执行体。多个生产者线程向这个队列写入仿函数执行体,一个消费者线程从队列中取出仿函数执行体来执行。为了保证队列的线程安全问题,我们可以使用一个锁来保护这个队列,或者使用无锁队列机制来解决安全问题。EventLoop::QueueInLoop(...)
函数通过通定义实现了三种不同模式的跨线程交换数据的队列。
测试对象
- evpp-v0.3.2
EventLoop::QueueInLoop(...)
函数内的队列的三种实现方式:
- 带锁的队列用
std::vector
和std::mutex
来实现,具体的 gcc 版本为 4.8.2 - boost::lockfree::queue from boost-1.53
- moodycamel::ConcurrentQueue with commit c54341183f8674c575913a65ef7c651ecce47243
- 带锁的队列用
测试环境
- Linux CentOS 6.2, 2.6.32-220.7.1.el6.x86_64
- Intel(R) Xeon(R) CPU E5-2630 v2 @ 2.60GHz
- gcc version 4.8.2 20140120 (Red Hat 4.8.2-15) (GCC)
测试方法
测试代码请参考https://github.com/Qihoo360/evpp/blob/master/benchmark/post_task/post_task6.cc. 在一个消费者线程中运行一个EventLoop
对象loop_
,多个生产者线程不停的调用loop_->QueueInLoop(...)
方法将仿函数执行体放入到消费者的队列中让其消费(执行)。每个生产者线程放入一定总数(由运行参数指定)的仿函数执行体之后就停下来,等消费者线程完全消费完所有的仿函数执行体之后,程序退出,并记录开始和结束时间。
为了便于大家阅读,现将相关代码的核心部分摘录如下。
event_loop.h中定义了队列:
std::shared_ptr<PipeEventWatcher> watcher_;
#ifdef H_HAVE_BOOST
boost::lockfree::queue<Functor*>* pending_functors_;
#elif defined(H_HAVE_CAMERON314_CONCURRENTQUEUE)
moodycamel::ConcurrentQueue<Functor>* pending_functors_;
#else
std::mutex mutex_;
std::vector<Functor>* pending_functors_; // @Guarded By mutex_
#endif
event_loop.cc中定义了QueueInLoop(...)
的具体实现:
void Init()
watcher_->Watch(std::bind(&EventLoop::DoPendingFunctors, this));
void EventLoop::QueueInLoop(const Functor& cb)
#ifdef H_HAVE_BOOST
auto f = new Functor(cb);
while (!pending_functors_->push(f))
#elif defined(H_HAVE_CAMERON314_CONCURRENTQUEUE)
while (!pending_functors_->enqueue(cb))
#else
std::lock_guard<std::mutex> lock(mutex_);
pending_functors_->emplace_back(cb);
#endif
watcher_->Notify();
void EventLoop::DoPendingFunctors()
#ifdef H_HAVE_BOOST
Functor* f = nullptr;
while (pending_functors_->pop(f))
(*f)();
delete f;
#elif defined(H_HAVE_CAMERON314_CONCURRENTQUEUE)
Functor f;
while (pending_functors_->try_dequeue(f))
f();
--pending_functor_count_;
#else
std::vector<Functor> functors;
std::lock_guard<std::mutex> lock(mutex_);
notified_.store(false);
pending_functors_->swap(functors);
for (size_t i = 0; i < functors.size(); ++i)
functors[i]();
#endif
我们进行了两种测试:
- 一个生产者线程投递1000000个仿函数执行体到消费者线程中执行,统计总耗时。然后同样的方法我们反复测试10次
- 生产者线程分别是2/4/6/8/12/16/20,每个线程投递1000000个仿函数执行体到消费者线程中执行,并统计总共耗时
测试结论
- 当我们只有生产者和消费者都只有一个时,大多数测试结果表明
moodycamel::ConcurrentQueue
的性能是最好的,大概比queue with std::mutex
高出10%~50%左右的性能。boost::lockfree::queue
比queue with std::mutex
的性能只能高出一点点。由于我们的实现中,必须要求能够使用多生产者的写入,所以并没有测试boost中专门的单生产者单消费者的无锁队列boost::lockfree::spsc_queue
,在这种场景下,boost稍稍有些吃亏,但并不影响整体测试结果及结论。 - 当我们有多个生产者线程和一个消费者线程时,
boost::lockfree::queue
的性能比queue with std::mutex
高出75%~150%左右。moodycamel::ConcurrentQueue
的性能最好,大概比boost::lockfree::queue
高出25%~100%,比queue with std::mutex
高出100%~500%。当生产者线程越多,也就是锁冲突概率越大时,moodycamel::ConcurrentQueue
的性能优势体现得更加明显。
因此,上述对比测试结论,就我们的evpp项目中的EventLoop
的实现方式,我们推荐使用moodycamel::ConcurrentQueue
来实现跨线程的数据交换。
更详细的测试数据,请参考下面的两个图表。
纵轴是执行耗时,越低性能越高。
图1,生产者和消费者都只有一个,横轴是测试的批次:
图2,生产者线程有多个,横轴是生产者线程的个数,分别是2/4/6/8/12/16/20:
其他的性能测试报告
The IO Event performance benchmark against Boost.Asio : evpp is higher than asio about 20%~50% in this case
The ping-pong benchmark against Boost.Asio : evpp is higher than asio about 5%~20% in this case
The throughput benchmark against libevent2 : evpp is higher than libevent about 17%~130% in this case
The performance benchmark of queue with std::mutex
against boost::lockfree::queue
and moodycamel::ConcurrentQueue
: moodycamel::ConcurrentQueue
is the best, the average is higher than boost::lockfree::queue
about 25%~100% and higher than queue with std::mutex
about 100%~500%
The throughput benchmark against Boost.Asio : evpp and asio have the similar performance in this case
The throughput benchmark against Boost.Asio(中文) : evpp and asio have the similar performance in this case
The throughput benchmark against muduo(中文) : evpp and muduo have the similar performance in this case
最后
报告中的图表是使用gochart绘制的。
非常感谢您的阅读。如果您有任何疑问,请随时在https://github.com/Qihoo360/evpp/issues跟我们讨论。谢谢。
evpp性能测试:与muduo进行吞吐量测试(代码片段)
...将https://github.com/Qihoo360/evpp和muduo放到一起做一次全面的性能测试。本文是关于这两个库在吞吐量方面的测试。测试对象evpp-v0.2.4basedonlibevent-2.0.21muduo-v1.0.9测试环境LinuxCentOS6.2,2.6.32-220.7.1.el6.x86_64Intel(R)Xeon(R)CPUE5-2630v2@2.60GHzgccvers... 查看详情
evpp性能测试:与boost.asio进行吞吐量对比测试(代码片段)
...的C++网络库代表。一般来讲,其他的网络库的性能如果不能与asio做一下全面的对比和评测,就不能令人信服。本次测试是参考陈硕的博客文章muduo与boostasio吞吐量对比,该文章的结论是:muduo吞吐量平均比asi... 查看详情
evpp性能测试:与muduo进行吞吐量测试(代码片段)
Byzieckey简介muduo是最近几年中国开源界里产生的优秀作品。它是由业内大牛陈硕实现的。详细介绍,请参考其博客介绍http://blog.csdn.net/solstice/article/details/5848547。本次测试是参考陈硕的博客文章muduo与libevent2吞吐量对比,... 查看详情
evpp性能测试:与boost.asio进行吞吐量对比测试(代码片段)
...的C++网络库代表。一般来讲,其他的网络库的性能如果不能与asio做一下全面的对比和评测,就不能令人信服。本次测试是参考陈硕的博客文章muduo与boostasio吞吐量对比,该文 查看详情
高性能无锁队列,代码注释
性能非常强大,达到1400w/s。对比其他Disruptor实现,这个实现太简洁了。作者是https://www.zhihu.com/people/gowkh。代码在https://github.com/yireyun/go-queue/blob/master/esQueue.go //esQueuepackagequeueimport( "fmt" "runtime" "s 查看详情
无锁队列的实现
锁是高性能程序的杀手,但是为了保证数据的一致性,在多线程的应用环境下又不得不加锁。但是在某些特殊的场景下,是可以通过优化数据结构来达到无锁的目的。那么我们就来看一下如何实现一个无锁队列。队列:众所周知... 查看详情
高效无锁环形队列
...队列多用于多线程之间数据异步传输,提高数据处理性能。下面介绍常规环形队列和高效无锁队列两种处理方式。常规环形队列如下图所示,队列头代表写指针,队列尾代表读指针,通过判断读写的位置判断队列... 查看详情
C++ 无锁队列
...是否是线程安全的设计,还是需要继续优化以获得更高的性能。12个线程出队和12个线程入队。开 查看详情
网络库压力测试:mongolsvsevpp
...基于libevent,进行了许多改造,对c++11友好。据称比libevent性能要好。到底有多好呢?360开发人员有自己的测试,信不信由你。evpp源码下有个httpecho演示,我把它改为hello,world的样子:voidDefaultHandler(evpp::EventLoop*loop,constevpp::http::Contex... 查看详情
java无锁队列disruptor,内存队列的生产解决方案(代码片段)
...易所LMAX开源的用于生产交易中的内存队列。为了实现高性能交易撮合队列时,现在普遍的交易撮合引擎都采用了内存队列的方式,这种方式减少了持久化过程中带来的磁盘IO延迟,可以提交整体的交易性能。Disruptor便... 查看详情
C++ 无锁队列与多线程崩溃
...序列化变量访问,但我试图尽可能避免使用互斥锁以提高性能。我有一个指针队列,它可能被许多线程填充并被许多线程消耗。它适用于单个线程,但当我使用多个线程运行时会崩溃。看起来消费者可能会得 查看详情
c++编程经验(10):无锁编程其实没那么玄乎(代码片段)
文章目录atomic演示曾经有个人,问我对无锁队列的实现是怎么想的。我想了一会儿,还是纳闷儿,无锁,也能做消息队列吗?然后他让我回去好好查查。没错,他就是面试官。atomic在有些场景里面,是... 查看详情
c++编程经验(10):无锁编程其实没那么玄乎(代码片段)
文章目录atomic演示曾经有个人,问我对无锁队列的实现是怎么想的。我想了一会儿,还是纳闷儿,无锁,也能做消息队列吗?然后他让我回去好好查查。没错,他就是面试官。atomic在有些场景里面,是... 查看详情
秒懂:jctool的mpsc高性能无锁队列(史上最全+10w字长文)(代码片段)
文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录博客园版为您奉上珍贵的学习资源:免费赠送:《尼恩Java面试宝典》持续更新+史上最全+面试必备2000页+面试必备+大厂必备+... 查看详情
发布一个高性能的reactor模式的c++网络库:evpp(代码片段)
发布一个高性能的Reactor模式的C++网络库:evpp简介https://github.com/Qihoo360/evpp是一个基于libevent开发的现代化的支持C++11特性的高性能网络库,自带TCP/UDP/HTTP等协议的异步非阻塞式的服务器和客户端库。特性现代... 查看详情
谈谈存储软件的无锁设计
面向磁盘设计的存储软件不需要考虑竞争锁带来的性能影响。磁盘存储软件的性能瓶颈点在于磁盘,磁盘抖动会引入极大的性能损耗。因此,传统存储软件的设计不会特别在意处理器的使用效率。曾经对一个存储虚拟化软件进行... 查看详情
多线程编程之无锁队列
关于无锁队列的概念与实现,可以参考博文《无锁队列的实现》,主要涉及到的知识点包括CAS原子操作、无锁队列的链表实现、无锁队列的数组实现以及ABA问题。 下面借鉴了《多线程的那点儿事(之无锁队列)》的代码,说... 查看详情
高效无锁环形队列
...队列多用于多线程之间数据异步传输,提高数据处理性能。下面介绍常规环形队列和高效无锁队列两种处理方式。常规环形队列如下图所示,队列头代表写指针,队列尾代表读指针,通过判断读写的位置判断队列... 查看详情