9

我正在通过环回 (127.0.0.1) 在 Linux 上使用优化的 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 微秒(最小时间)内完成了行程。

  • 在忙碌的旋转中(感谢彼得),我能够从平均 200 微秒增加到单个数据包的平均 65 微秒。

  • 不知道为什么 ZeroMQ比这慢 5 倍。(编辑:也许是因为我通过环回在同一台机器上测试这个并且ZeroMQ使用了两台不同的机器?)

4

2 回答 2

4

您经常看到唤醒线程的情况可能非常昂贵,这不仅是因为线程唤醒需要时间,而且线程在之后的数十微秒内运行速度慢 2-5 倍,因为缓存和

我过去避免这种情况的方法是忙着等待。不幸的是 selectNow 每次调用它都会创建一个新集合,即使它是一个空集合。这会产生如此多的垃圾,不值得使用。

解决此问题的一种方法是忙于等待非阻塞套接字。这不能很好地扩展,但可以为您提供最低的延迟,因为线程不需要唤醒并且您在此之后运行的代码更有可能在缓存中。如果你也使用线程关联,它可以减少你的线程干扰。

我还建议尝试减少代码锁定和垃圾。如果您这样做,您可以在 Java 中创建一个进程,该进程在 90% 的时间里在 100 微秒内发送对传入数据包的响应。这将允许您在每个数据包到达时以 100 Mb 的速度处理它们(由于带宽限制,最多相隔 145 微秒)对于 1 Gb 连接,您可以非常接近。


如果你想在 Java 中的同一个盒子上进行快速的进程间通信,你可以考虑像https://github.com/peter-lawrey/Java-Chronicle 这使用共享内存来传递具有往返延迟的消息(这很难做到有效地使用套接字)小于 200纳秒。它还可以保留数据,如果您只是想要一种快速生成日志文件的方法,它会很有用。

于 2012-08-24T09:39:57.967 回答
-1

如果您正确调整选择器,您可以在不到 2 微秒的时间内获得 Java 中的套接字间通信。这是我对 256 字节 UDP 数据包的单向结果:

Iterations: 1,000,000
Message Size: 256 bytes
Avg Time: 1,680 nanos
Min Time: 1379 nanos
Max Time: 7020 nanos
75%: avg=1618 max=1782 nanos
90%: avg=1653 max=1869 nanos
99%: avg=1675 max=1964 nanos
99.9%: avg=1678 max=2166 nanos
99.99%: avg=1679 max=5094 nanos
99.999%: avg=1680 max=5638 nanos

我在我的文章Inter-socket communication with less than 2 microseconds latency中详细讨论了 Java NIO 和反应器模式。

于 2013-02-16T01:36:15.763 回答