分享

采用libevent写管道事件通知与broadcast通知的效率对比

 阿青哥Joe 2018-06-30

我们的系统后台处理模仿memcached,采用libevent的事件通知实现数据的发送,采用linux的条件变量实现业务接收处理。因其存在可替换性,故对这两种方式的效率进行了部分测试。下面粘贴部分源码:

预测: 因条件变量机制是linux底层做的,而libevent的事件通知则是通过线程接收与事件关联,再通过管道实现数据传递。故应该是条件变量更快一点。

1,测试代码,发送数据并写管道通知或者写条件变量通知:

  1. static void* test1(void* args)
  2. {
  3. int i = 1;
  4. char buff[1] = {'m'};
  5. for (; i < 100000001; ++i)
  6. {
  7. pthread_mutex_lock(&mutexQueue);
  8. q1.push(i);
  9. pthread_mutex_unlock(&mutexQueue);
  10. //如采用写管道,就不采用broadcast,如采用broadcast,就不采用写管道
  11. if (write(threads->notify_send_fd, buff, 1) != 1)
  12. {
  13. LOG_ERROR("Write Pipe Failed!");
  14. }
  15. //pthread_cond_broadcast(&gCondReadInner);
  16. }
  17. }



2,libevent事件与管道创建

  1. {
  2. threads = static_cast<LIBEVENT_THREAD*>(calloc(1, sizeof(LIBEVENT_THREAD)));
  3. threads->base = event_base_new();
  4. int fds[2] = {-1, -1};
  5. if (pipe(fds))
  6. {
  7. LOG_ERROR("Create Pipe Failed!");
  8. return FAILED;
  9. }
  10. threads->notify_receive_fd = fds[0];
  11. threads->notify_send_fd = fds[1];
  12. //创建事件
  13. threads->notify_event = event_new(threads->base, threads->notify_receive_fd,EV_READ | EV_PERSIST, pipefunc, threads);
  14. if (NULL == threads->notify_event)
  15. {
  16. LOG_ERROR("Create Notify Event Failed!");
  17. return FAILED;
  18. }
  19. //添加事件
  20. if (-1 == event_add((event*)threads->notify_event, 0))
  21. {
  22. LOG_ERROR("Add Notify Event Failed!");
  23. return FAILED;
  24. }
  25. //准备结束,启动循环线程
  26. int iRet = 0;
  27. if ((iRet = pthread_create(&threads->thread_id, NULL, CLinkMgr::WorkerLibevent, (void*)threads)) != 0)
  28. {
  29. LOG_ERROR("Create Thread Failed! -- %d", iRet);
  30. return FAILED;
  31. }
  32. }

3,具体的管道接收线程实现

  1. <pre name="code" class="cpp">static void pipefunc(int fd, short which, void* arg)
  2. {
  3. char buff[1] = {0};
  4. if (read(fd, buff, 1) != 1)
  5. {
  6. LOG_ERROR("Read Text Failed!");
  7. return;
  8. }
  9. static int count = 0;
  10. int x;
  11. pthread_mutex_lock(&mutexQueue);
  12. x = q1.front();
  13. q1.pop();
  14. pthread_mutex_unlock(&mutexQueue);
  15. count++;
  16. if (count == 1)
  17. {
  18. LOG_ERROR("++++++++++++++++++++++ Count Start");
  19. }
  20. else if (count >= 100000000)
  21. {
  22. LOG_ERROR("++++++++++++++++++++++ Count End!, count = %d", count);
  23. }
  24. }



4,具体的条件变量读数据实现

  1. static void* ReadWait(void* args)
  2. {
  3. //设置线程对cancel状态的反应,允许退出线程
  4. (void)pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  5. //设置线程对cancel的执行时机,立即执行
  6. (void)pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
  7. static int count = 0;
  8. while (1)
  9. {
  10. pthread_mutex_lock(&mutexQueue);
  11. while (q1.empty())
  12. {
  13. pthread_cond_wait(&gCondReadInner, &mutexQueue);
  14. }
  15. int x = q1.front();
  16. q1.pop();
  17. pthread_mutex_unlock(&mutexQueue);
  18. if (x == 1)
  19. {
  20. LOG_ERROR("++++++++++++++++++++++ Count Start");
  21. }
  22. else if (x >= 100000000)
  23. {
  24. LOG_ERROR("++++++++++++++++++++++ Count End!, count = %d", x);
  25. }
  26. }
  27. }

5,main函数部分代码

  1. //测试线程
  2. pthread_t tid;
  3. (void)pthread_create(&tid, NULL, test1, NULL);
  4. /*
  5. //创建wait线程读
  6. pthread_t tid1;
  7. (void)pthread_create(&tid1, NULL, ReadWait, NULL);
  8. //测试线程
  9. pthread_t tid;
  10. (void)pthread_create(&tid, NULL, test1, NULL);
  11. */
  12. //循环处理
  13. (void)event_base_dispatch(base);

测试结果

测试结果
数据量 libevent管道事件用时 条件变量通讯用时
1000万 7-8秒 2-3秒
1亿 87秒 27秒

综上,条件变量的线程间通信效率是libevent写管道事件通知的3倍,符合我们的预测条件!!!。

也许这就是为什么memcached仅采用libevent写管道进行接收,而发送调用系统的发送函数的原因吧。

另外,通过该测试,能够看出来,这两种都是每秒百万级别的处理速度,对于当前系统的并发量还不存在瓶颈问题。             

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多