libeio用处
来源:互联网 发布:web服务器默认端口 编辑:程序博客网 时间:2024/05/16 07:51
顺手写了个程序对比了一下多线程IO和单线程异步IO的性能差异。需要说明的是,Linux上目前的异步IO是由用户态线程模拟的。目前内核原生的AIO和glibc中的异步IO都有缺陷,libeio目前感觉比较好(因为nodejs在用)。单线程异步IO指的是主线程中控制IO的代码全部都是在主线程中执行的,libeio内部使用的线程对外来说完全不可感知。单线程异步IO的好处就是主流程中的控制代码完全处于单线程环境,可以完全不用考虑锁的问题。
应用场景客户端启动20个线程同时从服务端求数据。服务端从磁盘出文件回给客户端。服务端有两种实现:1、使用多线程每个线程服务一个用户。2、使用单线程和异步IO来服务多个用户。
测试时服务端的处理器为单核600MHZ的ArmV6(RaspberryPi),客户端为2.5GHz Intel Core i5(2013版Macmini),因此客户端不会成为性瓶可以体现出服务端的差异。两台机器用100M网线直连。
20个线程下载700KB的文件:(测4次录每次成绩,数据为所花费的时间,越少越好)
多线程
183393
183505
183792
182878
异步IO
198165
191537
196565
193653
20个线程下载40K文件
多线程
12434
13099
12773
12958
异步IO
15293
15073
15974
15631
两种方案的优缺点对比
1、性上传统的多线程方案占有优势。不过测程序中多线程中完全没有使用锁,现实中由于的锁的限制多线程的性将大打折扣。
2、单线程异步IO程序中,由于控制代码在单线程环境中运行,可以完全无锁。
3、单线程异步IO程序中同时服务20个用户只使用了3个线程(libeio使用线程模拟异步IO,当前Linux上异步IO没有更好的决方案),多线程版本的程序使用了21个线程(包括主线程)。由于线程多,因此多线程版本的内存占用比libeio版本了很多。
4、libieo版本的流程没有多线程版本的直观。
总结
单线程异步IO的主流程代码可以完全无锁,这个是单线程异步IO的最大优势。在性能损失不多的情况下(和完全无锁的多线程方案对比,现实中完全无锁的多线程环境几乎不存在),很值得考虑。唯一需要考虑的,单线程异步IO可能对多核CPU的利用不如传统的多线程充分。
另外,还可以扩展一下,在编写复杂应用时,除了IO等阻塞操作,CPU密集的操作也可以拿出来到单独的线程中执行,尽量把耗时不多的逻辑部分留在单线程中,由于运行在单线程环境,这样很大程度上简化逻辑部分的编写。
相信上面这段话已经将libeio的feature讲的足够清楚:提供全套异步文件操作的接口,让使用者能写出完全非阻塞的程序。阻塞意味着低效,但非阻塞一定要有很好的通知机制才能做到高效。
其实linux下的AIO(异步IO)并不是没有解决方案:在用户态,多线程同步来模拟的异步IO,如Glibc 的AIO;以及在内核态实现异步通知,如linux内核2.6.22之后实现的Kernel Native AIO。但两者都存在让使用者望而祛步的问题。
Glibc的AIO bug太多,而且IO发起者并不是最后的IO终结者(callback是在单独的线程执行的);而kernel Native AIO只支持O_DIRECT方式,无法利用Page cache。
正是由于上述原因,Marc Alexander Lehmann大佬决定自己开发一个AIO库,及libeio。libeio也是在用户态用多线程同步来模拟异步IO,但实现更高效,代码也更可靠,目前虽然是beta版,但已经可以上生产了(node.js底层就是用libev和libeio来驱动的)。还要强调一点:libeio里IO的终结者正是当初IO的发起者(这一点非常重要,因为IO都是由用户的request而发起,而IO完成后返回给用户的response也能在处理request的线程中完成)。
实现
一次异步IO操作可以分为三个阶段:
初始化 –> 提交IO –> 通知worker线程 (主线程);
取request –> 执行IO –> 通知主线程 (worker线程);
取response –> callback –> 结束IO (主线程)。
下面我们通过一张流程图来剖析一下libeio的源码实现。
1. 主线程调用eio_init函数,主要是初始化req_queue,res_queue以及对应的mutex和cond;
2-3. 所有的IO操作其实都是对eio_sumbit的调用,而eio_sumbit的职能是将IO操作封装为request并插入到req_queue;并调用cond_signal向worker线程发出reqwait已经OK的信号;
libeio处理流程图
4. worker线程被创建后执行的函数为etp_proc,etp_proc启动后会一直等待reqwait条件的出现;
5-6. 当reqwait条件变量满足时,etp_proc从req_queue中取得一个待处理的request;并调用eio_execute来同步执行该IO操作;
7-8. eio_execute完成后,将response插入到res_queue队列中;同时调用want_poll来通知主线程request已经处理完毕;
9. 这里worker线程通知主线程的机制是通过向pipe[1]写一个byte数据;
10. 当主线程发现pipe[0]可读时,就调用eio_poll;
11. eio_poll从res_queue里取response,并调用该IO操作在init时设置的callback函数完成后续处理;
12. 在res_queue中没有待处理response时,调用done_poll;
13-14. done_poll从pipe[0]读出一个byte数据,该IO操作完成。
后记
libeio的实现就是这么简洁,这里需要说明两点:
1. 在worker线程完成IO请求,通知主线程的机制是需要使用者自定义的,wait_poll和done_poll就是libeio提供给使用者的接口(pipe是一种常用的线程通知机制)。
2. worker线程并不是为每个请求都创建一个,而是维护了一个worker线程池,关于这部分将会在下面的文章中单独讲到。
- libeio用处
- libeio 安装
- libeio性能测试
- libeio的个人理解
- libeio-异步I/O库初窥
- libeio源码分析 – 主流程
- libeio源码分析 – 主流程
- 关于libeio相关文章的几点补充
- xml用处
- PendingIntent 用处
- TexturePacker用处
- notepad++用处
- volatile 用处
- PHP用处
- weakSelf用处
- outlook用处
- return用处
- net_write_timeout用处
- POJ 2513 Colored Sticks
- linux bash shell 在控制台打印 8格棋盘的方法
- magento批量删除产品
- autoconf.h文件找不到
- yii网站开发技巧 要求全站登录才访问
- libeio用处
- 闪回技术(2):闪回归档
- 数据公钥加密和认证中的私钥公钥
- OpenGL ES 3.0-Transform Feedback Object
- Linux下malloc/free详解
- linux下C语言线程的介绍
- js如何实现整除
- 小小回忆录
- ubuntu10.04安装curb问题及解决方案