什么会导致软件在更好的硬件上 运行 变慢?
What Could Cause Software to Run Slower on Better Hardware?
我在 RHEL 7.4 上有一个 Java OSGi (Apache Felix) 应用程序 运行,它以 ~975 packets/second(长度为 1038 个八位字节)读取多播 UDP。然后它将数据转换为 XML,模拟跨越边界设备,并将其转换回 UDP 多播数据包。涉及多个线程,它的编写方式是如果模拟边界设备需要一段时间来处理一个有效载荷,它会缓冲它并在下一次发送更大的有效载荷。
通过此集成测试场景查看数据包延迟时,两台不同的桌面级机器明显快于我们期望部署的相当高端的服务器。
- 服务器延迟 5 秒。硬件:双至强 E5-2667v4@3.2GHz,128G RAM,16 个物理内核,32 个逻辑内核,RAID 1 SAS SSD。
- 桌面 A < 1 秒。 HW Xeon E5-1620v4@3.5Ghz, 64G 内存, 4 个物理, 8 个逻辑核心, 500G SSD
- 桌面 B < 1 秒。 HW i7-3770@3.4Ghz,16G RAM,4 个物理内核,8 个逻辑内核,1TB 7200RPM 驱动器。
我只提到硬盘驱动器是为了完整性,因为此应用程序不写入磁盘。从理论上讲,服务器的运行速度至少应与两个台式机一样快。
我已经消除的东西:
- 网卡。我已经用物理 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
记录两者的时间戳,减法产生延迟。所有三个应用程序(发件人、应用程序、tcpdump
在同一台机器上)。
首先针对虚拟接口的服务器硬件
i7 桌面硬件对虚拟接口
注意 Y 轴比例差异。服务器是0-4秒,i7桌面是0-1秒。看起来难以阅读的 X 轴是数据包编号。
下次尝试
我是 运行 应用程序的本地集成版本。然后,我消除了应用程序开始完成的几乎 100% 的工作,并发现服务器硬件上的延迟越来越长。然后我尝试 -Xmx100G -Xms100G
基本上让垃圾收集器远离 运行 EVER 并看到以下结果(< 1 秒一致延迟)。
这导致我 Java 8's Available Garbage Collectors。
服务器硬件上的默认垃圾收集器select 是新的:ParallelScavenge,旧的:ParallelOld。这是没有 XML 转换的结果延迟图,尽可能简单的测试来重现问题。
明确 select 垃圾优先垃圾收集器 -XX:+UseG1GC
select编辑新的:G1New,旧的:G1Old,它产生的延迟图不是很好:
显式 select 并发标记清除垃圾收集器 -XX:+UseConcMarkSweepGC
selected New: ParNew, Old: ConcurrentMarkSweep 其生成的延迟图看起来非常好:
看来问题已经解决了。将所有组件添加回原位后,我仍然遇到无法接受的延迟。我仍在 运行 测试以查看是否可以隔离问题。
Strace 结果
尝试 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
系统调用中,桌面都快很多倍。我仍然不明白为什么应用程序在更快的硬件上潜伏得更多。
分析
我已经分析了两种硬件上的软件,显示了相似的热点位置,这似乎排除了这种可能性。
我确认我在 RedHat 中使用 performance
CPU 调控器:CPUfreq Coverners
我 运行 VMWare ESXi 报告有问题的 BIOS 设置 Virtual Machine Application runs slower than expected on EXSi
直接指向我的答案。此 Dell R630 的默认设置为 "Performance Per Watt (DAPC)"(DAPC:Dell Active Power Controller)。切换到 "Performance" 完全解决了这个问题。这台机器在控制台上感觉更加敏捷,并且延迟比桌面能够实现的延迟低得多,这是我预期的 CPU 差异。
在戴尔 R630(可能还有其他)启动时更改 BIOS 的步骤:
- F2 进入系统设置
- Select "System BIOS"
- Select "System Profile Settings"
- 确保第一个条目设置为 "Performance" 默认为 "Performance Per Watt"
- Select "Back"
- Select "Finish"
- Select "Yes" 在系统重置时保存更改
- Select "OK" 设置保存成功
这是生成的延迟图,它们使用相同的 1 秒刻度。
服务器上的默认 GC:
服务器上的并发标记清除 GC:
服务器上的第一代 GC:
G1GC 和 CMSGC 之间没有太大区别,但两者的延迟明显优于默认值(这是预期的)。
逻辑核心时钟速度图
符号很难看清,但这两个图上有32个不同的点。总体而言,您可以快速分辨出哪个是性能,哪个是 performance-per-watt-dapc.
每瓦性能 (DAPC):
性能
一起绘制。红色子弹中的性能,蓝色空心圆中的每瓦性能
这是在 300 秒的数据流中捕获的,并相应地设置了 BIOS。以下是我捕获数据的方式,以防有人想知道:
for i in `seq 300`; do
paste /sys/devices/system/cpu/cpu[0-9]*/cpufreq/cpuinfo_cur_freq
sleep 1
done > performance.log
我在 RHEL 7.4 上有一个 Java OSGi (Apache Felix) 应用程序 运行,它以 ~975 packets/second(长度为 1038 个八位字节)读取多播 UDP。然后它将数据转换为 XML,模拟跨越边界设备,并将其转换回 UDP 多播数据包。涉及多个线程,它的编写方式是如果模拟边界设备需要一段时间来处理一个有效载荷,它会缓冲它并在下一次发送更大的有效载荷。
通过此集成测试场景查看数据包延迟时,两台不同的桌面级机器明显快于我们期望部署的相当高端的服务器。
- 服务器延迟 5 秒。硬件:双至强 E5-2667v4@3.2GHz,128G RAM,16 个物理内核,32 个逻辑内核,RAID 1 SAS SSD。
- 桌面 A < 1 秒。 HW Xeon E5-1620v4@3.5Ghz, 64G 内存, 4 个物理, 8 个逻辑核心, 500G SSD
- 桌面 B < 1 秒。 HW i7-3770@3.4Ghz,16G RAM,4 个物理内核,8 个逻辑内核,1TB 7200RPM 驱动器。
我只提到硬盘驱动器是为了完整性,因为此应用程序不写入磁盘。从理论上讲,服务器的运行速度至少应与两个台式机一样快。
我已经消除的东西:
- 网卡。我已经用物理 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
记录两者的时间戳,减法产生延迟。所有三个应用程序(发件人、应用程序、tcpdump
在同一台机器上)。
首先针对虚拟接口的服务器硬件
i7 桌面硬件对虚拟接口
注意 Y 轴比例差异。服务器是0-4秒,i7桌面是0-1秒。看起来难以阅读的 X 轴是数据包编号。
下次尝试
我是 运行 应用程序的本地集成版本。然后,我消除了应用程序开始完成的几乎 100% 的工作,并发现服务器硬件上的延迟越来越长。然后我尝试 -Xmx100G -Xms100G
基本上让垃圾收集器远离 运行 EVER 并看到以下结果(< 1 秒一致延迟)。
这导致我 Java 8's Available Garbage Collectors。
服务器硬件上的默认垃圾收集器select 是新的:ParallelScavenge,旧的:ParallelOld。这是没有 XML 转换的结果延迟图,尽可能简单的测试来重现问题。
明确 select 垃圾优先垃圾收集器 -XX:+UseG1GC
select编辑新的:G1New,旧的:G1Old,它产生的延迟图不是很好:
显式 select 并发标记清除垃圾收集器 -XX:+UseConcMarkSweepGC
selected New: ParNew, Old: ConcurrentMarkSweep 其生成的延迟图看起来非常好:
看来问题已经解决了。将所有组件添加回原位后,我仍然遇到无法接受的延迟。我仍在 运行 测试以查看是否可以隔离问题。
Strace 结果
尝试 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
系统调用中,桌面都快很多倍。我仍然不明白为什么应用程序在更快的硬件上潜伏得更多。
分析
我已经分析了两种硬件上的软件,显示了相似的热点位置,这似乎排除了这种可能性。
我确认我在 RedHat 中使用 performance
CPU 调控器:CPUfreq Coverners
我 运行 VMWare ESXi 报告有问题的 BIOS 设置 Virtual Machine Application runs slower than expected on EXSi
直接指向我的答案。此 Dell R630 的默认设置为 "Performance Per Watt (DAPC)"(DAPC:Dell Active Power Controller)。切换到 "Performance" 完全解决了这个问题。这台机器在控制台上感觉更加敏捷,并且延迟比桌面能够实现的延迟低得多,这是我预期的 CPU 差异。
在戴尔 R630(可能还有其他)启动时更改 BIOS 的步骤:
- F2 进入系统设置
- Select "System BIOS"
- Select "System Profile Settings"
- 确保第一个条目设置为 "Performance" 默认为 "Performance Per Watt"
- Select "Back"
- Select "Finish"
- Select "Yes" 在系统重置时保存更改
- Select "OK" 设置保存成功
这是生成的延迟图,它们使用相同的 1 秒刻度。
服务器上的默认 GC:
服务器上的并发标记清除 GC:
服务器上的第一代 GC:
G1GC 和 CMSGC 之间没有太大区别,但两者的延迟明显优于默认值(这是预期的)。
逻辑核心时钟速度图
符号很难看清,但这两个图上有32个不同的点。总体而言,您可以快速分辨出哪个是性能,哪个是 performance-per-watt-dapc.
每瓦性能 (DAPC):
性能
一起绘制。红色子弹中的性能,蓝色空心圆中的每瓦性能
这是在 300 秒的数据流中捕获的,并相应地设置了 BIOS。以下是我捕获数据的方式,以防有人想知道:
for i in `seq 300`; do
paste /sys/devices/system/cpu/cpu[0-9]*/cpufreq/cpuinfo_cur_freq
sleep 1
done > performance.log