【发布时间】:2023-04-08 14:00:01
【问题描述】:
我有一个在 RHEL 7.4 上运行的 Java OSGi (Apache Felix) 应用程序,它以大约 975 个数据包/秒(长度为 1038 个八位字节)的速度读取多播 UDP。然后,它将数据转换为 XML,模拟通过边界设备,并将其转换回 UDP 多播数据包。涉及多个线程,并且它的编写方式是,如果模拟边界设备需要一段时间来处理一个负载,它会缓冲它并在下一次发送更大的负载。
通过此集成测试方案查看数据包延迟时,两台不同的桌面级机器比我们预期部署的相当高端的服务器要快得多。
- 服务器延迟 5 秒。硬件:双 Xeon E5-2667v4@3.2GHz、128G RAM、16 个物理内核、32 个逻辑内核、RAID 1 SAS SSD。
- 桌面 A
- 桌面 B
为了完整起见,我只提到硬盘驱动器,因为此应用程序不会写入磁盘。在理论上,服务器的运行速度至少应该与两个桌面一样快。
我已经消除的东西:
- 网卡。我已经对物理 NIC 和虚拟设备进行了测试,以防万一 NIC 之间存在显着差异。
- 逻辑核心数。我已尝试禁用 16 和 24 个服务器逻辑核心以排除变量。
- Java 版本。所有这三个都已在 OpenJDK 和 Oracle 的 Java 上使用相同版本 (Java
1.8.0) 进行了尝试,产生了相同的结果。 - Java 标志是相同的,并且都与 felix 相关(安装目录、配置属性和要执行的 jar)。
- SELinux。我已经在所有三种模式下都试过了(禁用、强制、许可)。我没想到这里会有什么不同,但我现在正在抓住任何东西。
- 内核版本。我已经对
3.10.0、4.13.0和4.15.0进行了测试,结果相似。
ark.intel.com processor comparison
这里有两个示例图表来说明问题。该测试在 4 分 10 秒内向多播地址 A 发送 260,960 个 UDP 数据包,并在通过应用程序处理后,将数据包发送到多播地址 B。tcpdump 记录两者的时间戳,减法产生延迟。所有三个应用程序(Sender、Application、tcpdump 都在同一台机器上)。
首先针对虚拟接口的服务器硬件
i7 桌面硬件对抗虚拟接口
注意 Y 轴比例差异。服务器是 0-4 秒,i7 桌面是 0-1 秒。看起来难以阅读的 X 轴是 Packet Number。
下一次尝试
我正在运行应用程序的本地集成版本。然后,我消除了应用程序开始完成的几乎 100% 的工作,并看到服务器硬件上的延迟越来越长。然后我尝试-Xmx100G -Xms100G 基本上是为了让垃圾收集器永远不会运行,并看到了以下结果(
这让我找到了Java 8's Available Garbage Collectors。
服务器硬件上的默认垃圾收集器选择是新的:ParallelScavenge,旧的:ParallelOld。这是没有 XML 转换的结果延迟图,我可以通过简单的测试来复制问题。
明确选择 Garbage First Garbage Collector -XX:+UseG1GC 选择 New:G1New,Old:G1Old,结果延迟图不是很好:
明确选择 Concurrent Mark Sweep Garbage Collector -XX:+UseConcMarkSweepGC 选择 New:ParNew,Old:ConcurrentMarkSweep,其生成的延迟图看起来很棒:
看起来问题已经解决了。一旦我将所有组件添加回原位,我仍然会遇到不可接受的延迟。我仍在运行测试,看看是否可以隔离问题。
追踪结果
尝试strace -c -o /path/to/file -f 产生了以下顶级系统调用
首先是 i7 的桌面 strace 报告(截断前 10 项)
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
93.71 1418.604132 959 1479659 134352 futex
1.74 26.294223 730395 36 poll
1.74 26.288786 314 83645 4 read
1.41 21.373672 73 293618 epoll_pwait
1.19 17.952475 120 149854 2 recvfrom
0.10 1.448453 2 909731 getrusage
0.06 0.896903 3 281407 sendto
0.03 0.394695 2 198041 write
0.01 0.182809 10 18246 mmap
0.01 0.120735 6 20582 sched_yield
现在查看服务器的strace 报告:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
97.46 2119.311196 2642 802183 131276 futex
1.28 27.734136 6933534 4 poll
0.59 12.840448 49 263597 epoll_wait
0.41 8.885742 113 78387 2 recvfrom
0.07 1.575401 6 263671 sendto
0.07 1.515999 6 262256 epoll_ctl
0.04 0.902788 54 16800 sched_yield
0.03 0.743231 10 75455 write
0.02 0.490052 6 84509 7 read
0.01 0.170152 4 42732 lseek
我不清楚我应该从中得出什么结论。在futex 和poll 系统调用中,桌面速度要快很多倍。我仍然不明白为什么应用程序在更快的硬件上具有如此多的潜伏性。
分析
我已经在两个硬件上分析了软件,显示了相似的热点位置,这似乎排除了这种可能性。
【问题讨论】:
-
我假设环境标志在两者上是相同的。黑暗中的一枪:您是否尝试过研究 JIT 如何在其中一个上工作(即,无论出于何种原因它是否不在服务器上)?您是否尝试过使用 Flight Recorder 或类似的东西来查看延迟可能来自何处?
-
我只使用了与 felix 相关的 java 选项,所以标志应该是相同的。我现在将查看 JIT / Flight Recorder,看看是否有什么惊人的地方。
-
分配给JVM的内存是否超过了不再使用压缩指针的阈值?你实际上可以在这一点上受到显着的性能影响,因为更少的内容适合缓存。 (是的,您指定 JVM 标志是相同的,但您没有指定这些标志 是什么,因此我们不知道哪些配置是显式的,哪些是从硬件推断的) .
-
...不过,这确实是我要退出(商业)JVM 分析工具(也许还有 sysdig,以查看操作系统级别的系统调用响应是否存在差异)的地方次)。
-
Java 标志是默认的,而不是告诉 felix 从哪里获取配置/属性。如何判断分配给 JVM 的内存是否超过了这个阈值?此外,服务器版本的缓存是桌面 A 的 2.5 倍,这使得它不太可能,但我现在想排除一切。我会研究一下 sysdig。
标签: java osgi performance-testing multicast latency