select和poll之间的性能对比

poll 通过 revents 字段直接标记事件,而 select 需要遍历三个独立的位图。这种设计差异直接导致了性能上的差距。以下是详细对比:


1. poll 的事件检测:单次遍历 + 直接标记

(1)pollfd 结构体

cpp 复制代码
struct pollfd {
    int fd;         // 文件描述符
    short events;   // 用户关心的事件(如 POLLIN、POLLOUT)
    short revents;  // 内核填充的实际发生的事件
};
  • events :用户设置的监听事件掩码(如 POLLIN | POLLHUP)。
  • revents :由内核填充,表示该 fd 上实际发生的事件(如 POLLIN | POLLERR)。

(2)内核处理逻辑

  1. 用户调用

    cpp 复制代码
    struct pollfd fds[1000];
    for (int i = 0; i < 1000; i++) {
        fds[i].fd = ...;
        fds[i].events = POLLIN; // 只关心可读事件
    }
    poll(fds, 1000, timeout);
  2. 内核行为

    • 单次遍历 :内核直接遍历 fds 数组(1000 次)。
    • 直接标记
      • 对每个 fds[i],内核检查 fd 的状态。
      • 如果事件发生(如可读),则将 revents 设置为对应标志(如 POLLIN)。
      • 无需额外遍历或位操作
  3. 用户检查

    cpp 复制代码
    for (int i = 0; i < 1000; i++) {
        if (fds[i].revents & POLLIN) {
            // fd 可读
        }
    }

(3)优势

  • 高效 :内核只需遍历一次数组,且直接填充 revents,无冗余操作。
  • 灵活revents 可以同时标记多个事件(如 POLLIN | POLLHUP)。

2. select 的事件检测:三次遍历 + 位图操作

(1)select 的参数

cpp 复制代码
int select(
    int nfds,          // 最大 fd + 1
    fd_set *readfds,   // 可读事件位图
    fd_set *writefds,  // 可写事件位图
    fd_set *exceptfds, // 异常事件位图
    struct timeval *timeout
);
  • 三个位图:分别对应读、写、异常事件。
  • nfds :强制内核遍历 0nfds-1 的所有 fd,即使未被监听。

(2)内核处理逻辑

  1. 用户调用

    cpp 复制代码
    fd_set read_fds, write_fds;
    FD_ZERO(&read_fds);
    FD_ZERO(&write_fds);
    for (int fd = 3; fd <= 1002; fd++) {
        FD_SET(fd, &read_fds);  // 监听 1000 个 fd 的可读事件
    }
    select(1003, &read_fds, NULL, NULL, NULL);
  2. 内核行为

    • 拷贝位图 :将 read_fdswrite_fdsexcept_fds 从用户空间拷贝到内核。
    • 三次遍历
      1. 遍历 read_fds
        • 对每个 fd(从 01002):
          • 如果 FD_ISSET(fd, &read_fds) 为真,检查是否可读。
          • 如果可读,保留该 fd 在位图中(内核可能直接修改位图)。
      2. 遍历 write_fds(同理)。
      3. 遍历 except_fds(同理)。
    • 无效操作
      • 即使 fd=0,1,2 未被设置,内核仍会检查它们(共 3 次无效操作)。
      • 如果 fd 数量更多(如 10,000),无效操作会线性增加。
  3. 用户检查

    cpp 复制代码
    for (int fd = 3; fd <= 1002; fd++) {
        if (FD_ISSET(fd, &read_fds)) {
            // fd 可读
        }
    }

(3)劣势

  • 低效
    • 内核需要三次独立遍历(读、写、异常),且每次遍历都可能涉及全局扫描。
    • 每次遍历需通过 FD_ISSET 位操作检查 fd 是否被监听。
  • 局限性
    • FD_SETSIZE 限制(通常 1024),无法直接支持大量 fd。
    • 位图操作在 fd 稀疏时(如跳过前 1000 个 fd)浪费大量计算。

3. 直观对比:poll vs select 的事件检测

步骤 poll select
事件标记方式 revents 字段直接填充 三个独立位图(读、写、异常)
内核遍历次数 1 次(遍历 pollfd 数组) 3 次(遍历三个位图)
无效 fd 检查 无(仅检查用户提供的 fd) 有(必须检查 0nfds-1
位图操作 每次检查需 FD_ISSET 位操作
支持 fd 数量 无限制(仅受系统资源限制) FD_SETSIZE 限制(通常 1024)

4. 为什么 poll 更高效?

  1. 单次遍历 vs 三次遍历
    • poll 只需遍历用户提供的 fd 列表一次,而 select 需要遍历三个位图各一次。
  2. 直接标记 vs 位图操作
    • pollrevents 直接填充事件标志,无需位操作。
    • select 需通过 FD_ISSET 检查位图,额外开销大。
  3. 无效操作优化
    • poll 完全跳过未监听的 fd,而 select 必须检查所有 fd(即使未被监听)。

5. 代码示例对比

(1)poll 检测可读事件

cpp 复制代码
struct pollfd fds[1000];
for (int i = 0; i < 1000; i++) {
    fds[i].fd = 3 + i;
    fds[i].events = POLLIN;
}
poll(fds, 1000, timeout);

for (int i = 0; i < 1000; i++) {
    if (fds[i].revents & POLLIN) {
        printf("fd %d is readable\n", fds[i].fd);
    }
}

(2)select 检测可读事件

cpp 复制代码
fd_set read_fds;
FD_ZERO(&read_fds);
for (int fd = 3; fd <= 1002; fd++) {
    FD_SET(fd, &read_fds);
}
select(1003, &read_fds, NULL, NULL, timeout);

for (int fd = 3; fd <= 1002; fd++) {
    if (FD_ISSET(fd, &read_fds)) {
        printf("fd %d is readable\n", fd);
    }
}

6. 总结

  • pollrevents 设计
    通过单次遍历和直接标记事件,避免了 select 的三次遍历和位图操作,因此性能更高。
  • select 的局限性
    全局扫描和位图操作使其在处理大量 fd 时效率低下,且受 FD_SETSIZE 限制。
  • 适用场景
    • 高并发(大量 fd)时,优先使用 poll(或更现代的 epoll/kqueue)。
    • 低并发或遗留代码中,select 仍可能被使用,但性能较差。
相关推荐
程序员-King.17 小时前
day158—回溯—全排列(LeetCode-46)
算法·leetcode·深度优先·回溯·递归
星火开发设计18 小时前
C++ 数组:一维数组的定义、遍历与常见操作
java·开发语言·数据结构·c++·学习·数组·知识
月挽清风18 小时前
代码随想录第七天:
数据结构·c++·算法
TTGGGFF18 小时前
控制系统建模仿真(一):掌握控制系统设计的 MAD 流程与 MATLAB 基础运算
开发语言·matlab
小O的算法实验室18 小时前
2026年AEI SCI1区TOP,基于改进 IRRT*-D* 算法的森林火灾救援场景下直升机轨迹规划,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
2501_9444241219 小时前
Flutter for OpenHarmony游戏集合App实战之贪吃蛇食物生成
android·开发语言·flutter·游戏·harmonyos
小郭团队19 小时前
2_1_七段式SVPWM (经典算法)算法理论与 MATLAB 实现详解
嵌入式硬件·算法·硬件架构·arm·dsp开发
充值修改昵称19 小时前
数据结构基础:从二叉树到多叉树数据结构进阶
数据结构·python·算法
Deepoch19 小时前
Deepoc数学大模型:发动机行业的算法引擎
人工智能·算法·机器人·发动机·deepoc·发动机行业
Lhuu(重开版20 小时前
JS:正则表达式和作用域
开发语言·javascript·正则表达式