陈硕的Blog

muduo 与 libevent2 吞吐量对比

libevent 是一款非常好用的 C 语言网络库,它也采用 Reactor 模型,正好可以与 muduo 做一对比。

本文用 ping pong 测试来对比 muduo 和 libevent2 的吞吐量,测试结果表明 muduo 吞吐量平均比 libevent2 高 18% 以上,个别情况达到 70%。

测试对象

测试环境与测试方法

测试环境与前文《muduo 与 boost asio 吞吐量对比》相同。

我自己编写了 libevent2 的 ping pong 测试代码,地址在 http://github.com/chenshuo/recipes/tree/master/pingpong/libevent/ 。由于这个测试代码没有使用多线程,所以本次测试只对比单线程下的性能。

测试内容为:客户端与服务器运行在同一台机器,均为单线程,测试并发连接数为 1/10/100/1000/10000 时的吞吐量。

在同一台机器测试吞吐量的原因:

  • 现在的 CPU 很快,即便是单线程单 TCP 连接也能把 Gigabit 以太网的带宽跑满。如果用两台机器,所有的吞吐量测试结果都将是 100 MiB/s,失去了对比的意义。(或许可以对比哪个库占的 CPU 少。)
  • 在同一台机器上测试,可以在 CPU 资源相同的情况下,单纯对比网络库的效率。也就是说单线程下,服务端和客户端各占满 1 个 CPU,比较哪个库的吞吐量高。

测试结果

单线程吞吐量测试,数字越大越好:

muduo_libevent_16k

以上结果让人大跌眼镜,muduo 居然比 libevent 快 70%!跟踪 libevent2 的源代码发现,它每次最多从 socket 读取 4096 字节的数据 (证据在 buffer.c 的 evbuffer_read() 函数),怪不得吞吐量比 muduo 小很多。因为在这一测试中,muduo 每次读取 16384 字节,系统调用的性价比较高。

buffer.c:#define EVBUFFER_MAX_READ      4096

为了公平起见,我再测了一次,这回两个库都发送 4096 字节的消息。

muduo_libevent_4k

测试结果表明 muduo 吞吐量平均比 libevent2 高 18% 以上。

讨论

由于 libevent2 每次最多从网络读取 4096 字节,大大限制了它的吞吐量。

posted @ 2010-09-05 19:14 陈硕 阅读(3420) | 评论 (3)编辑 收藏

muduo 与 boost asio 吞吐量对比

muduo (http://code.google.com/p/muduo) 是一个基于 Reactor 模式的 C++ 网络库,我在编写它的时候并没有以高并发高吞吐为主要目标,但出乎我的意料,ping pong 测试表明,muduo 吞吐量比 boost.asio 高 15% 以上。

测试对象

测试环境

硬件:DELL 490 工作站,双路 Intel quad core Xeon E5320 CPU,16G 内存

操作系统:Ubuntu Linux Server 10.04.1 LTS x86_64

编译器:g++ 4.4.3

测试方法

依据 asio 性能测试 http://think-async.com/Asio/LinuxPerformanceImprovements 的办法,用 ping pong 协议来测试吞吐量。

简单地说,ping pong 协议是客户端和服务器都实现 echo 协议。当 TCP 连接建立时,客户端向服务器发送一些数据,服务器会 echo 回这些数据,然后客户端再 echo 回服务器。这些数据就会像乒乓球一样在客户端和服务器之间来回传送,直到有一方断开连接为止。这是用来测试吞吐量的常用办法。

asio 的测试代码取自 http://asio.cvs.sourceforge.net/viewvc/asio/asio/src/tests/performance/ ,未作更改。

muduo 的测试代码在 0.1.1 软件包内,路径为 examples/pingpong/,代码如 http://gist.github.com/564985 所示。

muduo 和 asio 的优化编译参数均为 -O2 -finline-limit=1000

$ BUILD_TYPE=release ./build.sh  # 编译 muduo 的优化版本

我主要做了两项测试:

  • 单线程测试,测试并发连接数为 1/10/100/1000/10000 时的吞吐量。
  • 多线程测试,并发连接数为 100 或 1000,服务器和客户端的线程数同时设为 1/2/3/4。(由于我家里只有一台 8 核机器,而且服务器和客户端运行在同一台机器上,线程数大于 4 没有意义。)

所有测试中,ping pong 消息的大小均为 16k bytes。测试用的 shell 脚本可从 http://gist.github.com/564985 下载。

测试结果

单线程测试的结果,数字越大越好:

single_thread

多线程测试的结果,数字越大越好:

multiple_thread_100conn

image007

测试结果表明 muduo 吞吐量平均比 asio 高 15% 以上。

讨论

muduo 出乎意料地比 asio 性能优越,我想主要得益于其简单的设计和简洁的代码。

asio 在多线程测试中表现不佳,我猜测其主要原因是测试代码只使用了一个 io_service,如果改用“io_service per CPU”的话,性能应该有所提高。我对 asio 的了解程度仅限于能读懂其代码,希望能有 asio 高手编写“io_service per CPU”的 ping pong 测试,以便与 muduo 做一个公平的比较。

ping pong 测试很容易实现,欢迎其他网络库(ACE、POCO、libevent 等)也能加入到对比中来,期待这些库的高手出马。

posted @ 2010-09-04 16:30 陈硕 阅读(4717) | 评论 (5)编辑 收藏

发布一个基于 Reactor 模式的 C++ 网络库

发布一个基于 Reactor 模式的 C++ 网络库

陈硕 (giantchen_AT_gmail)

Blog.csdn.net/Solstice

2010 Aug 30

本文主要介绍 muduo 网络库的使用。其设计与实现将有另文讲解。

目录

由来 1

下载与编译 2

例子 2

基本结构 3

公开接口 4

内部实现 4

线程模型 5

结语 5

由来

半年前我写了一篇《学之者生,用之者死——ACE历史与简评》,其中提到“我心目中理想的网络库”的样子:

  • 线程安全,支持多核多线程
  • 不考虑可移植性,不跨平台,只支持 Linux,不支持 Windows。
  • 在不增加复杂度的前提下可以支持 FreeBSD/Darwin,方便将来用 Mac 作为开发用机,但不为它做性能优化。也就是说 IO multiplexing 使用 poll 和 epoll。
  • 主要支持 x86-64,兼顾 IA32
  • 不支持 UDP,只支持 TCP
  • 不支持 IPv6,只支持 IPv4
  • 不考虑广域网应用,只考虑局域网
  • 只支持一种使用模式:non-blocking IO + one event loop per thread,不考虑阻塞 IO
  • API 简单易用,只暴露具体类和标准库里的类,不使用 non-trivial templates,也不使用虚函数
  • 只满足常用需求的 90%,不面面俱到,必要的时候以 app 来适应 lib
  • 只做 library,不做成 framework
  • 争取全部代码在 5000 行以内(不含测试)
  • 以上条件都满足时,可以考虑搭配 Google Protocol Buffers RPC

在想清楚这些目标之后,我开始第三次尝试编写自己的 C++ 网络库。与前两次不同,这次我一开始就想好了库的名字,叫 muduo (木铎),并在 Google code 上创建了项目: http://code.google.com/p/muduo/ 。muduo 的主体内容在 5 月底已经基本完成,现在我把它开源。

本文主要介绍 muduo 网络库的使用,其设计与实现将有另文讲解。

下载与编译

下载地址: http://muduo.googlecode.com/files/muduo-0.1.0-alpha.tar.gz

SHA1 Checksum: 5d3642e311177ded89ed0d15c10921738f8c984c

Muduo 使用了 Linux 较新的系统调用,要求 Linux 的内核版本大于 2.6.28 (我自己用的是 2.6.32 )。在 Debian Squeeze / Ubuntu 10.04 LTS 上编译测试通过,32 位和 64 位系统都能使用。

Muduo 采用 CMake 为 build system,安装方法:

$ sudo apt-get install cmake

Muduo 依赖 Boost,很容易安装:

$ sudo apt-get install libboost1.40-dev # 或 libboost1.42-dev

编译方法很简单:

$ tar zxf muduo-0.1.0-alpha.tar.gz

$ cd muduo/

$ ./build.sh

# 编译生成的可执行文件和静态库文件分别位于 ../build/debug/{bin,lib}

如果要编译 release 版,可执行

$ BUILD_TYPE=release ./build.sh

# 编译生成的可执行文件和静态库文件分别位于 ../build/release/{bin,lib}

编译完成之后请试运行其中的例子。比如 bin/inspector_test ,然后通过浏览器访问 http://10.0.0.10:12345/ 或 http://10.0.0.10:12345/proc/status,其中 10.0.0.10 替换为你的 Linux box 的 IP。

例子

Muduo 附带了几十个小例子,位于 examples 目录。其中包括从 Boost.Asio、JBoss Netty、Python Twisted 等处移植过来的例子。

examples

|-- simple # 简单网络协议的实现

|   |-- allinone  # 在一个程序里同时实现下面 5 个协议

|   |-- chargen   # RFC 864,可测试带宽

|   |-- daytime # RFC 867

|   |-- discard # RFC 863

|   |-- echo # RFC 862

|   |-- time # RFC 868

|   `-- timeclient # time 协议的客户端

|-- hub # 一个简单的 pub/sub/hub 服务,演示应用级的广播

|-- roundtrip # 测试两台机器的网络延时与时间差

|-- asio # 从 Boost.Asio 移植的例子

|   |-- chat # 聊天服务

|   `-- tutorial # 一系列 timers

|-- netty # 从 JBoss Netty 移植的例子

|   |-- discard # 可用于测试带宽,服务器可多线程运行

|   |-- echo # 可用于测试带宽,服务器可多线程运行

|   `-- uptime # TCP 长连接

`-- twisted # 从 Python Twisted 移植的例子

    `-- finger # finger01 ~ 07

基本结构

Muduo 的目录结构如下。

muduo

|-- base # 与网络无关的基础代码,已提前发布

`-- net # 网络库

    |-- http # 一个简单的可嵌入的 web 服务器

    |-- inspect # 基于以上 web 服务器的“窥探器”,用于报告进程的状态

    `-- poller # poll(2) 和 epoll(4) 两种 IO multiplexing 后端

Muduo 是基于 Reactor 模式的网络库,其核心是个事件循环 EventLoop,用于响应计时器和 IO 事件。Muduo 采用基于对象(object based)而非面向对象(object oriented)的设计风格,其接口多以 boost::function + boost::bind 表达

Muduo 的头文件明确分为客户可见和客户不可见两类。客户可见的为白底,客户不可见的为灰底。

inc

这里简单介绍各个头文件及 class 的作用,详细的介绍留给以后的博客。

公开接口
  • Buffer 仿 Netty ChannelBuffer 的 buffer class,数据的读写透过 buffer 进行
  • InetAddress 封装 IPv4 地址 (end point),注意,muduo 目前不能解析域名,只认 IP
  • EventLoop 反应器 Reactor,用户可以注册计时器回调
  • EventLoopThread 启动一个线程,在其中运行 EventLoop::loop()
  • TcpConnection 整个网络库的核心,封装一次 TCP 连接
  • TcpClient 用于编写网络客户端,能发起连接,并且有重试功能
  • TcpServer 用于编写网络服务器,接受客户的连接
  • 在这些类中,TcpConnection 的生命期依靠 shared_ptr 控制(即用户和库共同控制)。Buffer 的生命期由 TcpConnection 控制。其余类的生命期由用户控制。
  • HttpServer 和 Inspector,暴露出一个 http 界面,用于监控进程的状态,类似于 Java JMX。这么做的原因是,《程序员修炼之道》第 6 章第 34 条提到“对于更大、更复杂的服务器代码,提供其操作的内部试图的一种漂亮技术是使用内建的 Web 服务器”,Jeff Dean 也说“(每个 Google 的服务器进程)Export HTML-based status pages for easy diagnosis”。
内部实现
  • Channel 是 selectable IO channel,负责注册与响应 IO 事件,它不拥有 file descriptor。它是 Acceptor、Connector、EventLoop、TimerQueue、TcpConnection 的成员,生命期由后者控制。
  • Socket 封装一个 file descriptor,并在析构时关闭 fd。它是 Acceptor、TcpConnection 的成员,生命期由后者控制。EventLoop、TimerQueue 也拥有 fd,但是不封装为 Socket。
  • SocketsOps 封装各种 sockets 系统调用。
  • EventLoop 封装事件循环,也是事件分派的中心。它用 eventfd(2) 来异步唤醒,这有别于传统的用一对 pipe(2) 的办法。它用 TimerQueue 作为计时器管理,用 Poller 作为 IO Multiplexing。
  • Poller 是 PollPoller 和 EPollPoller 的基类,采用“电平触发”的语意。它是 EventLoop 的成员,生命期由后者控制。
  • PollPoller 和 EPollPoller 封装 poll(2) 和 epoll(4) 两种 IO Multiplexing 后端。Poll 的存在价值是便于调试,因为 poll(2) 调用是上下文无关的,用 strace 很容易知道库的行为是否正确。
  • Connector 用于发起 TCP 连接,它是 TcpClient 的成员,生命期由后者控制。
  • Acceptor 用于接受 TCP 连接,它是 TcpServer 的成员,生命期由后者控制。
  • TimerQueue 用 timerfd 实现定时,这有别于传统的设置 poll/epoll_wait 的等待时长的办法。为了简单起见,目前用链表来管理 Timer,如果有必要可改为优先队列,这样复杂度可从 O(n) 降为 O(ln n) (某些操作甚至是 O(1))。它是 EventLoop 的成员,生命期由后者控制。
  • EventLoopThreadPool 用于创建 IO 线程池,也就是说把 TcpConnection 分派到一组运行 EventLoop 的线程上。它是 TcpServer 的成员,生命期由后者控制。

线程模型

Muduo 的线程模型符合我主张的 one loop per thread + thread pool 模型。每个线程最多有一个 EventLoop。每个 TcpConnection 必须归某个 EventLoop 管理,所有的 IO 会转移到这个线程,换句话说一个 file descriptor 只能由一个线程读写。TcpConnection 所在的线程由其所属的 EventLoop 决定,这样我们可以很方便地把不同的 TCP 连接放到不同的线程去,也可以把一些 TCP 连接放到一个线程里。TcpConnection 和 EventLoop 是线程安全的,可以跨线程调用。TcpServer 直接支持多线程,它有两种模式:

1. 单线程,accept 与 TcpConnection 用同一个线程做 IO。

2. 多线程,accept 与 EventLoop 在同一个线程,另外创建一个 EventLoopThreadPool,新到的连接会按 round-robin 方式分配到线程池中。

结语

Muduo 是我对常见网络编程任务的总结,用它我能很容易地编写多线程的 TCP 服务器和客户端。Muduo 是我业余时间的作品,代码估计还有很多 bug,功能也不完善(例如不支持 signal 处理),待日后慢慢改进吧。

posted @ 2010-08-29 23:42 陈硕 阅读(11971) | 评论 (20)编辑 收藏

发布一个 Linux 下的 C++ 多线程库

发布一个我自己业余时间编写的 C++ 多线程库 for Linux,这个库只有不到 1000 行源代码,封装了 pthreads 的常用功能(互斥器、条件变量、线程),实现了简单的线程池,并仿照 java concurrent 包编写了 BlockingQueue 和 CountDownLatch。库里的每个 class 都提供了使用样例。

这个库的内容:

  • 整数的原子操作, AtomicInt32 和 AtomicInt64
  • 线程,Thread
  • 线程池,ThreadPool
  • 互斥器与条件变量, MutexLock,MutexLockGuard 与 Condition
  • 带调用栈信息 (stack trace) 的异常基类,Exception
  • 仿 Java concurrent 的 BlockingQueue 和 CountDownLatch
  • Singleton 与 ThreadLocal

注:我故意没有提供信号量 Semaphore 的封装。将来或许会增加读写锁的封装,如果我在博客中用到的话。

Thread 和 ThreadPool 的接口设计采用了《以 boost::function 和 boost:bind 取代虚函数》里提倡的风格,没有使用继承和基类。

注意,CurrentThread 有一个 thread local 变量 t_threadName,其作用是在调试和分析 core dump 时打印线程的名称,例如:

(gdb) p 'muduo::CurrentThread::t_threadName'
$4 = 0x4057fe "ThreadPool2"

MutexLock,MutexLockGuard 与 Condition 的使用请参考《多线程服务器的常用编程模型》。

CountDownLatch 的使用样例见 test/BlockingQueue_test.cc

git 下载地址: http://github.com/chenshuo/recipes

浏览源代码: http://github.com/chenshuo/recipes/tree/master/thread/

这个库在 Debian Squeeze 和 Ubuntu 10.04 LTS 下编译测试通过,适用于 x86 和 x86-64 平台。

posted @ 2010-08-21 23:47 陈硕 阅读(9684) | 评论 (37)编辑 收藏

整合 Google 开源 C++ 代码

     摘要: Google 开源了很多优秀的 C++ 程序库,本文介绍如何将其中几个整合到一起。
本文涉及的 Google 库有:
gflags - 命令行参数解析。可以完全用命令行来配置应用程序,省去配置文件。
gtest - C++ 单元测试框架
gmock - C++ 单元测试中用到的 mock
glog - 日志库
protobuf - 高效的网络协议格式
还有第三方的 libunwind 和 zlib。  阅读全文

posted @ 2010-04-17 21:43 陈硕 阅读(15037) | 评论 (9)编辑 收藏

对 C++ 历史的个人观点

     摘要: C++ 语言设计的三大约束,历史发展中的谎言与真相。  阅读全文

posted @ 2010-04-06 21:34 陈硕 阅读(8391) | 评论 (25)编辑 收藏

学之者生,用之者死——ACE历史与简评

     摘要: ACE 是现代面向对象网络编程的鼻祖,确立了许多重要模式,如 Reactor、Acceptor 等,重要到我们甚至觉得网络编程就应该是那样的。但为什么 ACE 叫好不叫座?大名鼎鼎却使用者寥寥?本文谈谈从其诞生背景、代码质量等方面谈谈我的个人观点。  阅读全文

posted @ 2010-03-11 20:34 陈硕 阅读(6469) | 评论 (4)编辑 收藏

多线程服务器的常用编程模型

本文主要讲我个人在多线程开发方面的一些粗浅经验。总结了一两种常用的线程模型,归纳了进程间通讯与线程同步的最佳实践,以期用简单规范的方式开发多线程程序。万字长文,慎入,有 PDF 版下载。

本文 PDF 版下载: http://files.cppblog.com/Solstice/multithreaded_server.pdf


posted @ 2010-02-12 16:51 陈硕 阅读(4392) | 评论 (7)编辑 收藏

当析构函数遇到多线程 —— C++ 中线程安全的对象回调

     摘要: 编写线程安全的类不是难事,用同步原语保护内部状态即可。但是对象的生与死不能由对象自身拥有的互斥器来保护。如何保证即将析构对象 x 的时候,不会有另一个线程正在调用 x 的成员函数?或者说,如何保证在执行 x 的成员函数期间,对象 x 不会在另一个线程被析构?如何避免这种竞态条件是 C++ 多线程编程面临的基本问题,可以借助 boost 的 shared_ptr 和 weak_ptr 完美解决。这也是实现线程安全的 Observer 模式的必备技术。全文 1 万 2 千余字,有 PDF 版下载。  阅读全文

posted @ 2010-01-28 08:13 陈硕 阅读(7019) | 评论 (5)编辑 收藏

仅列出标题
共6页: 1 2 3 4 5 6 
<2011年3月>
272812345
6789101112
13141516171819
20212223242526
272829303112
3456789

导航

统计

常用链接

随笔分类

随笔档案

相册

搜索

最新评论

阅读排行榜

评论排行榜