Hier ist, wie die Serialisierung von Listen definiert:
instance Binary a => Binary [a] where
put l = put (length l) >> mapM_ put l
Das heißt, zuerst die Länge der Liste serialisiert werden, dann serialisiert die Liste selbst.
Um die Länge der Liste herauszufinden, müssen wir die gesamte Liste auswerten. Aber wir können es nicht Müll-sammeln, weil seine Elemente für die zweite Teil, mapM_ put l
benötigt werden. Daher muss die gesamte Liste im Speicher abgelegt werden, nachdem die Länge ausgewertet wurde und bevor die Elementserialisierung gestartet wird.
Hier ist, wie das Heap-Profil wie folgt aussieht:
Beachten Sie, wie es wächst, während die Liste erstellt wird seine Länge zu berechnen, und nimmt dann während der Elemente serialisiert werden und können durch gesammelt werden der GC.
Also, wie das zu beheben? In Ihrem Beispiel kennen Sie die Länge bereits. So Sie können eine Funktion schreiben, die die bekannte Länge nimmt, im Gegensatz zur Berechnung es:
import Data.Binary
import Data.ByteString.Lazy as L
import qualified Data.ByteString as B
import Data.Binary.Put
main = do
let len = 10000001 :: Int
bs = encodeWithLength len [0..len-1]
L.writeFile "test" bs
putWithLength :: Binary a => Int -> [a] -> Put
putWithLength len list =
put len >> mapM_ put list
encodeWithLength :: Binary a => Int -> [a] -> ByteString
encodeWithLength len list = runPut $ putWithLength len list
Dieses Programm läuft innerhalb 53k von Heap-Speicher.
Sie können auch eine Sicherheitsfunktion in putWithLength
einfügen: Berechnen Sie die Länge beim Serialisieren der Liste, und überprüfen Sie am Ende das erste Argument. Wenn es eine Abweichung gibt, einen Fehler werfen.
Übung: Warum müssen Sie immer noch die Länge an putWithLength
übergeben anstatt den berechneten Wert wie oben beschrieben zu verwenden?
Im Allgemeinen ist GHCi keine gute Wahl für jede Art von Profilerstellung. Es führt keinerlei Optimierung durch und interpretiert Haskell-Code, so dass die Speicherauslastung und die Leistungsmerkmale oft völlig anders sind als bei kompiliertem Haskell. Sie sollten mit 'ghc -O2' kompilieren und sehen, ob sich das Problem dort wiederholt. – shang
Ich habe das Problem in einem Programm gefunden, das mit ghc -O2 kompiliert wurde, und habe es bis hierhin durchgearbeitet. –