rai*_*hoo 24 concurrency multithreading garbage-collection haskell ghc
我在一个拥有12个内核的系统上运行了几个Haskell进程.所有进程都-threaded
使用12种功能进行编译和运行.他们都使用的一个库resource-pool
保存了一个数据库连接池.
有趣的是,即使所有进程实际上都处于空闲状态,它们也会占用大约2%的CPU时间.检查这些进程之一strace -p $(pgrep processname) -f
表明该进程正在进行不合理的系统调用,即使它实际上不应该做任何事情.把事情放到透视中:
-N2
5秒会生成66K日志文件.-N64
产生60 兆字节的日志.因此,功能的数量会增加大量发布的系统调用量.
深入挖掘,我们发现resource-pool
正在运行一个收割机线程,它会每秒触发一次,以检查它是否可以清理一些资源.我们可以用这个简单的程序模拟相同的行为.
module Main where
import Control.Concurrent
import Control.Monad (forever)
main :: IO ()
main = forever $ do
threadDelay (10 ^ 6)
Run Code Online (Sandbox Code Playgroud)
如果我传递-B
给运行时系统,每当发出GC时我都会得到音频反馈,在这种情况下每隔60秒就会发出一次.
因此,当我通过传递-I0
给RTS来抑制这些GC周期时,strace
在该进程上运行命令只会产生大约70K的大型日志文件.由于该进程也在运行scotty
服务器,因此在请求进入时会触发GC,因此它们似乎在我真正需要它时发生.
由于我们将在明年的过程中大量增加这台机器上的Haskell进程数量,我想知道如何将它们的空闲时间保持在合理的水平.显然,过往-I0
似乎是一个相当糟糕的主意(?).另一个想法是将功能的数量从12减少到类似4的东西.有没有其他方法来调整RTS以便我可以在空闲时保持进程从刻录到多个CPU周期?
GHC 内存管理的结构方式是,为了控制内存使用,在程序运行期间定期需要一次“主要 GC”。这是一个相对昂贵的操作,并且它“让世界停止”——在这种情况发生时程序没有任何进展。
显然,在程序执行的任何关键点上都不希望发生这种情况。因此,默认情况下,每当 GHC 编译的程序空闲时,就会执行一次主要 GC。这通常是一种在不中断程序交互的情况下降低垃圾级别并提高整体内存效率和性能的低调方法。这称为“空闲 GC”。
然而,在这样的场景中,这可能会成为一个问题:许多并发进程,每个进程都频繁唤醒,运行很短的时间,然后又回到空闲状态。这是服务器进程的常见场景。在这种情况下,当空闲 GC 启动时,它不会阻塞正在运行的进程(该进程已完成其工作),但它会从系统上运行的其他进程窃取资源。由于程序频繁空闲,因此没有必要在每次空闲时都产生一次 Major GC 的开销。
“强力”方法是将 RTS 选项传递-I0
给程序,完全禁用空闲 GC。这将在短期内解决这个问题,但会错过收集垃圾的机会。这可能会导致垃圾堆积,导致 GC 在不合时宜的时刻启动。
部分为了回答这个问题,该标志-Iw
被添加到 GHC 运行时系统中。这建立了允许空闲 GC 运行的最小间隔。例如,-Iw5
即使程序空闲了几次,直到上次 GC 过去 5 秒后才会运行空闲 GC。这应该可以解决问题。
请记住GHC 用户指南中的警告:
这是一项实验性功能,如果它会导致问题和/或可以从进一步调整中受益,请告知我们。
哈斯克尔快乐!
归档时间: |
|
查看次数: |
314 次 |
最近记录: |