zoukankan      html  css  js  c++  java
  • lockfree buffer test

    性能测试(3): 对无锁队列boost::lockfree::queue和moodycamel::ConcurrentQueue做一个性能对比测试

    版权声明:本文为博主zieckey原创文章,转载时请保留版权信息。 https://blog.csdn.net/zieckey/article/details/69803011

    English version : The performance benchmark of queue with std::mutex against boost::lockfree::queue and moodycamel::ConcurrentQueue

    Brief

    我们使用https://github.com/Qihoo360/evpp项目中的EventLoop::QueueInLoop(...)函数来做这个性能测试。我们通过该函数能够将一个仿函数执行体从一个线程调度到另一个线程中执行。这是一个典型的生产者和消费者问题。

    我们用一个队列来保存这种仿函数执行体。多个生产者线程向这个队列写入仿函数执行体,一个消费者线程从队列中取出仿函数执行体来执行。为了保证队列的线程安全问题,我们可以使用一个锁来保护这个队列,或者使用无锁队列机制来解决安全问题。EventLoop::QueueInLoop(...)函数通过通定义实现了三种不同模式的跨线程交换数据的队列。

    测试对象

    1. evpp-v0.3.2
    2. EventLoop::QueueInLoop(...)函数内的队列的三种实现方式:

    测试环境

    1. Linux CentOS 6.2, 2.6.32-220.7.1.el6.x86_64
    2. Intel(R) Xeon(R) CPU E5-2630 v2 @ 2.60GHz
    3. 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
    }

    我们进行了两种测试:

    1. 一个生产者线程投递1000000个仿函数执行体到消费者线程中执行,统计总耗时。然后同样的方法我们反复测试10次
    2. 生产者线程分别是2/4/6/8/12/16/20,每个线程投递1000000个仿函数执行体到消费者线程中执行,并统计总共耗时

    测试结论

    1. 当我们只有生产者和消费者都只有一个时,大多数测试结果表明moodycamel::ConcurrentQueue的性能是最好的,大概比queue with std::mutex高出10%~50%左右的性能。boost::lockfree::queuequeue with std::mutex的性能只能高出一点点。由于我们的实现中,必须要求能够使用多生产者的写入,所以并没有测试boost中专门的单生产者单消费者的无锁队列boost::lockfree::spsc_queue,在这种场景下,boost稍稍有些吃亏,但并不影响整体测试结果及结论。
    2. 当我们有多个生产者线程和一个消费者线程时,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跟我们讨论。谢谢。

  • 相关阅读:
    python调用go
    manjaro安装qt
    Ubuntu16.04 ROS安装kinect2并获取骨骼数据+配置kinect2_tracker_pd很不专业的博客-程序员宅基地
    Kinect XBOX 360和六轴机械臂的实时映射
    KinectV2.0 VS2019配置记录
    (29条消息) windows下用kinect V2 识别人体骨骼_interstellar-ai的博客-CSDN博客
    Baxter实战:Ubuntu16.04+Kinect2实现动作跟随
    Kinect2和六轴机械臂的实时映射(初步)
    CS395-T: Robot Learning from Demonstration and Interaction
    无需公网IP,远程SSH访问Linux服务器!
  • 原文地址:https://www.cnblogs.com/lvdongjie/p/9681536.html
Copyright © 2011-2022 走看看