Ich habe den folgenden Code, der abgespeckt wurde und ich denke, so minimal wie möglich, dass einige sehr seltsame Verhalten hat.Unerwartetes Speicherwachstum mit Control.Monad foldM
Der Code besteht aus zwei Quelldateien: man einige Daten zu definieren:
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 }
Und das andere ist Main:
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()
Der obige Code wie (zusammengestellt 7.10.3 mit GHC mit Stack 1.0.0 mit Standard-Flags) hat einen schnellen Anstieg der Speicherauslastung (über 1 GB) und dauert in der Regel 3,3 Sekunden.
Wenn ich eine Änderung an den Code, zum Beispiel:
- Verwenden Sie eine der kommentierten Alternativen zum Problem Linie
- aus jeder Zeile herausnehmen
runFunction
Die Speichernutzung wird minimal bleiben und dauert nur etwa 1 Sekunde.
Ein Merkmal, das ich denke, ist sehr überraschend für mich ist, dass foldM
mit foldlM
ersetzen (was soweit ich foldM = foldlM
wissen) das Problem behebt.
Auch Änderungen an Code, die ich nicht sehe, hat eine Beziehung zu den Problemzeilen des Codes auch das Problem behebt. Zum Beispiel Entfernen der letzten putStrLn.
Eine weitere Kuriosität ist, dass, wenn ich das MyFunction Modul in das Hauptmodul fusionieren, während es das Problem nicht beheben, es foldlM
tatsächlich verursacht als foldM
mit übermäßigen Speichern verhalten.
In dem realen Code, der dies herkam, habe ich eine große Anzahl exampleFunction
s, und es ist deutlich mehr Main
Code, und jedes begegnet mir so oft diese Art von unerklärlicher Speichernutzung von Funktionen, die in der Regel gelöst werden können durch eine Art Voodoo.
Ich bin auf der Suche nach einer Erklärung für das Verhalten. Wenn ich weiß, warum das passiert, kann ich dann versuchen, es zu vermeiden. Könnte das ein Compiler-Problem oder vielleicht nur ein Missverständnis sein?
(*) Ich habe das sekundäre Problem hervorgehoben, das das gleiche Speicherwachstum mit FoldlM verursacht.
Die Tatsache, dass sich Dinge zwischen den Modulen auf das Verhalten * stark * auswirken, deutet darauf hin, dass GHCs Inliner beteiligt ist. Eine Transformation, die durch Inlining ermöglicht wird, ist entweder * Ihnen * oder * Ihnen * schaden. – dfeuer
ein nicht verwandtes Problem ist Ihre Arithmetik in exampleFunction wird standardmäßig auf Integer, die ziemlich langsam sein wird. Schalten Sie "-Wall" ein. – jberryman
@jberryman exampleFunction soll nur ein Beispiel sein - um den Speicherzuwachs zu zeigen. – pticawr