第二部分 · 6 / 7
性能选项
rdmanet 用函数选项模式调优,把 Option 传给
Dial / Listen / ListenPacket。这一节过一遍每个选项的含义、默认值和何时该动它。
全部选项
| Option | 作用 | 默认 |
|---|---|---|
WithDevice("mlx5_3") | 按名字选网卡(CPU 网 vs GPU 网) | 第一块可用 |
WithPort(n) | HCA 端口号(1 起) | 1 |
WithGIDIndex(n) | GID 表索引(RoCE v2) | 0(示例常用 3) |
WithQueueDepth(n) | 队列深度(同时在飞的 WR 数) | 128 |
WithBufferSize(n) | 每个 bounce buffer 槽大小(字节) | 64 KiB |
WithHandshake() | 改用 TCP 带外握手 | 关(用 rdma_cm) |
WithPollMode(...) | CQ 排空方式 | PollEvent |
conn, _ := rdmanet.Dial(addr,
rdmanet.WithDevice("mlx5_1"),
rdmanet.WithGIDIndex(3),
rdmanet.WithQueueDepth(256),
rdmanet.WithPollMode(rdmanet.PollBusy),
)
轮询模式:延迟 vs CPU
这是最影响性能特征的旋钮,本质是一道权衡题:
| 模式 | 做法 | 特点 |
|---|---|---|
PollEvent默认 | 等完成通道事件(ibv_get_cq_event),被唤醒后批量收割 | CPU 占用低,略有延迟。通用场景首选 |
PollBusy | 专用 goroutine 忙轮询 CQ | 延迟最低,吃满一个核。延迟敏感场景 |
为什么默认 PollEvent
事件驱动是对 CPU 友好的通用默认值;忙轮询是延迟敏感用户的显式选择。
PollEvent 是零值(iota 0),也是文档约定的默认。
队列深度与缓冲区大小
WithQueueDepth:允许同时在飞的工作请求数。调大能提升流水线深度、利好高带宽,但占用更多资源。WithBufferSize:每个注册槽的大小。消息普遍偏大时调大可减少分片;偏小时调小省内存。
批量 I/O
每次 post/收割都有固定开销,批量 API 把若干消息在一把锁下一起处理,摊薄开销, 在小消息高频场景显著提升吞吐:
conn.SendBatch(msgs) // 一次发多条
msgs, _ := conn.RecvBatch(32) // 一次最多收 32 条
// UD 侧对应 WriteToBatch / ReadFromBatch
零拷贝发送
默认 SendMsg 会把你的数据拷进库维护的 bounce buffer 再发。
如果你能从一块预注册的缓冲区直接构造数据,就能省掉这次拷贝:
buf, _ := conn.AllocBuffer(4096) // 调用方拥有的预注册 Buffer
// ……往 buf 里写数据……
conn.SendBuffer(buf) // 直接从 buf 发,无 bounce 拷贝
UD 地址注册表
UD 无连接,发送前要知道对端 Addr。Registry 是个轻量的「名字→Addr」目录,
用来带外发现对端(也可以用任意手段分发 Addr.String()):
reg := rdmanet.NewRegistry()
pc.Register(reg, "node-a") // 登记自己
addr, _ := reg.LookupAddr("node-b") // 查别人
下一步
最后一节讲一个工程上的巧思:跨平台 stub——为什么没有网卡也能编译和测试。
gordma 教程