【问题标题】:Java TCP/IP Socket Latency - stuck at 50 μs (microseconds)? (used for Java IPC)Java TCP/IP 套接字延迟 - 停留在 50 微秒(微秒)? (用于Java IPC)
【发布时间】:2011-08-22 01:12:49
【问题描述】:

我们一直在分析和分析我们的应用程序,以尽可能减少延迟。我们的应用程序由 3 个独立的 Java 进程组成,它们都运行在同一台服务器上,它们通过 TCP/IP 套接字相互传递消息。

我们已将第一个组件的处理时间减少到 25 μs,但我们看到 TCP/IP 套接字写入(在 localhost 上)到下一个组件总是需要大约 50 μs。我们看到了另一种异常行为,即接受连接的组件可以更快地写入(即

不是 TCP/IP 专家,我不知道可以做些什么来加快速度。 Unix 域套接字会更快吗?内存映射文件?还有哪些其他机制可能是将数据从一个 Java 进程传递到另一个进程的更快方法?

2011 年 6 月 21 日更新 我们创建了 2 个基准测试应用程序,一个在 Java 中,一个在 C++ 中,以更严格地对 TCP/IP 进行基准测试并进行比较。 Java 应用使用 NIO(阻塞模式),而 C++ 使用 Boost ASIO tcp 库。结果或多或少相当,C++ 应用程序比 Java 快约 4 μs(但在其中一项测试中,Java 击败了 C++)。此外,两个版本都显示每条消息的时间存在很大差异。

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

【问题讨论】:

  • 微秒的 SI 简写是 μs,而不是 μ(数量和单位之间应该有空格)。我帮你修好了。
  • 我也不是专家,我会冒险猜测 UDP 可能会降低您的延迟,因为它是一种更轻量级的协议。当然,如果您的应用程序必须手动实现与 TCP 开箱即用相同的可靠性保证,那么编程起来会更加痛苦,并且可能不会产生任何好处。
  • stdin/stdout/stderr 怎么样(例如,第一个进程启动另外 2 个,并且通信只发生在这个“主”和 2 个从属之间)?这是一个选项吗?
  • 这三个进程不能共享同一个JVM有什么原因吗?
  • @Marcelo:谢谢!对于更好的语法和句法的指针总是很感激!

标签: java ipc tcp interprocess low-latency


【解决方案1】:

如果使用原生库via JNI 是一种选择,我会考虑像往常一样实现 IPC(搜索 IPC、mmap、shm_open 等)。

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

TCP 套接字写入需要 50 微秒,这让我有点惊讶 - 大多数操作系统在一定程度上优化了 TCP 环回。 Solaris 实际上用一个叫做TCP Fusion 的东西做得很好。如果已经对环回通信进行了任何优化,那通常是针对 TCP 的。 UDP 往往会被忽视——所以在这种情况下我不会打扰它。我也不会打扰管道(stdin/stdout 或您自己的命名管道等),因为它们会更慢。

通常,您看到的很多延迟很可能来自信号 - 在套接字的情况下等待像 select() 这样的 IO 选择器,或者等待信号量,或者等待某些东西。如果您想要尽可能低的延迟,则必须烧掉一个处于紧密循环轮询新数据的核心。

当然,总是有commercial off-the-shelf 路线——我碰巧知道这肯定会很快解决你的问题——但当然它确实要花钱。并且为了全面披露:我确实为 Informatica 的低延迟消息传递软件工作。 (作为一名工程师,我诚实的看法是,它是一款非常棒的软件——当然值得一试。)

【讨论】:

  • 我查看了您的网站并看到了您的超级消息产品。我在 CISCO UCS 上看到它显示的延迟小于 1 μs。你认为它在标准 Linux 服务器上会是什么? (例如 2 双核英特尔至强)?
  • 我实际上已经在这里用我花了大约 400 美元买的一个极小的 Core 2 Quad Q6600 机器进行了测试,我也可以在 C 语言中得到低于 1 微秒的时间(不过稍微低于 - 它是仍然不如更高级的思科服务器机器那样令人印象深刻)。不到一微秒的数字全部来自使用纯 C 应用程序运行的基准测试;对于 Java,由于 JNI 开销,添加几微秒作为基本地板。这也是一个在紧密循环中轮询的接收线程;您也可以在非轮询的情况下运行它,但是您会获得更多的信号/线程唤醒延迟。
【解决方案2】:

“O'Reilly 关于 NIO 的书(Java NIO,第 84 页)似乎对 内存映射是否留在内存中。也许它只是在说 就像其他内存一样,如果你的物理内存用完了,它就会被交换 回磁盘,否则不行?”

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 我们或多或少得出了与您相同的结论。不过,我对内存映射文件的问题是,该内存是否会保留在 RAM 中,或者是否有时会分页回磁盘(不确定管理该文件的规则是什么)。 O'Reilly 关于 NIO 的书(Java NIO,第 84 页)似乎对内存映射是否保留在内存中含糊不清。也许它只是说像其他内存一样,如果你的物理内存用完了,它会被交换回磁盘,否则不会?
    【解决方案4】:

    MemoryMappedFiles 根本不是低延迟 IPC 的可行解决方案 - 如果内存的映射段得到更新,它最终将同步到磁盘,从而引入不可预测的延迟,至少以毫秒为单位。对于低延迟,可以尝试共享内存+消息队列(通知)或共享内存+信号量的组合。这适用于所有 Unix,尤其是 System V 版本(不是 POSIX),但如果您在 Linux 上运行应用程序,则使用 POSIX IPC 非常安全(大多数功能在 2.6 内核中可用)是的,您需要 JNI 才能完成此操作。

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

    【讨论】:

    • "如果映射的内存段得到更新,它最终将同步到磁盘"。它是否仅作为“交换”分页到磁盘(即当 O/S 没有足够的物理 RAM 将其仅作为内存保留时)?
    • pdflush 知道什么时候。在 Linux 上。
    【解决方案5】:

    查看https://github.com/pcdv/jocket

    它是使用共享内存的本地 Java 套接字的低延迟替代品。

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

    【讨论】:

    • 我开始看Jocket。它似乎正在使用 MappedByteBuffer ,这是由其他几个答案建议的。我已经使用 MappedByteBuffer 进行了测试,发现它对于 IPC 来说非常快。但是关于磁盘 I/O 何时发生(这会引入非常大的停顿,取决于刷新到磁盘的缓冲区的大小),我仍然有一个未解决的问题。
    • 没错,它使用了MappedByteBuffer。我也担心 I/O 延迟,所以我决定尽可能在“/dev/shm”下创建文件(在 linux 下,它被挂载为 tmpfs,因此没有 I/O)。但是,在我的基准测试中,我无法观察到任何明显的性能差异......
    • 感谢您提供有关 /dev/shm 的提示。这绝对值得我们尝试。
    猜你喜欢
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-21
    • 2019-12-16
    相关资源
    最近更新 更多