gpt4 book ai didi

haskell - Control.Monad foldM 意外的内存增长

转载 作者:行者123 更新时间:2023-12-03 15:10:35 25 4
gpt4 key购买 nike

我有以下代码,这些代码已被剥离,我认为尽可能少地有一些非常奇怪的行为。

代码由两个源文件组成:
一个定义一些数据:

module MyFunction where

data MyFunction =
MyFunction {
functionNumber :: Int,
functionResult :: IO String
}

makeMyFunction :: Show a => Int -> IO a -> MyFunction
makeMyFunction number result = MyFunction {
functionNumber = number,
functionResult = result >>= return . show }

另一个是主要的:
module Main (main) where

import System.CPUTime (getCPUTime)
import Data.List (foldl')
import Data.Foldable (foldlM)
import Control.Monad (foldM)
import MyFunction

exampleFunction = do
--let x = foldl' (\a b -> a `seq` (a + b)) 0 [1..20000000] -- This works
--x <- foldlM (\a b -> a `seq` return (a + b)) 0 [1..20000000] -- This works (*)
x <- foldM (\a b -> a `seq` return (a + b)) 0 [1..20000000] -- This doesn't
print x
return ()

runFunction fn = do
result <- functionResult fn
duration <- getCPUTime
if result /= "()"
then putStrLn ""
else return ()
putStrLn (show (fromIntegral duration / (10^9)) ++ "ms")
return fn

main = do
runFunction (makeMyFunction 123 exampleFunction)
return ()

上面的代码(使用带有默认标志的堆栈 1.0.0 的 GHC 7.10.3 编译)的内存使用量迅速增加(超过 1GB),通常需要 3.3 秒。

如果我对代码进行更改,例如:
  • 使用问题行
  • 的注释替代方法之一
  • runFunction中取出任何一行

  • 内存使用量将保持最小,并且只需要大约 1 秒。

    我认为最令我惊讶的一个功能是替换 foldMfoldlM (据我所知 foldM = foldlM )解决了这个问题。

    此外,对我看不到的代码进行更改与问题代码行有任何关系也可以解决问题。例如删除最后一个 putStrLn。

    另一个奇怪的是,如果我将 MyFunction 模块合并到 Main 模块中,虽然它不能解决问题,但实际上会导致 foldlM表现得像 foldM使用过多的内存。

    在这个来自的真实代码中,我有一个大数字 exampleFunction s,还有更多 Main代码,而且我经常遇到这种无法解释的函数内存使用情况,通常可以通过某种巫术来解决。

    我正在寻找行为的解释。如果我知道为什么会发生这种情况,我可以考虑避免它。这可能是编译器问题,还是只是我的误解?

    (*) 我已经强调了导致与 foldlM 相同的内存增长的次要问题。

    最佳答案

    这里是 foldlM来自 Foldable.hs (ghc)

    -- | Monadic fold over the elements of a structure,
    -- associating to the left, i.e. from left to right.
    foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
    foldlM f z0 xs = foldr f' return xs z0
    where f' x k z = f z x >>= k

    foldM来自 Monad.hs
    foldM          :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
    {-# INLINEABLE foldM #-}
    {-# SPECIALISE foldM :: (a -> b -> IO a) -> a -> [b] -> IO a #-}
    {-# SPECIALISE foldM :: (a -> b -> Maybe a) -> a -> [b] -> Maybe a #-}
    foldM = foldlM

    我将这些定义放在一个单独的模块测试中,并测试了使用和不使用 INLINEABLE/SPESIALISE 行的执行。不管是什么原因,省略 SPECIALIZE 指令有帮助,执行时间和内存使用与 foldlM 一样。

    经过一点点挖掘,移除线
    {-# SPECIALISE foldM :: (a -> b -> IO a) -> a -> [b] -> IO a #-}

    影响最大。

    关于haskell - Control.Monad foldM 意外的内存增长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34830900/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com