epoll 与 io_uring 性能测试

1. 测试说明

学习过 epoll 和 io_uring 好奇那个性能更好一些,所以分别通过 2 种技术实现 tcpserver。通过相同的客户端来测试 100 个连接,访问 100 万次服务器,它们的 qps 分别是多少。

有想自己测试的小伙伴也可以使用下面代码,自己动手测试一下,对于 io_uring 原理不太清楚的可以看我另外一篇文章了解一下。

2. 测试结果

epoll 的测试结果:

io_uring 的测试结果:

3. 总结

通过上面测试结果可以看到,在相同的环境下,io_uring 与 epoll 的 qps 相差不大,epoll 要稍微高一些。当然这并不能说明 io_uring 比 epoll 的性能低,原因可能是本次没有涉及到大量的磁盘 IO 操作,有兴趣的小伙伴可以自己实现测试一下,有时间我也会在尝试一下。

4. 测试代码

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>

#define MAX_EVENTS 10
#define MAX_BUFF_SIZE 1024

int main() {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(2048);
    addr.sin_addr.s_addr = INADDR_ANY;
    if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind");
        close(sockfd);
        return -1;
    }

    listen(sockfd, 10);

    // 创建 epoll 句柄
    int epollfd = epoll_create(MAX_EVENTS);
    if (epollfd == -1) {
        perror("epoll_create");
        close(sockfd);
        return -1;
    }

    // 添加监听套接字 sockfd 到 epoll
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = sockfd;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event) == -1) {
        perror("epoll_ctl");
        close(epollfd);
        close(sockfd);
        return -1;
    }

    struct epoll_event events[MAX_EVENTS];

    char buff[MAX_BUFF_SIZE];

    while (1) {
        // 监听事件
        int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if (nfds == -1) {
            perror("epoll_wait");
            break;
        }

        for (int i = 0; i < nfds; ++i) {
            if (events[i].data.fd == sockfd) {
                // 有新连接
                int clientfd = accept(sockfd, NULL, NULL);
                if (clientfd == -1) {
                    perror("accept");
                    continue;
                }

                // 添加新连接到 epoll
                event.events = EPOLLIN;
                event.data.fd =
<think>好的,用户想要查找关于io_uring的详细用法说明,包括教程示例代码。我需要根据提供的引用内容来组织回答。 首先,我应该先介绍io_uring的基本概念组成部分。根据引用[1][2],io_uring使用三个系统调用:io_uring_setup、io_uring_enterio_uring_register。需要解释每个系统调用的作用,比如io_uring_setup用于设置上下文,分配提交队列(SQ)完成队列(CQ)。这部分是基础,用户需要先了解这些结构才能继续。 接下来,用户可能需要知道具体的使用步骤。根据引用[4],初始化io_uring队列使用io_uring_queue_init,然后打开文件,准备读操作,提交请求,并处理完成队列。需要分步骤讲解,比如初始化、准备操作、提交请求、处理完成事件清理资源。每个步骤对应代码示例中的部分,可以结合代码进行说明。 示例代码部分,用户可能需要一个完整的例子来参考。引用[4]中的示例展示了读取文件的基本流程,包括初始化、打开文件、准备读操作、提交处理完成事件。需要确保代码语法正确,并解释关键函数的作用,比如io_uring_prep_readio_uring_submit。同时,需要注意使用$$...$$格式来展示代码块,符合系统级指令的要求。 优化部分,用户可能关心如何提高性能。根据引用[3],可以使用预注册缓冲区、固定文件句柄SQ轮询模式。这些优化方法能减少系统调用内核开销,提升效率。需要简要说明每个优化的原理实现方式,比如使用io_uring_register来注册缓冲区。 最后,生成相关问题。根据用户可能的疑问,比如性能分析、epoll的对比、错误处理等,列出相关问题,帮助用户进一步学习。 需要注意引用标识的正确使用,比如在提到初始化时引用[2],提到提交队列时引用[3]。保持回答结构清晰,分步骤说明,并确保所有LaTeX格式正确,尤其是行内变量如$O(1)$的时间复杂度。 总结回答结构:概述、核心组件、使用步骤、示例代码、优化方法、注意事项相关问题。确保每个部分都结合引用内容,自然添加引用标识,避免使用Markdown格式,用中文简洁明了地解释。</think>### io_uring 使用教程 #### 一、概述 `io_uring` 是 Linux 5.1 引入的高性能异步 I/O 框架,通过减少系统调用次数用户态/内核态数据拷贝提升效率。其核心组件包括: 1. **提交队列(SQ)**:用户态向内核提交 I/O 请求的队列[^2]; 2. **完成队列(CQ)**:内核返回操作结果的队列; 3. **共享内存**:通过 `mmap` 实现零拷贝通信[^1]。 #### 二、核心步骤 1. **初始化 io_uring 实例** 使用 `io_uring_queue_init` 创建上下文并映射内存: ```c struct io_uring ring; int ret = io_uring_queue_init(ENTRIES, &ring, 0); // ENTRIES为队列深度 ``` 2. **准备 I/O 操作** 通过预定义函数(如 `io_uring_prep_read`)封装请求: ```c struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); // 获取提交队列条目 io_uring_prep_read(sqe, fd, buffer, size, offset); // 封装读请求 ``` 3. **提交请求** 调用 `io_uring_submit` 将请求发送至内核: ```c io_uring_submit(&ring); // 触发系统调用(若未启用SQ轮询) ``` 4. **处理完成事件** 轮询完成队列并处理结果: ```c struct io_uring_cqe *cqe; io_uring_wait_cqe(&ring, &cqe); // 等待完成事件 if (cqe->res > 0) { // 处理数据 } io_uring_cqe_seen(&ring, cqe); // 标记事件已处理 ``` 5. **清理资源** ```c io_uring_queue_exit(&ring); ``` #### 三、示例代码(读取文件) ```c #include <liburing.h> #include <fcntl.h> #define ENTRIES 4 int main() { struct io_uring ring; io_uring_queue_init(ENTRIES, &ring, 0); // 初始化[^4] int fd = open("test.txt", O_RDONLY); char buffer[4096]; struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); io_uring_prep_read(sqe, fd, buffer, sizeof(buffer), 0); // 准备读操作[^3] io_uring_submit(&ring); // 提交请求 struct io_uring_cqe *cqe; io_uring_wait_cqe(&ring, &cqe); // 等待完成 if (cqe->res > 0) { // 处理数据 } io_uring_cqe_seen(&ring, cqe); close(fd); io_uring_queue_exit(&ring); return 0; } ``` #### 四、优化方法 1. **预注册缓冲区** 使用 `io_uring_register` 注册固定缓冲区,减少内存拷贝[^1]: ```c io_uring_register_buffers(&ring, &iov, 1); // 注册缓冲区 ``` 2. **固定文件句柄** 避免重复文件描述符引用计数操作: ```c io_uring_register_files(&ring, &fd, 1); ``` 3. **SQ轮询模式** 启用 `IORING_SETUP_SQPOLL` 减少系统调用[^2]。 #### 五、注意事项 - 错误处理需检查 `cqe->res` 的返回值; - 高并发场景需合理设置队列深度; - 避免频繁内存分配,可复用 `sqe` 缓冲区。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值