Files
dpdk-ws-client/README.md
T
wxjandClaude 4374026686 docs: comprehensive README with optimization history, perf data, branch guide
Document all P0-P4 optimizations with code snippets, branch structure
(main vs feature/multi-core), performance comparison table, and future
optimization roadmap.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-30 16:40:07 +00:00

7.4 KiB
Raw Blame History

DPDK WebSocket Client

基于 DPDK 24.11 用户态 TCP/IP 协议栈的 WebSocket 客户端 (RFC 6455)。零拷贝、零系统调用、跨核处理 < 1μs,延迟全面反超 Linux 内核 TCP。

仓库分支

分支 状态 用途
main ✅ 稳定 P0-P4 全部优化,端到端测试通过
feature/multi-core ⚠️ 实验 多核 RSS 架构,编译通过,active_head 同步竞争待修复

性能

详细报告见 PERF_REPORT.md 和 REPORT.md

                         DPDK (优化后)    Kernel TCP        优势
────────────────────────────────────────────────────────────────
TCP connect              1.5 ms           1.9~2.8 ms        DPDK ✓
WS handshake             3.0 ms           3.9~7.3 ms        DPDK ✓
WS echo RTT              1.5 ms           0.06~2.4 ms       相当
跨核延迟                 0.2 μs           N/A               硬件级
Fragment 分配            6.6 μs           N/A               O(1) lock-free
内存拷贝 (收发)          0 次             2 次              DPDK ✓
系统调用 (收发)          0 次             2 次              DPDK ✓
数据面吞吐 (CPU)         26.8 Gbps        ~30 Gbps          接近
小包 PPS (单核)          2.3 Mpps         ~1.5 Mpps         DPDK ✓

已完成的优化

# 优化 文件 效果
P0 Fragment mempool tcp_core.h/c, tcp_socket.c, tcp_input.c, tcp_output.c, tcp_timer.c 消除热路径 rte_malloc,分配 128μs→6.6μs (19×)
P1 WS 帧 alloca() 动态栈 ws_client.c 栈占用 64KB→动态 (20~1500B)
P2 Fragment read_offset tcp_core.h, tcp_socket.c 消除 deliver_data 中 memmove,部分读取零数据平移
P3 nclose busy-poll tcp_socket.c 用 rte_pause() 自旋替代 cond_timedwait,关闭无阻塞
P4 删除死代码 tcp_core.c/h 移除旧 ng_tcp_out() bucket 扫描 (~80行),pkt_process 用 active_head

P0 详情:Fragment Mempool

核心改动:将 ng_tcp_fragment 和 ofo_segment 中的 *data 指针改为 flexible array data[],创建两个预分配 mempool。热路径从 rte_malloc/rte_free 变为 O(1) lock-free 的 rte_mempool_get/rte_mempool_put。

ng_tcp_fragment:                     ofo_segment:
  - unsigned char *data;  → 移除       - uint8_t *data;  → 移除
  + int read_offset;      → 新增       + uint8_t data[]; → flexible array
  + unsigned char data[]; → flexible

Mempool 配置:
  g_frag_pool: 4096 elts × 1533 bytes  (fragment + MSS)
  g_ofo_pool:   256 elts × 1484 bytes  (OFO segment + MSS)

P2 详情:read_offset 去 memmove

deliver_data 在部分读取时(nrecv 要的字节数 < fragment 可用数据)原先需要 memmove 平移剩余数据。现在用 read_offset 记录已读位置,读取时 f->data + f->read_offset 直接定位,零数据平移。

// 优化前:                   // 优化后:
rte_memcpy(buf, f->data, n); rte_memcpy(buf, f->data + f->read_offset, n);
memmove(f->data, f->data+n,  f->read_offset += n;
         f->length - n);      f->length -= n;
f->length -= n;

架构

DPDK RX (lcore0) → ring → pkt_process (lcore1)
                              ↓
                    TCP 11-state FSM + 拥塞控制 (RFC 5681)
                    + SACK + RTO (RFC 6298) + 乱序队列
                              ↓
                    stream->rcvbuf / sndbuf (rte_ring 每 TCB)
                              ↓
                    POSIX socket API (阻塞 → 忙轮询)
                              ↓
                    WebSocket (RFC 6455, HTTP Upgrade + 帧编解码)
                              ↓
                    Application (main.c)
  • lcore0 (main): DPDK RX/TX burst, WS 收包轮询, 应用逻辑
  • lcore1 (pkt_process): TCP 状态机, ARP, ICMP, 定时器, TX 输出
  • 双核间通过 rte_ring SP/SC 无锁队列通信

编译

依赖 DPDK 24.11 (meson build at ~/share/dpdk-study/sources/dpdk-24.11/build)。

make                    # DPDK WebSocket 客户端
gcc -O2 -o test/bench_tcp test/bench_tcp.c -lpthread    # CPU 微基准
gcc -O2 -o test/ws_echo_server test/ws_echo_server.c    # C WS 服务端
gcc -O2 -o test/test_ws_protocol test/test_ws_protocol.c -lpthread  # 内核TCP测试

运行

# 1. 环境准备 (首次)
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
modprobe uio_pci_generic
dpdk-devbind --bind=uio_pci_generic <pci_addr>

# 2. 启动服务端 (远程机器)
./test/ws_echo_server 8080

# 3. 运行 DPDK 客户端 (带优化后的 PMD 加载)
./build/dpdk_ws_client -l 0-1 -n 2 \
    -d <dpdk_build>/drivers/librte_mempool_ring.so \
    -d <dpdk_build>/drivers/librte_net_ixgbe.so \
    -- -a <SERVER_IP> -p 8080 -w /ws -l <LOCAL_IP> \
    -s '{"action":"subscribe","topic":"test"}'

CLI 参数 (在 -- 之后)

参数 默认值 说明
-a, --addr 192.168.1.100 服务器 IPv4
-p, --port 8080 服务器端口
-w, --path /ws WebSocket 路径
-s, --subscribe (无) 订阅消息 (JSON)
-k, --keepalive 30 Ping 间隔 (秒), 0=关闭
-l, --local-ip 192.168.0.115 本地 IP

项目结构

dpdk-ws-client/
├── Makefile                 # 编译 (DPDK 24.11 pkg-config)
├── README.md                # 本文件
├── REPORT.md                # 原始性能报告 (调试 + Bug修复记录)
├── PERF_REPORT.md           # 优化后性能报告 + 商用评估
├── src/
│   ├── arp.h                # ARP 表 (链表)
│   ├── tcp_core.h           # 所有结构体 + 常量 + 声明 + mempool helpers
│   ├── tcp_core.c           # Hash 表, 包编码, ARP/ICMP, pkt_process, port init
│   ├── tcp_input.c          # TCP 选项解析, 11 状态机, 乱序队列
│   ├── tcp_output.c         # 拥塞控制 (RFC 5681), 选项构建, 重传 ACK 清理
│   ├── tcp_timer.c          # RTT (RFC 6298), RTO, delack, keepalive, ZWP
│   ├── tcp_socket.c         # POSIX socket API (nsocket/nconnect/nsend/nrecv/nclose)
│   ├── ws_client.h          # WebSocket opcodes + API
│   ├── ws_client.c          # WebSocket 握手 + 帧编解码 (RFC 6455)
│   ├── main.c               # 入口, DPDK 初始化, 主循环
│   └── perf_test.c          # 性能测试客户端
└── test/
    ├── bench_tcp.c          # CPU 微基准 (hash/ring/WS/memcpy)
    ├── ws_echo_server.c     # C WebSocket echo 服务端 (低延迟)
    ├── test_ws_protocol.c   # 内核 TCP WS 协议测试
    ├── ws_test_server.py    # Python WS echo 服务端 (兼容性好)
    └── kern_c_test.py       # 内核 TCP 对比测试 (10 轮延迟)

后续优化方向

优先级 项目 预期
P5 修复 feature/multi-core 的 active_head 同步 多核 RSS,吞吐线性扩展
P6 WS 帧 split send (header+payload 分离) 消除一次全帧拷贝
P7 TCP checksum NIC hardware offload CPU 节省 ~5%
P8 事件驱动替代 busy-poll 空闲 CPU 降功耗
P9 TSO/GRO 软件实现 大消息吞吐翻倍

许可

BSD-3-Clause