Java和C/C++之间进程间通信的最快(低延迟)方法

Bas*_*ien 99 c++ java performance latency ipc

我有一个Java应用程序,通过TCP套接字连接到用C/C++开发的"服务器".

app和server都运行在同一台机器上,一个Solaris机箱(但我们最终考虑迁移到Linux).交换的数据类型是简单的消息(登录,登录ACK,然后客户端要求的东西,服务器回复).每条消息长约300字节.

目前我们正在使用套接字,一切正常,但我正在寻找一种更快的方式来交换数据(更低的延迟),使用IPC方法.

我一直在研究网络,并提出了以下技术的参考:

  • 共享内存
  • 管道
  • 队列
  • 以及所谓的DMA(直接内存访问)

但我无法找到他们各自的性能适当的分析,无论是如何实现它们在Java和C/C++(这样他们可以互相交谈),也许除了管我能想象该怎么办.

在这种情况下,任何人都可以评论每种方法的表现和可行性吗?任何有用的实现信息的指针/链接?


编辑/更新

按照我在这里的评论和答案,我发现了有关Unix域套接字的信息,它似乎是通过管道构建的,并且可以节省整个TCP堆栈.它是特定于平台的,因此我计划使用JNI或者judsjunixsocket进行测试.

接下来可能的步骤是直接实现管道,然后共享内存,虽然我已被警告过额外的复杂程度......


谢谢你的帮助

And*_*riy 102

刚刚测试了我的Corei5 2.8GHz上的Java延迟,只发送/接收了单字节,2个Java进程刚刚生成,没有为任务集分配特定的CPU内核:

TCP         - 25 microseconds
Named pipes - 15 microseconds
Run Code Online (Sandbox Code Playgroud)

现在显式指定核心掩码,如taskset 1 java Srvtaskset 2 java Cli:

TCP, same cores:                      30 microseconds
TCP, explicit different cores:        22 microseconds
Named pipes, same core:               4-5 microseconds !!!!
Named pipes, taskset different cores: 7-8 microseconds !!!!
Run Code Online (Sandbox Code Playgroud)

所以

TCP overhead is visible
scheduling overhead (or core caches?) is also the culprit
Run Code Online (Sandbox Code Playgroud)

同时Thread.sleep(0)(作为strace显示导致执行单个sched_yield()Linux内核调用)需要0.3微秒 - 因此命名为单核的命名管道仍然有很多开销

一些共享内存测量: 2009年9月14日 - Solace Systems今天宣布其统一消息平台API使用共享内存传输可以实现小于700纳秒的平均延迟. http://solacesystems.com/news/fastest-ipc-messaging/

PS - 第二天以内存映射文件的形式尝试共享内存,如果繁忙等待是可以接受的,我们可以通过以下代码传递单个字节,将延迟减少到0.3微秒:

MappedByteBuffer mem =
  new RandomAccessFile("/tmp/mapped.txt", "rw").getChannel()
  .map(FileChannel.MapMode.READ_WRITE, 0, 1);

while(true){
  while(mem.get(0)!=5) Thread.sleep(0); // waiting for client request
  mem.put(0, (byte)10); // sending the reply
}
Run Code Online (Sandbox Code Playgroud)

注意:需要Thread.sleep(0),因此2个进程可以看到彼此的更改(我还不知道另一种方式).如果2个进程与taskset强制使用相同的核心,则延迟变为1.5微秒 - 这是一个上下文切换延迟

PPS - 和0.3微秒是一个很好的数字!以下代码仅需0.1微秒,而仅执行原始字符串连接:

int j=123456789;
String ret = "my-record-key-" + j  + "-in-db";
Run Code Online (Sandbox Code Playgroud)

PPPS - 希望这不是太偏离主题,但最后我尝试用增量静态volatile int变量替换Thread.sleep(0)(JVM碰巧在执行此操作时刷新CPU缓存)并获得 - 记录!- 72纳秒延迟java-to-java进程通信!

然而,当被强制使用相同的CPU核心时,易失性递增的JVM永远不会相互控制,从而产生恰好10毫秒的延迟 - Linux时间量似乎是5毫秒......所以只有在有备用核心时才应该使用它 - 否则睡觉(0)更安全.

  • 尝试LockSupport.parkNanos(1),应该做同样的事情. (3认同)

MSa*_*ers 10

DMA是一种硬件设备可以在不中断CPU的情况下访问物理RAM的方法.例如,一个常见的例子是硬盘控制器,它可以直接从磁盘复制字节到RAM.因此,它不适用于IPC.

现代操作系统直接支持共享内存和管道.因此,它们非常快.队列通常是抽象,例如在套接字,管道和/或共享存储器之上实现.这可能看起来像一个较慢的机制,但另一种方法是创建这样的抽象.


Pet*_*rey 10

问题是前一段时间被问到的,但您可能对https://github.com/peter-lawrey/Java-Chronicle感兴趣,它支持200 ns的典型延迟和20 M消息/秒的吞吐量.它使用进程之间共享的内存映射文件(它还会持久保存数据,这使得它成为持久化数据的最快方式)


sus*_*rik 7

这是一个包含各种IPC传输的性能测试的项目:

http://github.com/rigtorp/ipc-bench


bak*_*kal 6

如果你考虑使用本机访问(因为你的应用程序和"服务器"都在同一台机器上),考虑JNA,它有更少的样板代码供你处理.


Nit*_*art 6

迟到,但想指出一个致力于使用Java NIO测量ping延迟的开源项目.

在这篇文中进一步探讨/解释.结果是(RTT in nanos):

Implementation, Min,   50%,   90%,   99%,   99.9%, 99.99%,Max
IPC busy-spin,  89,    127,   168,   3326,  6501,  11555, 25131
UDP busy-spin,  4597,  5224,  5391,  5958,  8466,  10918, 18396
TCP busy-spin,  6244,  6784,  7475,  8697,  11070, 16791, 27265
TCP select-now, 8858,  9617,  9845,  12173, 13845, 19417, 26171
TCP block,      10696, 13103, 13299, 14428, 15629, 20373, 32149
TCP select,     13425, 15426, 15743, 18035, 20719, 24793, 37877
Run Code Online (Sandbox Code Playgroud)

这与接受的答案一致.System.nanotime()误差(通过无测量估计)测量为大约40纳米,因此对于IPC,实际结果可能更低.请享用.