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

时间:2010-04-14 06:23:02

标签: java c++ performance ipc latency

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

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

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

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

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

但是我无法找到他们各自表现的正确分析,也没有找到如何在JAVA和C / C ++中实现它们(这样他们可以互相交谈),除了可能想象如何做的管道。

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


编辑/更新

发表评论&我得到的答案,我发现有关Unix域套接字的信息,它似乎是通过管道构建的,并且可以节省整个TCP堆栈。 它是特定于平台的,因此我计划使用JNI或judsjunixsocket对其进行测试。

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


感谢您的帮助

10 个答案:

答案 0 :(得分:99)

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

TCP         - 25 microseconds
Named pipes - 15 microseconds

现在明确指定核心掩码,例如 taskset 1 java Srv taskset 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 !!!!

所以

TCP overhead is visible
scheduling overhead (or core caches?) is also the culprit

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

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

P.S。 - 第二天尝试以内存映射文件的形式共享内存, 如果忙碌等待是可以接受的,我们可以将延迟减少到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
}

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

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

int j=123456789;
String ret = "my-record-key-" + j  + "-in-db";

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

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

答案 1 :(得分:10)

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

答案 2 :(得分:9)

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

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

答案 3 :(得分:7)

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

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

答案 4 :(得分:6)

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

答案 5 :(得分:6)

迟到,但想要指出open source project致力于使用Java NIO测量ping延迟。

在此blog post中进一步探讨/解释。结果是(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

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

答案 6 :(得分:2)

我对本机进程间通信知之甚少,但我猜你需要使用本机代码进行通信,您可以使用JNI机制访问本机代码。因此,从Java中,您可以调用与其他进程通信的本机函数。

答案 7 :(得分:1)

在我以前的公司,我们曾经使用过这个项目http://remotetea.sourceforge.net/,非常容易理解和整合。

答案 8 :(得分:0)

您是否考虑过保持套接字打开,以便可以重用连接?

答案 9 :(得分:-1)

关于JNI性能的Oracle错误报告:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069

JNI是一个慢速接口,因此Java TCP套接字是应用程序之间通知的最快方法,但这并不意味着您必须通过套接字发送有效负载。使用LDMA传输有效负载,但正如previous questions指出的那样,Java对内存映射的支持并不理想,因此您需要实现一个JNI库来运行mmap。