【发布时间】:2012-08-23 20:26:37
【问题描述】:
我正在 Linux over loopback (127.0.0.1) 上使用优化的 Java NIO 选择器进行一些基准测试。
我的测试很简单:
- 一个程序将 UDP 数据包发送到另一个程序,该程序将其回显给发送者,并计算往返时间。下一个数据包仅在前一个数据包被确认(返回时)时发送。在执行基准测试之前,会使用数百万条消息进行适当的预热。该消息有 13 个字节(不包括 UDP 标头)。
对于往返时间,我得到以下结果:
- 最短时间:13 微秒
- 平均时间:19 微秒
- 75% 百分位数:18,567 纳秒
- 90% 百分位数:18,789 纳秒
- 99% 百分位数:19,184 纳秒
- 99.9% 百分位数:19,264 纳秒
- 99.99% 百分位数:19,310 毫微
- 99.999% 百分位数:19,322 纳秒
但这里的问题是我正在旋转 100 万条消息。
如果我只旋转 10 条消息,我会得到截然不同的结果:
- 最短时间:41 微秒
- 平均时间:160 微秒
- 75% 百分位:150,701 纳秒
- 90% 百分位数:155,274 毫微
- 99% 百分位数:159,995 纳秒
- 99.9% 百分位数:159,995 纳秒
- 99.99% 百分位数:159,995 纳秒
- 99.999% 百分位数:159,995 纳秒
如果我错了,请纠正我,但我怀疑一旦我们让 NIO 选择器旋转,响应时间就会变得最佳。但是,如果我们发送消息之间的间隔足够大,我们就要付出唤醒选择器的代价。
如果我只发送一条消息,我会收到 150 到 250 微秒之间的不同时间。
所以我对社区的问题是:
1 - 对于此往返数据包测试,我的最短时间为 13 微秒,平均为 19 微秒。到目前为止,我似乎击败了ZeroMQ,所以我可能在这里遗漏了一些东西。从这个基准来看,ZeroMQ 在标准内核上的平均时间为 49 微秒(99% 百分位数)=> http://www.zeromq.org/results:rt-tests-v031
2 - 当我旋转单个或很少的消息时,我可以做些什么来改善选择器的反应时间? 150微不好看。或者我应该假设在 prod 环境中选择器不会完全正确?
通过忙于在 selectNow() 周围旋转,我可以获得更好的结果。发送少量数据包仍然比发送很多数据包更糟糕,但我认为我现在达到了选择器性能限制。我的结果:
- 发送单个数据包我得到一致的 65 微秒往返时间。
- 发送两个数据包平均往返时间约为 39 微秒。
- 发送 10 个数据包,平均往返时间约为 17 微秒。
- 发送 10,000 个数据包,平均往返时间约为 10,098 纳秒。
- 发送 100 万个数据包,平均往返时间为 9,977 纳秒。
结论
所以看起来 UDP 数据包往返的物理障碍平均为 10 微秒,尽管我有一些数据包在 8 微秒(最短时间)内完成了行程。
在忙碌的旋转中(感谢 Peter),我能够从平均 200 微秒增加到单个数据包的平均 65 微秒。
不知道为什么 ZeroMQ 是 5 times slower。 (编辑:可能是因为我通过环回在同一台机器上测试这个,而 ZeroMQ 使用的是两台不同的机器?)
【问题讨论】:
-
我认为这主要是由于 HotSpot JVM 预热时间而不是选择器的具体行为。
-
感谢@EJP,但我确实在 -server 模式下对 JVM 做了一些热身。在发送将触发基准测试的消息之前,我发送了几百万条消息。你为什么认为会发生这种情况=>“如果我只发送一条消息,我会收到 150 到 250 微秒之间的不同时间。”
-
叫我疯了,但你为什么不直接用 C 重新实现你的(根据描述)短程序并看看性能。
-
@NoSenseEtAl 说我疯了,但我很想有一个非阻塞选择器的 C 实现,我的 Java 程序可以通过 JNI 调用它。哪里有这么强大的东西?
-
@Julie 我的建议是关于暖/冷性能...您可以在 C 中编写简单的 UDP 代码并运行 1M 和 10 个消息,看看它是否具有相同的分布 - 如果有的话可能不是选择器预热问题。关于 C 实现-我不知道,尽管 wiki 建议可以这样做:“例如,符合 POSIX 的操作系统将直接表示这些概念,select()。”另外,您可能想查看 LMAX Distruptor,不只是为了 Disruptor,他们有很多博客解释如何编写低延迟 Java 代码。
标签: java real-time messaging nio zeromq