Así es como se define serialización de listas:
instance Binary a => Binary [a] where
put l = put (length l) >> mapM_ put l
Es decir, serializar en primer lugar la longitud de la lista, a continuación, serializar la propia lista.
Para conocer la longitud de la lista, debemos evaluar toda la lista. Pero no podemos recoger basura, porque sus elementos son necesarios para la segunda parte , mapM_ put l
. Por lo tanto, toda la lista debe almacenarse en la memoria después de evaluar la longitud y antes de que comience la serialización de los elementos.
Así es como el perfil del montón se parece a:
Aviso cómo crece mientras que la lista está siendo construido para calcular su longitud, y luego disminuye mientras que los elementos están en serie y pueden ser recogidos por el GC.
Entonces, ¿cómo solucionarlo? En tu ejemplo, ya sabes la longitud. Por lo que puede escribir una función que toma la longitud conocida, a diferencia de la computación que:
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
Este programa se ejecuta dentro de 53k de espacio de almacenamiento dinámico.
También puede incluir una función de seguridad en putWithLength
: calcule la longitud mientras serializa la lista, y verifique con el primer argumento al final. Si hay una discrepancia, arroje un error.
Ejercicio: ¿por qué aún necesita pasar la longitud a putWithLength
en lugar de usar el valor calculado como se describe arriba?
En general, GHCi no es una buena opción para ningún tipo de perfil. No realiza ningún tipo de optimización e interpreta el código Haskell, por lo que el uso de la memoria y las características de rendimiento a menudo son completamente diferentes en comparación con el compilado Haskell. Debe compilar con 'ghc -O2' y ver si el problema se repite allí. – shang
Encontré el problema en un programa compilado con ghc -O2, y trabajé en esto. –