使用新 PID 的轻量级进程行为

使用新 PID 的轻量级进程行为

我一直在尝试轻量级流程。基本上是调用克隆函数并将新的 PID 分配给克隆的 LWP。这工作得很好,它让我可以识别那些 LWP 的所有子线程。我遇到的小问题是性能。它的性能下降了很多(处理速度减慢了 30%)。现在我读到可以安排 LWP 并为其分配优先级(我也没有尝试)。这对性能有帮助吗?

我在运行 strace 时注意到一件事,Futex 使用量爆炸了 8-10 倍。 LWP 可能是造成这种情况的主要原因吗?可以理解的部分是上下文切换的爆炸,但我认为LWP共享相同的内存空间,所以Futex的使用不应该出现爆炸。

在使用 LWP 或决定使用它们时,是否有应该遵循的技巧或最佳实践?

从性能的角度来看,分叉是更好还是更差的选择?

答案1

我在运行 strace 时注意到一件事,Futex 使用量爆炸了 8-10 倍。 LWP 可能是造成这种情况的主要原因吗? [...],但我认为LWP共享相同的内存空间,所以Futex的使用量不应该出现爆炸。

是的,使用 LWP 可能会增加 Futexes 的使用,因为它们实际上是针对这种确切情况的,即共享相同内存的不同线程的同步。

当存在共享内存时,Futexes 用于任何锁定操作的慢速路径,LWP 或线程告诉内核阻止它,直到通知锁定已被解锁。

快速路径使用原子操作(CPU 以原子方式增加或减少计数器,因此它可以检测是第一个锁定还是最后一个解锁),因此不需要在快速路径上发出系统调用。

增加锁争用意味着将会发生更多的 Futex 操作,这可能会影响性能,不仅因为系统调用本身,还因为当调用 Futex 时,这意味着某些 LWP 或线程正在休眠等待资源。

glibc 中的代码会意识到多线程或 LWP 的使用,因此即使您的代码中没有显式锁,系统库也会有它们,因此可能会出现锁争用,从而可能会减慢速度你的程序如所描述的那样。

它的性能下降了很多(处理速度减慢了 30%)。

当您有许多线程共享内存时,另一个因素是还有一些内核内存结构具有粗略锁,并且也可能会产生锁争用。

特别是,mmap_sem每次将更多内存区域映射到进程时都需要锁定它以进行写入。 (特别是与malloc()朋友分配更多内存可能触发这个。)

现在我读到可以安排 LWP 并为其分配优先级(我也没有尝试)。这对性能有帮助吗?

可能……很难说。你必须进行基准测试。

如果您看到的是锁争用,并且它是通过您的代码路径进行概括的(不是本地化到单个或几个 LWP),那么它不太可能有帮助。

您可以使用perf工具帮助您了解 Linux 上一组进程的性能。它可以向您显示热点,还可以向您显示内核热点是否存在。

在使用 LWP 或决定使用它们时,是否有应该遵循的技巧或最佳实践?

对于 LWP 或线程,当使用大量它们时, 的实现malloc()变得非常重要,因为存在与内核相关的问题(扩展内存映射导致潜在的争用mmap_sem),以及用户空间中的问题(使用单个 arenas对于所有线程意味着您需要锁定以保留它们的空间。)

编写一些 malloc 库是为了提高使用大量线程的情况下的性能。例如,tcmalloc或者杰马洛克。采用这些通常很简单(只需链接一个额外的库),并且如果这确实是您的瓶颈,则可以产生巨大的性能提升。一如既往,进行基准测试以确定这是否有帮助。

从性能的角度来看,分叉是更好还是更差的选择?

可能更好。很难说,您需要进行基准测试,看看在您的具体情况下它是否更好。

与采用 LWP 一样,您应该进行基准测试来判断这是否真正值得。

如上所述,在 LWP 或线程之间使用共享内存(或者让更多 LWP 或线程共享相同的内存)会增加锁争用的可能性(即使您自己没有显式锁,glibc 和内核也会这样做。) LWP 很可能实际上会减慢你的速度。

我亲眼目睹了多线程应用程序速度太慢的情况。开发人员将其更改为使用单线程而不是 40 个线程。应用程序突然加速了 1,000%。结果发现 90% 的时间都花在了锁争用上!

答案2

经过几天的测试,我发现了以下情况。

Futexes 来自线程之间共享内存缓冲区(不幸的是这是不可避免的),线程以相当高的频率运行数学模型。 futex 直接影响执行延迟,但不是线性的,如果数据频率较高,则影响更大。

由于我知道大多数数据的大小,因此可以避免使用内存池或类似的某些分配。这对执行和 CPU 负载有积极的影响。

LWP 使用与父 PID 不同的 PID 进行克隆,这在 Linux 中没问题,但在 pThread 上不起作用。就性能而言,由于 LWP 的影响,它的性能有所下降,但并不明显。共享内存资源正在造成一个更大的问题。

关于使用 jeMalloc、tcMalloc 和 locklessMalloc 构建应用程序,这些库都没有给我带来竞争优势。如果核心数为 4 或更高,TcMalloc 就很好,如果缓存很大,则 jeMalloc 就很好。但对于我来说,多个运行场景的结果与基线相差 +/- 1%。

关于将更多内存区域映射到进程中,这会对整体执行产生很大的差异。 FillBraden 在这方面是正确的,当执行开始或数据流增加数据量时,它对我们打击很大。我们通过内存池升级了那里的行为。

包含一个使用 SCHED_RR 运行应用程序的测试系列,这也提高了执行速度。问题是它还在优先级方面对线程进行了更高的评分,因此这会产生影响。优点是我能够非常可靠地运行没有超线程的核心。原因是应用程序和模型的行为。由于未知的原因,超线程使事情变得相当混乱。

分叉各个模型有助于识别哪些线程属于哪个模型,但它并没有给我们带来执行速度的任何优势。这绝对是一种交易,也是一种解决方案,用于识别运行不良的模型线程并修复它们。

相关内容