我在扩展多进程/多线程 C++ 应用程序时遇到了奇怪的行为。该应用程序包含 10 个独立进程,通过Unix 域套接字每个线程都有大约 100 个线程执行 IO,并且该 IO 上有多个进程。系统是 OLTP,事务处理时间至关重要。IPC IO 基于使用 zmq 通过 unix 域套接字进行的 boost 序列化(它在我们的本地服务器(两个具有 24 个内核的旧 xeon)上的所有基准测试中都足够快)。现在,我们在内核数更多的系统上观察到了极低的性能!
1x Intel® Xeon® X5650 - 虚拟 - 6 核 - TPS 约为 150 (预期)
1x Intel® Xeon® E5-4669 v4 - 专用 - 32 核 - TPS 约为 700 (预期)
2x Intel® Xeon® E5-2699 v4 - 专用 - 88 核 -TPS 约为 90(应该是~2000)
在第三台服务器上运行几个基准测试显示处理器能力完全正常。内存带宽和延迟看起来很正常。
htop 显示内核耗时非常长(红色部分)。所以我们的第一个猜测是某些系统调用需要太长时间才能完成,或者我们在多线程代码中犯了错误。(见下图)perf top
报告特定的系统调用/内核例程(native_queued_spin_lock_slowpath
)占用了大约 40% 的内核时间(见下图)我们不知道它做了什么。
然而,还有另一个非常奇怪的观察结果是:
降低分配给进程的核心数量,使得系统更好地利用核心(更多绿色部分,更高的 CPU 使用率)并使整个软件(所有 10 个进程)运行得更快(TPS 约为 400)。
因此,当我们运行该流程时,taskset -cp 0-8 service
我们达到~400 TPS。
你如何解释为什么将分配的 CPU 数量从 88 个降低到 8 个可以使系统运行速度提高 5 倍,但 88 个核心的性能却只有预期的 1/4?
附加信息:
操作系统:Debian 9.0 amd64
内核:4.9.0
答案1
当多个插槽大幅降低性能时,肯定看起来像是 NUMA 效应。
perf
非常有用。在性能报告中,您可以看到native_queued_spin_lock_slowpath
占用了 35%,这似乎对您的并发代码来说是很大的开销。如果您对并发代码不是很了解,那么棘手的部分就是可视化什么在调用什么。
我会推荐利用系统范围的 CPU 采样制作火焰图。 快速开始:
git clone https://github.com/brendangregg/FlameGraph # or download it from github
cd FlameGraph
perf record -F 99 -a -g -- sleep 60
perf script | ./stackcollapse-perf.pl > out.perf-folded
./flamegraph.pl out.perf-folded > perf-kernel.svg
在结果图中,寻找最高的“高原”。这表明函数具有最独特的时间。
我期望当该bpfcc-tools
软件包在 Debian 稳定版中时,能够以更少的开销直接收集这些“折叠”堆栈。
如何处理取决于你发现的内容。了解哪些关键部分受到锁的保护。与对现代硬件上可扩展同步的现有研究进行比较。例如,Concurrency Kit 演示文稿指出,不同的自旋锁实现具有不同的属性。
答案2
我敢说这是硬件“问题”。您使 IO 子系统过载,而正是这种并行性导致其速度变慢(如磁盘)。
主要症状为:
- IO 线程约 100 个
- 您只字未提 IO。这是典型的缺乏经验的人忽视且从不谈论的领域。数据库的典型特征是“哦,我有那么多内存,但我没有告诉您我使用慢速大容量磁盘运行,为什么我这么慢”。
答案3
因为软件厂商大多懒得做多核优化。
软件设计师很少设计能够充分利用系统硬件功能的软件。一些编写良好的软件可以被认为是优秀的软件,例如挖矿软件,因为其中许多软件能够将显卡的处理能力发挥到最大程度(与游戏不同,游戏从未接近利用 GPU 的真正处理能力)。
如今,很多软件都存在类似情况。它们从不费心进行多核优化,因此,与运行速度较低的内核相比,运行速度较高的内核较少的软件时,性能会更好。在内核较多且速度较快的情况下,由于相同的原因,这并不总是一种优势:代码编写不当。程序会尝试将其子任务拆分到过多的内核上,这实际上会延迟整体处理。