haskell - 从源代码编译时与链接到包时不同的空间行为
问题描述
此代码是否泄漏取决于我如何编译相同的 RWS 实现:
import Control.Monad (replicateM_)
import qualified Control.Monad.RWS.CPS as RWS
import Data.Monoid (Sum (..))
-- import qualified RWS
{-# ann module "HLint: ignore Use camelCase" #-}
count_a_lot :: Int -> RWS.RWS () (Sum Int) () ()
count_a_lot = flip replicateM_ count
count :: RWS.RWS () (Sum Int) () ()
count = RWS.tell . Sum $ 1
main :: IO ()
main = print . snd $ RWS.evalRWS (count_a_lot 10000000) () ()
我尝试了两种使用 RWS 的 CPS 版本的方法:
import qualified Control.Monad.RWS.CPS as RWS
使用writer-cps-transformers
来自 nixpkgs 的包。import qualified RWS
使用Internal.hs,逐字复制RWS.hs
到与上面粘贴的文件相同的本地目录。
如果我编译上面的代码并运行它,我可以看到它在恒定空间中运行。
如果我更改导入以编译并链接RWS.hs
import Control.Monad (replicateM_)
-- import qualified Control.Monad.RWS.CPS as RWS
import Data.Monoid (Sum (..))
import qualified RWS
我得到空间泄漏。黑色的固定器有(103)<*>.\
,蓝色的是SYSTEM
为什么会这样?或者我应该如何最好地调试这个?
在这两种情况下,我都使用 ghc(无 cabal)和-Wall -O2 -prof -fprof-auto -rtsopts -fexternal-interpreter
. 如果相关,我可以发布更详细的编译器调用和输出行。
使用ghc-8.4.4
,transformers-0.5.5.0
和writer-cps-transformers-0.1.1.4
. 我知道这些版本不是最新的,但我有兴趣知道发生了什么,而不是解决实际的泄漏问题,所以我认为这些版本并不相关。
解决方案
事实上,罪魁祸首是分析系统。请注意,您实际上不需要-prof
检测空间泄漏——RTS 选项-s
可以在没有它的情况下打印出“总内存”测量值。不太科学,空间泄漏让程序慢了很多,你可以感受一下。有了这个,我发现禁用-prof
会将“本地”版本的内存使用量减少到 2MB(与“库”版本相同),并将其保留为已使用的 ~1.9GB。
分析可以使事情变慢的原因是因为 GHC 也拒绝优化。它不能再积极地重构你编写的代码,因为成本中心对代码意味着某种结构,有时优化后成本中心没有好地方,从而阻碍了这种优化。准确地知道这里出了什么问题需要知道您构建库时使用的标志,但高级解释是该writer-cps-transformers
库是用比文件更积极的分析(因此更积极的优化)构建的RWS.hs
。-fprof-auto
是一个非常激进的分析选项,它可以轻松破坏许多优化。如果我使用writer-cps-transformers
build with -fprof-auto
,我会遇到同样的问题。如果我使用RWS.hs
没有分析或使用较弱的东西,比如-fprof-auto-exported
,然后我解决了这个问题。
推荐阅读
- java - 如何对地图进行排序和编辑?
- java - 查找和更改数组 Java OOP 的索引
- typescript - 使用 Jest/Typescript 和虚拟函数测试 fs 库函数
- ruby - Mongodb ruby 驱动:编辑 Collection::View 实例过滤器
- javascript - 合并 UI Grid 中的特定列
- php - nginx + php-fpm 产生 300ms 开销
- css - 如何将表单字段与文本内联?
- c# - 如果在加载时更改内容,则 StackPanel 中的 WPF 标签不会调整大小
- node.js - 从 readline 的线路回调中进行异步调用?
- embedded-linux - dbus-daemon --session 给出循环包含错误