2011-05-09 32 views
10

我们一直在分析和剖析我们的应用程序,以尽可能减少延迟。我们的应用程序由3个独立的Java进程组成,它们都运行在同一台服务器上,它们通过TCP/IP套接字将消息传递给对方。Java TCP/IP套接字延迟 - 卡在50微秒(微秒)? (用于Java IPC)

我们将第一个组件的处理时间缩短到25μs,但我们看到TCP/IP套接字写入(在本地主机上)到下一个组件总是需要大约50μs。我们看到另一个异常行为,其中接受连接的组件可以写得更快(即<50μs)。目前,除套接字通信外,所有组件都运行< 100μs。

不是TCP/IP专家,我不知道如何才能加快速度。 Unix域套接字会更快吗? MemoryMappedFiles?还有哪些其他机制可能是将数据从一个Java过程传递到另一个更快的方法?

UPDATE 2011/6/21 我们创建了2个基准测试应用程序,一个使用Java,一个使用C++来更严格地对TCP/IP进行基准测试并进行比较。 Java应用程序使用NIO(阻塞模式)和C++使用的tcp库Boost ASIO。结果大致相当,C++应用程序比Java快大约4μs(但在其中一项Java打败C++的测试中)。而且,两个版本在每条消息的时间上都表现出很大的变化。

我想我们同意共享内存实现将会是最快的基本结论。 (虽然我们也想评估Informatica产品,只要它符合预算。)

+3

微秒的SI简写为μs,而不是μ(并且您应该在数量和单位之间有一个空格)。我为你修好了。 – 2011-05-09 22:52:38

+0

不是一个专家,我会冒险猜测UDP可能会让你的延迟下降,因为它是一个更轻量级的协议。当然,编程时会更加痛苦,如果您的应用程序必须手动实现TCP提供的开箱即用的可靠性保证,则可能不会带来任何好处。 – 2011-05-09 23:02:10

+0

stdin/stdout/stderr怎么样(例如第一个进程启动另一个进程2,comms只发生在这个'master'和2个slave之间)?这是一个选项吗? – laher 2011-05-09 23:08:07

回答

3

如果使用本地库via JNI是一个选项,我会考虑像往常一样实现IPC(搜索IPC,mmap,shm_open等) )。

使用JNI有很多开销,但至少比使用套接字或管道进行任何操作所需的全部系统调用要少一些。使用通过JNI的轮询共享内存IPC实现,您可能会降低大约3微秒的单向延迟。 (请确保使用-Xcomp JVM选项或调整编译阈值;否则您的前10,000个样本将会非常糟糕,这会产生很大的差异。)

我对TCP套接字写入有点惊讶需要50微秒 - 大多数操作系统在一定程度上优化了TCP环回。 Solaris实际上做的很好,名为TCP Fusion。如果根本没有任何环回通信的优化,它通常是用于TCP。 UDP往往会被忽视 - 所以在这种情况下我不打扰它。我也不会打扰管道(标准输入/标准输出或你自己的命名管道等),因为它们会变得更慢。

而且通常,您看到的很多延迟可能来自信号传递 - 或者在插座的情况下等待IO选择器,如select(),或等待信号量或等待某个事件。如果您想要尽可能降低延迟,那么您必须刻录一个核心,以循环轮询新数据。

当然,总是有一条commercial off-the-shelf路线 - 我碰巧知道这条路线可以快速解决您的问题 - 但这当然会花费您的金钱。为了充分披露:我在Informatica的低延迟消息传递软件上工作。 (作为一名工程师,我的诚恳意见是,它是非常出色的软件 - 当然值得检查这个项目。)

+0

我检出了您的网站并看到了您的超级消息产品。我在CISCO UCS上看到它显示的延迟小于1微秒。你认为它会在标准的Linux服务器上出现什么? (例如2个双核英特尔至强)? – 2011-05-12 00:16:32

+2

我实际上在这里用一个我买的Dinky Core 2 Quad Q6600机器在这里测试了大约400美元,我也可以在C下得到1微秒的时间(虽然只是一点点 - 但它仍然不如爱好者思科服务器机器可以做)。一微秒以下的数字全部来自使用普通C应用程序运行的基准测试;对于Java,由于JNI开销,将几微秒添加为基本层。这也是一个接收线程,在一个紧密的循环轮询;你也可以运行它非轮询,但是你会得到更多的微信号/线程唤醒延迟。 – strangelydim 2011-05-13 16:49:53

1

查看我的回答fastest (low latency) method for Inter Process Communication between Java and C/C++ - 用java到Java延迟可以减少到0.3微秒存储器映射文件(共享存储器)

+0

我们得出了或多或少的相同结论。尽管我有关于内存映射文件的问题,无论内存是留在RAM中,还是有时会返回到磁盘(不知道管理这些内容的规则是什么)。 O'Reilly关于NIO的书(Java NIO,第84页)似乎对存储器映射是否停留在内存模糊不清。也许这只是说像其他内存一样,如果你用完了物理,它会被换回到磁盘,但是否则不会呢? – 2011-06-21 14:07:12

1

MemoryMappedFiles不用于低等待时间IPC可行的解决方案在所有 - 如果映射段的内存得到更新,它最终将被同步到磁盘,从而引入不可预测的延迟,至少在毫秒内测量。对于低延迟,可以尝试共享内存+消息队列(通知)或共享内存+信号量的组合。这适用于所有的Unix,特别是System V版本(不是POSIX),但是如果你在Linux上运行应用程序,那么使用POSIX IPC就可以保证安全(大多数功能都可以在2.6内核中使用)是的,你需要JNI才能完成。

UPD:我忘了这是JVM -JVM IPC,我们已经有了GC,我们无法完全控制,所以引入额外的几个由于OS文件缓冲区闪存到磁盘而导致的ms暂停可能是可以接受的。

+0

“如果映射的内存段被更新,它最终将被同步到磁盘”。它是否仅作为“交换”分页(即,当O/S没有足够的物理RAM来保存它作为内存时)? – 2011-10-12 16:05:18

+0

pdflush知道何时。在Linux上。 – 2011-10-26 00:05:25

2

“O'Reilly出版的NIO(Java NIO,第84页),似乎是含糊 内存映射是否保留在内存中,也许它只是说,像其他的内存,如果您运行 在物理上,这被换回 回到磁盘,但否则不?“

Linux。 mmap()调用在OS页面缓存区域中分配页面(这些页面会周期性地刷新到磁盘,并且可以基于与LRU算法近似的Clock-PRO被驱逐?)所以你的问题的答案是 - 是的。内存映射缓冲区可以从内存中逐出(理论上),除非它被锁定(mlock())。这是理论上的。在实践中,我认为如果你的系统不是交换几乎不可能在这种情况下,第一个受害者是页面缓冲区。

1

退房https://github.com/pcdv/jocket

这是一个低延迟替代对于使用共享内存的本地Java插槽。

现代CPU上2个进程之间的RTT延迟远低于1us。

+0

我开始看Jocket。它似乎正在使用MappedByteBuffer,这是由其他几个答案继承人提出的。我已经使用MappedByteBuffer进行了测试,并且发现它对IPC来说非常快。但是关于磁盘I/O何时发生(其中引入了非常大的暂停,取决于刷新到磁盘的缓冲区的大小),我仍然没有得到答案。 – 2013-09-12 15:11:58

+0

没错,它使用MappedByteBuffer。我也担心I/O延迟,因此我决定尽可能在“/ dev/shm”下创建文件(在linux下,它被挂载为tmpfs,因此没有I/O)。然而,在我的基准测试中,我无法观察到任何明显的性能差异...... – pcdv 2013-09-12 15:34:09

+0

感谢您关于/ dev/shm的提示。这绝对值得我们尝试。 – 2013-09-12 17:25:14