Jimmy Chen

A Programmer

(原创)使用Android NDK socket编程对比多路复用select和epoll的性能

  上一篇在分析lowmemorykiller的时候遇到了一个特殊的调用,那就是epoll。epoll属于多路复用的机制之一。之前了解得不是特别深入,所以这一篇做一下深入了解,当然相关概念网上已经很多了,这里主要是通过写代码来进行了解,毕竟再多的概念不如使用一次来得直接。所以这篇的前面部分摘录自网络,在讲解基础的相关知识后,会通过Android NDK,编写一个socket程序,来对比select和epoll两者的性能。

概述

  I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。select,poll,epoll都是IO多路复用的机制。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。下面我们分别谈谈select和epoll的区别:

select

原理概述

  select 的核心功能是调用tcp文件系统的poll函数,不停的查询,如果没有想要的数据,主动执行一次调度(防止一直占用cpu),直到有一个连接有想要的消息为止。从这里可以看出select的执行方式基本就是不停的调用poll,直到有需要的消息为止。

优点

  1. select的可移植性更好,在某些Unix系统上不支持poll()。
  2. select对于超时值提供了更好的精度:微秒,而poll是毫秒。

缺点

  1. 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;
  2. 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大;
  3. select支持的文件描述符数量太小了,默认是1024。但是在Android系统上的Linux内核,默认是128

epoll

原理概述

  epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时, 返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一 个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射技术,这 样便彻底省掉了这些文件描述符在系统调用时复制的开销。

优点

  1. 支持一个进程打开大数目的socket描述符:相比select则没有对FD数量的限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。
  2. IO效率不随FD数目增加而线性下降:epoll不存在这个问题,它只会对”活跃”的socket进行操作,这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有”活跃”的socket才会主动的去调用 callback函数,其他idle状态socket则不会,在这点上,epoll实现了一个”伪”AIO,因为这时候推动力在os内核。在一些 benchmark中,如果所有的socket基本上都是活跃的:比如一个高速LAN环境,epoll并不比select/poll有什么效率,相反,如果过多使用epoll_ctl,效率相比还有稍微的下降。但是一旦使用idle connections模拟WAN环境,epoll的效率就远在select/poll之上了。
  3. 使用mmap加速内核与用户空间的消息传递:这点实际上涉及到epoll的具体实现了。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。

缺点

  1. 需要在2.6以上的内核才支持epoll
  2. 跨平台性较差,select可以在Linux、Windows和Apple平台下适用。但是epoll只能在Linux下适用。

epoll和select性能比较

  下面通过Android NDK编写socket程序,进行epoll和select性能比较。在此之前需要先获取《(原创)在JNI(Native)层调用Android的Log系统》中说到的log系统,将四个文件下载下来适配适用。下面直接贴出代码,关键部分已经在源码中添加注释说明了:

MainActivity.java

native-lib.cpp

AndroidManifest.xml

activity_main.xml

对比结果

  下面是执行前和之前后的截图,从图片中可以看出select和epoll两者还是有差别了,50000次发送调用epoll比select快8秒。因为例子中只是一个线程进行send和receive,所以这里对比的也只是简单的系统调用性能的比较,没有涉及到多线程send和receive。要是多线程send和receive,估计epoll的性能会比select更好。

《(原创)使用Android NDK socket编程对比多路复用select和epoll的性能》 《(原创)使用Android NDK socket编程对比多路复用select和epoll的性能》

最后该项目已经上传到github,欢迎下载:https://github.com/xiaojimmychen/SelectVSEpoll.git

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注