智能批处理与空闲策略
别再在应用里对消息做批处理了。Aeron 已经替你做了——做得更好,而且不会增加延迟。
核心原则只有一句话:来了就发,不要等(消息一到就立即发送,不要等待)。每条消息一到达就立即发布。C media driver 会通过 sendmmsg() 把缓冲区里已有的内容合并成更少的系统调用。你既能获得批处理带来的吞吐收益,又无需为等待凑满一个批次而付出延迟代价。
在延迟与 CPU 的取舍中,真正起作用的旋钮是空闲策略(idle strategy)——而不是你的批次大小。
智能批处理 vs 应用层批处理
Section titled “智能批处理 vs 应用层批处理”Aeron 的智能批处理与应用层批处理有着本质区别:
- 不要试图把多条消息聚合到一个 MTU 里——收益是递减的。
- C media driver 会在传输层通过
sendmmsg()自动完成批处理。 - 应用代码只需在消息到达时就将其发布即可。
传输层会自然地把时间上相近到达的消息合并成更少的系统调用。无需任何应用逻辑,更关键的是,不会增加任何等待时间。
批次大小的边际收益递减
Section titled “批次大小的边际收益递减”更大的批次很快就不再有帮助。性能增益曲线是典型的边际收益递减。
- 从 batch=1 提升到 batch=2 会带来显著改善。
- 从 batch=8 提升到 batch=16 几乎没有任何收益。
这正是手写批处理会吃亏的原因。吞吐量的上升空间会迅速趋于平缓,但你为了把批次攒大而扣下的每一条消息,都会直接累加到 p50 和 p99 上。你用确定无疑的延迟代价,换来的只是边际的——最终可忽略不计的——吞吐收益。
空闲策略才是你真正的旋钮
Section titled “空闲策略才是你真正的旋钮”空闲策略控制着线程轮询与让出 CPU 之间的频率。这个选择才是你在延迟与 CPU 之间真正的调节杠杆。
| 策略 | 行为 | 适用场景 |
|---|---|---|
| BusySpin / NoOp | 从不让出 CPU,持续轮询 | 最低延迟,最高 CPU 开销 |
| Sleeping / Yield | 在两次轮询之间让出 CPU | 更低的 CPU 占用,更高的延迟 |
如何结合你的延迟预算来理解这张表:
- BusySpin / NoOp 通过从不在消息到达时陷入休眠,把 p50 和 p99 压到地板上——代价是一个被完全占满的核。请把它留给专用、隔离的核心,在那里 CPU 任你尽情消耗。
- Sleeping / Yield 用延迟换取余量。线程在两次轮询之间把 CPU 交还出去,因此一条消息可能在休眠期间到达并被迫等待——这会抬高 p50,尤其是 p99。当 CPU 是共享或存在争用、且你的尾延迟预算能够吸收这些抖动时,再使用它。
关于 media driver 如何合并发送、以及每种空闲策略的具体实现细节,请参阅 The Aeron Files。