使用“threadDelay”时系统调用过多`

我在一个12核的系统上运行了几个Haskell进程。所有进程都使用线程化编译,并使用12种功能运行。他们都使用的一个库是资源池,它保持一个数据库连接池

有趣的是,即使所有进程实际上都处于空闲状态,它们也会消耗大约2%的CPU时间。使用strace-p$(pgrep processname)-f检查其中一个进程,会发现该进程正在进行不合理的系统调用,即使它实际上不应该做任何事情。要正确看待事物:

  • 在带有-N2的进程上运行strace 5秒钟会生成66K日志文件
  • 使用(不合理的)-N64运行它会产生一个60兆字节的日志

因此,功能的数量大大增加了发出的系统调用的数量

深入挖掘我们发现,资源池正在运行一个收割者线程,该线程每秒都会启动一次,以检查它是否可以清理一些资源。我们可以用这个简单的程序模拟相同的行为

模块主目录
导入控制。并发
进口管制.单子(永远)
main::IO()
main=forever$do
线程延迟(10^6)

如果我将-B传递给运行时系统,每当发出GC时,我都会收到音频反馈,在本例中是每60秒一次

因此,当我通过将-I0传递给在进程上运行strace命令的RTS来抑制这些GC循环时,只会产生大约70K个大型日志文件。由于该进程还运行一个scotty服务器,所以当请求传入时会触发GC,因此它们似乎在我实际需要它们时发生

由于我们将在明年大量增加这台机器上Haskell进程的数量,我想知道如何将它们的空闲时间保持在合理的水平。显然,传递-I0似乎是一个相当糟糕的主意(?)。另一个想法是将功能的数量从12个减少到大约4个。有没有其他方法来调整RTS,这样我就可以避免进程在空闲时消耗大量CPU周期

按照GHC内存管理的结构,为了控制内存使用,在程序运行期间,需要周期性地使用“主要GC”。这是一个相对昂贵的操作,它“阻止了世界”——在这一过程中,该计划没有取得任何进展

显然,在程序执行的任何关键点发生这种情况都是不可取的。因此,默认情况下,每当GHC编译的程序空闲时,都会执行主要GC。这通常是一种低调的方法,可以在不中断程序交互的情况下降低垃圾级别,提高总体内存效率和性能。这被称为“空闲GC”

但是,在这样的场景中,这可能会成为一个问题:许多并发进程,每个进程都会频繁唤醒,运行很短时间,然后又回到空闲状态。这是服务器进程的常见场景。在这种情况下,当空闲GC启动时,它不会阻止正在运行的进程(该进程已完成其工作),但会从系统上运行的其他进程中窃取资源。由于程序经常空闲,因此没有必要在每次空闲时都产生主要GC的开销

“暴力”方法是将RTS选项-I0传递给程序,完全禁用空闲GC。这将在短期内解决这一问题,但错过了收集垃圾的机会。这可能会导致垃圾堆积,导致GC在不合适的时候启动

部分是为了回答这个问题,GHC运行时系统中添加了标志-Iw。这建立了允许闲置地面军事系统运行的最小间隔。例如,-Iw5将不会运行空闲GC,直到自上次GC以来已过5秒,即使程序多次空闲。这应该可以解决问题

请记住GHC用户指南中的警告:

这是一个实验性功能,请告诉我们它是否会导致问题和/或可能从进一步调整中受益

哈斯凯林快乐

发表评论