NeFut Logo NeFut
EN 管理员登录

[C++黑魔法] 从1.5万到12.5万请求每秒:我的epoll HTTP服务器优化之旅

发布于:2026-06-05 07:20 最后更新:2026-06-06 13:04
#algorithm #optimization #C++

大家好,作为一名网络编程新手,我在一个月前决定从零开始构建一个基于epoll的HTTP服务器,并在此过程中对每个主要架构变化进行了基准测试。

性能基准

基准命令:

wrk -t4 -c10000 -d10s http://127.0.0.1:8080/

请求:GET /index.html
响应:静态HTML文件(约1500字节)
CPU:Intel i5-13420H(第13代)
编译器:Clang(O3)

架构 吞吐量 (req/sec) 描述
Blocking ~15k 单线程阻塞接受/读取/写入
Epoll (LT) ~34k 单线程事件循环使用非阻塞I/O多路复用
Epoll (LT, keep alive) ~37.5k 单线程事件循环,持久连接
Epoll (LT, keep alive, sendfile) ~41k 单线程事件循环,持久连接与零拷贝文件服务
Epoll (LT, keep alive, sendfile, multithreading) ~125k 多线程架构,运行4个并发epoll循环(在测试机器上最优)

一些令人惊讶的观察

sendfile的效果比我预期的要小……对于一个专门用来服务文件的服务器,我原本期待更大的增益,但可能因为我的文件仅有约1.5KB,所以没有太大帮助。

更多线程反而让性能变差:

工作线程 吞吐量 (req/sec)
1 ~40k
2 ~95k
3 ~115k
4 ~125k
5 ~90k
6 ~90k
8 ~75k
10 ~70k
12 ~65k

我的CPU有6个物理核心和12个逻辑处理器,我怀疑在较高线程数下,所有循环的系统调用成本、上下文切换和共享内核对象上的锁竞争占据了主导地位,尽管我还没有完全调查清楚。

使用perf进行分析

函数 近似CPU样本
readSock() ~22%
writeSock() ~16%
parse() ~8%
std::format() ~7%
open() ~3%
sendfile() ~2.5%

结果发现,我花在读取和解析请求上的时间仍然比发送响应的时间要多,这意味着未来可能还可以尝试批量读取或缓冲池。

最后思考

我可以继续寻找微优化的可能性,甚至实验边缘触发架构,但此时我有些疲惫,这个项目在此结束似乎是个不错的选择。代码库相对较小(约1k LOC),如果有人感兴趣,可以查看: GitHub 代码库

博主点评: 本文展示了基于epoll的HTTP服务器优化过程中的关键技术挑战与解决方案,尤其是对多线程性能的深入分析,值得开发者们借鉴。通过对性能瓶颈的细致剖析,可以为未来的优化方向提供重要思路。

原文链接: https://www.reddit.com/r/cpp/comments/1twh4e1/i_spent_a_month_optimizing_my_epoll_based_http/

[h] 返回首页