Java NIO选择器可能的最小延迟

Jul*_*lie 9 java messaging nio real-time zeromq

我在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倍.(编辑:也许是因为我通过环回在同一台机器上测试它,而ZeroMQ使用两台不同的机器?)

Pet*_*rey 4

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

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

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

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


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