我在Linux over loopback(127.0.0.1)上使用优化的Java NIO选择器进行一些基准测试。
我的测试非常简单:
对于往返时间,我得到以下结果:
但这里的问题是我正在播放100万条消息。
如果我只旋转10条消息,我会得到非常不同的结果:
如果我错了,请纠正我,但我怀疑,一旦我们让NIO选择器旋转,响应时间就会变得最佳。但是,如果我们发送的消息间隔足够大,我们会支付唤醒选择器的费用。
如果我只发送一条消息,我会得到150到250微秒的不同时间。
所以我对社区的问题是:
1 - 我的最小时间为13微秒,平均为19微秒,这次往返包测试。看起来我到目前为止打ZeroMQ所以我可能在这里遗漏了一些东西。从这个基准测试看,ZeroMQ在标准内核上的平均时间为49微秒(99%)=> http://www.zeromq.org/results:rt-tests-v031
2 - 当我旋转一条或几条短信时,我能做些什么来改善选择器的反应时间? 150微米看起来不太好。或者我应该假设在prod环境中选择器不会完全?
通过忙于围绕selectNow()旋转,我可以获得更好的结果。发送少量数据包仍然比发送许多数据包更糟糕,但我认为我现在正在达到选择器性能限制。我的结果:
结论
因此看起来UDP数据包往返的物理屏障平均为10微秒,尽管我得到了一些数据包,以8微秒(最短时间)进行旅行。
随着忙碌的旋转(感谢Peter),我能够从平均200微秒变为平均每个数据包65微秒。
不确定为什么ZeroMQ是5 times slower。 (编辑:也许是因为我通过环回在同一台机器上测试它,而ZeroMQ使用两台不同的机器?)
答案 0 :(得分:4)
你经常看到那些唤醒线程的情况可能非常昂贵,不仅仅是因为线程需要花费时间才能唤醒,但是线程运行速度慢了几倍,持续了几十微秒,然后作为缓存和
我过去避免这种情况的方法是忙等待。不幸的是,selectNow每次调用它时都会创建一个新集合,即使它是一个空集合。这会产生很多垃圾,不值得使用。
解决这个问题的一种方法是忙于等待非阻塞套接字。这不会特别好地扩展,但可以为您提供最低的延迟,因为线程不需要唤醒,并且在此之后运行的代码更可能在缓存中。如果你也使用线程亲和力,它可以减少你的线程干扰。
我还建议尝试减少代码锁定和减少垃圾。如果这样做,您可以使用Java进程,该进程在90%的时间内在100微秒内发送对传入数据包的响应。这将允许您在到达时以100 Mb处理每个数据包(由于带宽限制,最多相隔145微秒)对于1 Gb连接,您可以非常接近。
如果你想在Java中的同一个盒子上进行快速进程间通信,你可以考虑类似https://github.com/peter-lawrey/Java-Chronicle这样的事情。它使用共享内存来传递具有往返延迟的消息(这对于套接字来说更难以有效) 200
答案 1 :(得分:-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和反应器模式。