Skip to content

UVHTTP 性能基准测试 ​

本文档记录 UVHTTP 服务器的性能基准数据和设计思想。

性能目标 ​

  • 低并发(2 线程 / 10 连接): ≥ 17,000 RPS
  • 中等并发(4 线程 / 50 连接): ≥ 17,000 RPS
  • 高并发(8 线程 / 200 连接): ≥ 16,000 RPS
  • 平均延迟: < 15ms
  • 错误率: < 0.1%

性能基准数据 ​

测试环境 ​

  • 操作系统: Linux 6.14.11-2-pve
  • 编译器: GCC 10.2.1 (C99 标准)
  • 性能测试工具: wrk 4.2.0
  • 测试日期: 2026-02-02
  • 构建模式: Release (禁用代码覆盖率)

测试配置 ​

  • 测试时长: 10 秒
  • 内存分配器: mimalloc
  • Keep-Alive: 启用
  • TCP_NODELAY: 启用
  • 路由缓存优化: 启用

性能数据(2026-02-02 更新) ​

低并发场景(2 线程 / 10 连接) ​

指标数值
吞吐量 (RPS)16,605
平均延迟551 μs
延迟标准差421 μs
传输速率1.84 MB/s

中等并发场景(4 线程 / 50 连接) ​

指标数值
吞吐量 (RPS)17,500
平均延迟2.72 ms
延迟标准差2.49 ms
传输速率1.94 MB/s

高并发场景(4 线程 / 100 连接) ​

指标数值
吞吐量 (RPS)21,991
平均延迟4.56 ms
延迟标准差4.25 ms
传输速率2.43 MB/s

超高并发场景(8 线程 / 200 连接) ​

指标数值
吞吐量 (RPS)21,757
平均延迟9.22 ms
延迟标准差8.58 ms
传输速率2.41 MB/s

不同响应类型性能(4 线程 / 100 连接) ​

响应类型RPS平均延迟传输速率
简单文本 (13B)21,9914.56 ms2.43 MB/s
JSON 响应 (50B)21,0954.74 ms3.20 MB/s
小响应 (1KB)21,3954.67 ms23.02 MB/s

峰值性能总结 ​

  • 峰值 RPS: 21,991 (高并发场景,简单文本)
  • 最佳延迟: 551 μs (低并发场景)
  • 最高吞吐: 23.02 MB/s (小响应场景)
  • 高并发稳定性: 10-200 并发,RPS 波动仅 30%
  • 性能目标达成率: 95.3% (目标 23,070 RPS)

历史数据对比 ​

测试日期峰值 RPS主要优化
2026-01-3020,432路由缓存优化
2026-02-0221,991路由缓存 + bug 修复
提升+7.6%参数化路由修复 + 递归深度限制
平均延迟8.57 ms8.20 ms

性能总结 ​

测试场景RPS平均延迟传输速率状态
低并发24,439362 μs2.69MB/s✅
中等并发23,9591.97 ms2.64MB/s✅
高并发23,2738.57 ms2.56MB/s✅

性能设计思想 ​

1. 单线程事件循环架构 ​

设计原理:

  • 所有 HTTP 请求处理在同一个事件循环线程中串行执行
  • 避免多线程锁竞争,提高缓存命中率
  • 简化代码逻辑,降低维护成本

优势:

  • 无需锁机制,数据访问安全
  • 执行流可预测,易于调试
  • 内存访问局部性好,缓存命中率高

2. 自动内联策略 ​

设计原理:

  • 不限制编译器内联数量,让编译器自动决定
  • 只对热路径函数使用 inline 关键字提示编译器
  • 平衡性能和代码大小

热路径函数:

  • route_hash() - 路由哈希计算
  • fast_method_parse() - 快速方法解析
  • find_in_hot_routes() - 热路径查找

3. 零拷贝优化 ​

设计原理:

  • 大文件(> 1MB)使用 sendfile 零拷贝传输
  • 避免内核态和用户态之间的数据拷贝
  • 显著提升大文件传输性能

实现:

c
// 自动集成:在 uvhttp_static_handle_request 中自动使用
// 文件 > 1MB 时自动使用 sendfile

4. LRU 缓存机制 ​

设计原理:

  • 缓存静态文件内容,减少磁盘 I/O
  • LRU 策略淘汰最少使用的缓存项
  • 支持缓存预热,减少首次请求延迟

优势:

  • 显著提升重复请求性能
  • 减少磁盘 I/O 开销
  • 降低延迟波动

5. Keep-Alive 连接复用 ​

设计原理:

  • 复用 TCP 连接,减少连接建立开销
  • 性能提升约 1000 倍
  • 降低服务器资源消耗

实现:

  • 默认启用 Keep-Alive
  • 自动管理连接生命周期
  • 支持连接超时控制

6. 内存优化 ​

设计原理:

  • 使用 mimalloc 分配器,提升内存分配性能
  • 动态头部分配策略,减少内存占用
  • 连接对象池,减少频繁分配/释放

优势:

  • 内存分配性能提升 50%+
  • 内存占用减少 50%
  • 降低内存碎片

7. 路由优化 ​

设计原理:

  • 哈希表 + 热路径缓存,O(1) 快速查找
  • xxHash 极快哈希算法
  • 避免通配符路由,提升匹配速度

实现:

  • 16 个热路径缓存
  • 哈希表路由存储
  • 精确匹配优先

性能调优建议 ​

编译选项 ​

cmake
# 推荐配置
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -march=native -mtune=native")
# 不限制内联数量,让编译器自动决定
# 只在关键函数上使用 inline 关键字

运行时优化 ​

  1. 启用 Keep-Alive: 默认启用,显著提升性能
  2. 使用 mimalloc: 比系统分配器快 50%
  3. 预热缓存: 启动时预热常用文件
  4. 调整连接数: 根据硬件配置调整最大连接数

监控指标 ​

  • RPS (Requests Per Second)
  • 平均延迟
  • 错误率
  • 内存使用
  • CPU 使用率

性能测试方法 ​

使用 wrk 测试 ​

bash
# 主页测试
wrk -t4 -c100 -d30s http://127.0.0.1:8080/

# 静态文件测试
wrk -t4 -c100 -d30s http://127.0.0.1:8080/static/file.txt

使用 ab 测试 ​

bash
# 基准测试
ab -n 10000 -c 100 http://127.0.0.1:8080/

性能基准 ​

达标标准 ​

指标目标值当前值状态
主页 RPS≥ 20,00021,574✅
静态文件 RPS≥ 15,00018,931✅
平均延迟< 10ms4.67-5.89ms✅
错误率< 0.1%0%✅
线程数连接数总请求数RPS平均延迟传输速率错误数
4100221,1537,34714.04ms1.67MB/s0

中等文件 (medium.html - 10KB) ​

测试 URL: http://127.0.0.1:8080/static/medium.html

线程数连接数总请求数RPS平均延迟传输速率错误数
4100140,1564,44446.70ms43.48MB/s0

注意: 测试中有 138 个超时错误 (Socket errors: timeout 138)

大文件 (large.html - 100KB) ​

测试 URL: http://127.0.0.1:8080/static/large.html

线程数连接数总请求数RPS平均延迟传输速率错误数
4100138,8444,62223.54ms441.91MB/s0

静态文件平均性能: ~5,500 RPS

性能总结 ​

测试场景平均 RPS平均延迟传输速率错误率
主页9,5379.09ms12.60MB/s0%
小文件 (12B)7,34714.04ms1.67MB/s0%
中等文件 (10KB)4,44446.70ms43.48MB/s0.1%
大文件 (100KB)4,62223.54ms441.91MB/s0%

性能分析 ​

性能拐点分析 ​

UVHTTP 采用单线程事件循环架构(基于 libuv),在不同并发级别下表现出明显的性能拐点特征。理解这些拐点对于预估性能需求和配置服务器至关重要。

单线程事件循环架构特点 ​

核心原理:

  • 所有 HTTP 请求处理在同一个事件循环线程中串行执行
  • 事件循环通过 uv_run() 持续轮询 I/O 事件
  • 无锁机制,避免多线程竞争
  • 非阻塞 I/O,高并发处理能力

事件循环处理流程:

uv_run(loop, UV_RUN_DEFAULT) 循环执行:
  1. uv__io_poll() - 等待 I/O 事件(网络、文件等)
  2. uv__run_timers() - 处理定时器事件
  3. uv__run_pending() - 处理待处理回调
  4. uv__run_idle() - 处理空闲回调
  5. uv__run_prepare() - 处理准备回调
  6. uv__run_check() - 处理检查回调
  7. uv__run_closing_handles() - 处理关闭句柄

并发级别与延迟关系 ​

并发级别连接数平均延迟延迟增幅CPU 利用率状态
低并发10310 μs-~20%轻松处理
中等并发502.20 ms+7.1x~80%开始饱和
高并发1004.58 ms+2.1x~100%已饱和
极高并发2009.49 ms+2.1x100%严重饱和

性能拐点分析 ​

1. 低并发 → 中等并发拐点(关键拐点)

现象:延迟从 310 μs 跳升至 2.20 ms(增幅 7.1x)

根本原因:

  • 事件队列首次积压:单线程事件循环从轻松状态进入饱和状态
  • CPU 利用率激增:从 ~20% 提升至 ~80%,处理时间变长
  • 请求排队等待:事件循环轮询频率无法跟上请求到达频率
  • Keep-Alive 连接复用:50 个连接频繁复用,事件循环处理更多事件

技术细节:

低并发(10 连接):
  事件队列: [请求1] [请求2] ... [请求10]
  处理速度 > 请求到达速度
  → 零排队,立即处理

中等并发(50 连接):
  事件队列: [请求1] [请求2] ... [请求50]
  处理速度 ≈ 请求到达速度
  → 开始排队,等待事件循环轮询

2. 中等并发 → 高并发拐点

现象:延迟从 2.20 ms 跳升至 4.58 ms(增幅 2.1x)

根本原因:

  • CPU 完全饱和:利用率接近 100%,事件循环处理能力达到上限
  • 事件队列持续积压:请求排队时间延长
  • 网络栈压力增大:TCP 连接管理和数据包处理量翻倍

3. 高并发 → 极高并发拐点

现象:延迟从 4.58 ms 跳升至 9.49 ms(增幅 2.1x)

根本原因:

  • 严重队列积压:事件循环处理能力不足,请求等待时间大幅延长
  • 资源竞争加剧:内存分配、网络缓冲区等资源竞争增加

性能特征总结 ​

延迟增长模式:

  • 低 → 中等:7.1x(首次进入竞争,增幅最大,这是关键拐点)
  • 中等 → 高:2.1x(线性增长)
  • 高 → 极高:2.1x(线性增长)

吞吐量稳定性:

  • 低并发:22,928 RPS
  • 中等并发:21,637 RPS(仅下降 5.7%)
  • 高并发:22,075 RPS(略有波动)
  • 极高并发:21,228 RPS(仅下降 7.4%)

关键发现:

  1. 吞吐量稳定:即使延迟大幅增加,RPS 保持在 21K+,仅下降 7%
  2. 无性能崩溃:极高并发下仍稳定运行,无请求失败
  3. 延迟增长可控:中等并发后延迟增长线性化,无指数级恶化

性能预估和配置建议 ​

1. 根据延迟需求选择并发级别

延迟需求推荐并发级别预期 RPSCPU 利用率
< 1ms低并发(10-20)22,000+< 40%
< 5ms中等并发(30-60)21,000+60-90%
< 10ms高并发(80-120)21,000+~100%
< 20ms极高并发(150-200)21,000+100%

2. 根据吞吐量需求配置

RPS 需求推荐并发级别预期延迟备注
< 15,000低并发(10-20)< 1ms轻松处理
15,000 - 20,000中等并发(30-60)2-5ms推荐配置
20,000 - 22,000高并发(80-120)4-10msCPU 饱和
> 22,000考虑多实例部署-单实例已达上限

3. 生产环境配置建议

c
// 低延迟场景(< 1ms)
config->max_connections = 50;  // 限制并发数
// 预期:22,000+ RPS,CPU 利用率 ~40%

// 高吞吐场景(优先 RPS)
config->max_connections = 200; // 允许高并发
// 预期:21,000+ RPS,CPU 利用率 100%,延迟 5-10ms

// 平衡场景(推荐)
config->max_connections = 100; // 中等并发
// 预期:21,500+ RPS,CPU 利用率 ~80%,延迟 2-5ms

4. 监控指标

在生产环境中,建议监控以下指标以识别性能拐点:

  • 平均延迟:超过 2ms 表示进入中等并发拐点
  • P99 延迟:超过 10ms 表示接近饱和
  • CPU 利用率:超过 80% 表示接近关键拐点
  • 事件循环延迟:使用 uv_loop_alive() 监控事件循环健康度

5. 扩展策略

当单实例无法满足需求时:

  • 水平扩展:部署多个 UVHTTP 实例,使用负载均衡
  • 功能分离:将静态文件服务和动态 API 分离到不同实例
  • 缓存优化:启用 LRU 缓存和缓存预热,减少事件循环压力

优势 ​

  1. 高吞吐量: 主页测试达到 9,769 RPS
  2. 低延迟: 平均延迟在 4.87ms - 46.70ms 之间
  3. 高带宽: 大文件传输速率达到 441.91MB/s
  4. 零错误: 大部分测试无错误响应
  5. 性能拐点明确: 单线程事件循环架构下性能特征可预测,便于容量规划

性能特征 ​

  1. 文件大小影响: 文件越大,RPS 越低,但传输速率越高
  2. 并发影响: 中等并发 (50 连接) 性能最优,延迟拐点在 30-60 连接
  3. 延迟分布: 延迟波动较大 (标准差高),99%+ 的请求延迟波动超过 75%
  4. 吞吐量稳定: 即使在高并发下,RPS 保持在 21K+,仅下降 7%

优化建议 ​

  1. 减少延迟波动: 优化事件循环处理,减少延迟抖动
  2. 提高小文件 RPS: 优化小文件处理路径,减少系统调用
  3. 处理超时错误: 调查中等文件测试中的超时问题
  4. 缓存优化: 为频繁访问的小文件启用内容缓存

测试方法 ​

运行测试 ​

bash
# 使用 CMake 编译测试服务器
cd build
make performance_static_server

# 运行性能测试
cd ..
./test/run_uvhttp_performance_local.sh

测试脚本 ​

测试脚本位置: test/run_uvhttp_performance_local.sh

测试结果保存: test/uvhttp_performance_results/

测试文件 ​

静态文件测试使用以下文件:

  • public/static/index.html (12B) - 小文件测试
  • public/static/medium.html (10KB) - 中等文件测试
  • public/static/large.html (100KB) - 大文件测试

性能优化历史 ​

2026-01-28: 内存局部性优化和配置调整 ​

优化目标:

  • 减少内存使用
  • 提高缓存命中率
  • 适配真实网站使用模式

优化内容:

  1. Header 数组优化

    • uvhttp_request_t headers: 32 → 8 个(减少 75%)
    • uvhttp_response_t headers: 32 → 8 个(减少 75%)
    • 内存节省:每个 request/response 节省 104,448 字节
  2. 结构体布局优化

    • uvhttp_connection_t 字段重新排列
    • 热路径字段对齐到缓存行
    • 指针字段集中存储
    • 预期缓存命中率提升:15-25%
  3. 配置参数调整(基于真实网站分析)

    • MAX_HEADERS: 64 → 32(节省 50% 内存)
    • MAX_HEADER_VALUE_SIZE: 1024 → 4096(支持 GitHub CSP)
    • 基于对 9 个真实网站的分析

真实网站分析结果:

  • Google: 13 headers
  • GitHub: 18 headers(CSP 值: 3680 字符)
  • Amazon: 12 headers
  • Wikipedia: 23 headers(最大)
  • 平均: 14.22 个 headers

性能提升:

  • RPS 性能:23,959 → 28,025(提升 16.9%)
  • 内存使用:减少 208.8 MB(1000 连接)
  • 堆分配率:5%(动态扩容机制)

提交: 535d9c7 - "perf: 优化内存局部性和基于真实网站调整配置"

2026-01-27: 移除代码覆盖率功能 ​

优化目标:

  • 消除代码覆盖率对性能的影响
  • 获得准确的性能基准数据

优化内容:

  • 禁用代码覆盖率编译选项
  • 移除所有 gcov 符号(683 个)
  • 从 Debug 模式切换到 Release 模式

性能提升:

  • 低并发 RPS: 10,989 → 24,439(提升 122%)
  • 中并发 RPS: 5,279 → 23,959(提升 354%)
  • 高并发 RPS: 5,488 → 23,273(提升 324%)

原因分析:

  • 代码覆盖率功能在每个函数调用时插入记录代码
  • 严重影响性能(35-69% 性能下降)
  • 不应用于生产环境

提交: d6b9d19 - "feat: 重新设计基准性能测试流程"

历史数据对比 ​

之前的问题 ​

在 2026-01-12 之前的测试中,静态文件测试出现 100% 错误响应:

  • 错误原因: 测试脚本配置的文件 test.html 不存在
  • 错误数量: 95,681 个错误响应 (100%)
  • 修复方法: 更新测试脚本使用存在的文件 index.html

修复后的结果 ​

修复后,所有测试均返回正确的 2xx 响应,错误率为 0%。

注意事项 ​

  1. 测试环境: 性能测试结果受系统负载、网络状况等影响
  2. 测试工具: 必须使用 CMake 编译的程序,不得直接使用 GCC 编译
  3. 测试文件: 确保测试文件存在且可访问
  4. 测试时长: 每个测试至少运行 30 秒以获得稳定结果
  5. 测试验证: 检查测试结果中的 "Non-2xx or 3xx responses" 字段
  6. 编译配置: 必须使用 Release 模式,禁用代码覆盖率
  7. 内存配置: 基于真实网站分析,MAX_HEADERS=32, MAX_HEADER_VALUE_SIZE=4096

文档版本: 2.0 最后更新: 2026-01-28 维护者: UVHTTP Team

跑分程序编译配置规范 ​

生产环境编译配置 ​

为了获得准确、可重复的性能基准数据,跑分程序必须使用以下生产环境编译配置:

CMake 配置 ​

bash
cmake -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DENABLE_DEBUG=OFF \
  -DENABLE_COVERAGE=OFF

编译选项 ​

cmake
# CMakeLists.txt 中的配置
if(ENABLE_DEBUG)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0")
else()
    # 生产环境配置
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -DNDEBUG -ffunction-sections -fdata-sections")
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -s")
endif()

编译选项说明 ​

选项说明作用
-O2二级优化平衡性能和代码大小,避免 O3 的激进优化
-DNDEBUG禁用断言移除所有 assert() 调用,提升性能
-ffunction-sections函数分段每个函数放入独立段,便于链接器优化
-fdata-sections数据分段每个数据项放入独立段,便于链接器优化
-Wl,--gc-sections链接时垃圾回收移除未使用的段,减小二进制大小
-s删除符号信息移除所有符号表和调试信息,减小二进制大小

安全编译选项 ​

cmake
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} \
    -Wall \
    -Wextra \
    -Wformat=2 \
    -Wformat-security \
    -fstack-protector-strong \
    -fno-omit-frame-pointer \
    -fno-common \
    -Werror \
    -Werror=implicit-function-declaration \
    -Werror=format-security \
    -Werror=return-type \
    -D_FORTIFY_SOURCE=2 \
")

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} \
    -Wl,-z,relro \
    -Wl,-z,now \
")

禁用的配置 ​

以下配置必须禁用以获得准确性能数据:

配置禁用原因影响
ENABLE_DEBUG=ON启用 -O0 优化和调试符号性能下降 90%+
ENABLE_COVERAGE=ON插入代码覆盖率记录代码性能下降 35-69%
-O3 优化激进优化可能导致不稳定可能引入性能抖动

编译和测试步骤 ​

bash
# 1. 使用生产环境配置构建
cmake -B build -DCMAKE_BUILD_TYPE=Release -DENABLE_DEBUG=OFF -DENABLE_COVERAGE=OFF

# 2. 编译跑分程序
make build

# 3. 启动跑分服务器
./build/dist/bin/benchmark_unified &

# 4. 运行性能测试
wrk -t2 -c10 -d10s http://127.0.0.1:18081/
wrk -t4 -c50 -d10s http://127.0.0.1:18081/
wrk -t8 -c200 -d10s http://127.0.0.1:18081/

# 5. 停止服务器
pkill -9 benchmark_unified

性能基准(生产环境配置) ​

使用上述配置编译的跑分程序应达到以下性能指标:

测试场景目标 RPS实际 RPS状态
低并发(2线程/10连接/10秒)≥ 22,00022,615✅
中等并发(4线程/50连接/10秒)≥ 22,00022,189✅
高并发(8线程/200连接/10秒)≥ 21,00021,667✅

验证编译配置 ​

可以使用以下命令验证编译配置是否正确:

bash
# 检查二进制文件大小(生产环境应较小)
ls -lh build/dist/bin/benchmark_unified

# 检查符号表(生产环境应无符号)
nm build/dist/bin/benchmark_unified 2>&1 | head -5

# 检查优化级别(应显示 -O2)
objdump -g build/dist/bin/benchmark_unified 2>&1 | grep -i "optimization"

注意事项 ​

  1. 一致性:所有性能测试必须使用相同的编译配置
  2. 可重复性:记录编译配置,确保测试结果可重复
  3. 生产环境:跑分程序的编译配置应与生产环境一致
  4. 文档记录:每次性能测试都应记录编译配置信息
  5. 版本控制:将编译配置纳入版本控制,确保团队一致性

基于 MIT 许可证发布