Haskell: Leistung von IORefs

8

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

    
hpacheco 05.06.2014, 19:13
quelle

3 Antworten

14

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:

%Vor%

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:

%Vor%

Dies läuft in konstantem Abstand in ungefähr 0,7 Sekunden auf meinem Rechner.

    
Gabriel Gonzalez 05.06.2014, 23:20
quelle
14

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

%Vor%

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 :

%Vor%     
luqui 05.06.2014 19:22
quelle
2

Ich habe herausgefunden, dass der Hack zu einer Lösung darin besteht, stattdessen einen faulen mapM zu verwenden, der als

definiert ist %Vor%

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:

%Vor%

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? :)

    
hpacheco 05.06.2014 23:04
quelle

Tags und Links