Ich habe versucht, einen Algorithmus in Haskell zu codieren, der viele veränderbare Referenzen benötigt, aber er ist (vielleicht nicht überraschend) sehr langsam im Vergleich zum reinen Lazy Code. Betrachten Sie ein sehr einfaches Beispiel:
%Vor% Auf meinem Rechner läuft GHC 7.8.2, main1
braucht 1.2s und verwendet 290MB Speicher, während main2
nur 0.4s benötigt und nur 1MB benutzt. Gibt es einen Trick, um dieses Wachstum zu verhindern, besonders im Weltraum? Ich brauche oft IORef
s für nicht-primitive Typen im Gegensatz zu Int
und nehme an, dass ein IORef
einen zusätzlichen Zeiger verwenden würde, ähnlich einem normalen Thunk, aber meine Intuition scheint falsch zu sein.
Ich habe bereits einen spezialisierten Listentyp mit einem entpackten IORef
versucht, aber ohne signifikanten Unterschied.
Danke
Das Problem ist die Verwendung von mapM
, die sowohl auf Zeit als auch auf Platz immer auf großen Listen schlecht funktioniert. Die richtige Lösung besteht darin, die Zwischenlisten mit mapM_
und (>=>)
zu verschmelzen:
Dies läuft in konstantem Abstand und bietet eine hervorragende Leistung, läuft in 0,4 Sekunden auf meinem Rechner.
Bearbeiten: Als Antwort auf Ihre Frage können Sie dies auch mit pipes
tun, damit Sie die Schleife nicht manuell fusionieren müssen:
Dies läuft in konstantem Abstand in ungefähr 0,7 Sekunden auf meinem Rechner.
Dies ist sehr wahrscheinlich nicht über IORef
, sondern über Strenge. Aktionen in der IO
-Monade sind seriell - alle vorherigen Aktionen müssen abgeschlossen sein, bevor die nächste gestartet werden kann. Also
erzeugt eine Million IORef
s, bevor etwas gelesen wird.
Allerdings
%Vor% was sehr gut streamt, so dass wir print
ein Element der Liste, dann das nächste usw. erzeugen.
Wenn Sie einen faireren Vergleich wünschen, verwenden Sie eine strikte map
:
Ich habe herausgefunden, dass der Hack zu einer Lösung darin besteht, stattdessen einen faulen mapM
zu verwenden, der als
Damit kann die monadische Version innerhalb der gleichen 1 MB und ähnlichen Zeit ausgeführt werden. Ich würde erwarten, dass eine faule ST
-Monade dieses Problem eleganter lösen könnte, ohne unsafeInterleaveIO
als Funktion zu verwenden:
aber das funktioniert nicht (Sie müssen auch unsafeInterleaveST
verwenden), was mich darüber nachdenkt, wie faul das Control.Monad.ST.Lazy
wirklich ist. Weiß jemand das? :)
Tags und Links haskell