Ich habe in Haskell einen Daemon geschrieben, der alle fünf Minuten Informationen von einer Webseite löscht.
Der Daemon lief ursprünglich ungefähr 50 Minuten lang, aber dann starb er unerwartet mit out of memory (requested 1048576 bytes)
. Jedes Mal, wenn ich es lief, starb es nach der gleichen Zeit. Wenn Sie ihn nur für 30 Sekunden in den Ruhezustand setzen, ist er nach 8 Minuten verstummt.
Ich erkannte, dass der Code, um die Website zu scrappen, unglaublich speicherineffizient war (von etwa 30 Millionen beim Schlafen bis 250 Millionen beim Analysieren von 9 Millionen HTML), also schrieb ich es um, so dass es jetzt nur 15 Millionen mehr beim Parsen verbraucht. Als ich dachte, das Problem sei behoben, rannte ich den Dämon über Nacht, und als ich aufwachte, brauchte ich weniger Speicherplatz als in dieser Nacht. Ich dachte, ich wäre fertig, aber etwa 20 Stunden nach dem Start war es mit dem gleichen Fehler abgestürzt.
Ich habe angefangen, ghc profiling zu untersuchen, aber ich konnte das nicht zur Arbeit bringen. Als nächstes fing ich an, mit rts zu experimentieren, und ich versuchte es zu ändern -H64m
, um die Standard-Heap-Größe so festzulegen, dass sie größer ist als von meinem Programm verwendet wurde. Außerdem wurde -Ksize
verwendet, um die maximale Größe des Stacks zu verkleinern, um zu sehen, ob dies früher zum Absturz führen würde.
Trotz jeder Änderung, die ich gemacht habe, scheint der Daemon immer noch nach einer konstanten Anzahl von Iterationen abzustürzen. Wenn das Parsing effizienter wird, ist dieser Wert höher, aber es stürzt immer noch ab. Das macht für mich keinen Sinn, weil keine von diesen Läufen überhaupt meinen gesamten Speicher verwendet, geschweige denn Swap Space. Die Größe des Heapspeichers sollte standardmäßig unbegrenzt sein, das Verkleinern der Stackgröße hat keinen Unterschied gemacht, und alle meine ulimits sind entweder unbegrenzt oder wesentlich höher als das, was der Daemon verwendet.
Im ursprünglichen Code habe ich den Absturz irgendwo im HTML-Parsing lokalisiert, aber ich habe nicht das selbe für die speichereffizientere Version getan, weil 20 Stunden so lange dauern. Ich weiß nicht, ob das überhaupt nützlich wäre, weil es nicht so aussieht, als ob ein bestimmter Teil des Programms defekt wäre, weil es vor dem Absturz Dutzende von Iterationen erfolgreich ausgeführt hat.
Aus Ideen heraus sah ich sogar den ghc-Quellcode durch Dieser Fehler, und es scheint ein fehlgeschlagener Aufruf von mmap zu sein, was für mich nicht sehr hilfreich war, weil ich annehme, dass das nicht die Wurzel des Problems ist.
(Bearbeiten: Code neu geschrieben und an das Ende des Posts verschoben)
Ich bin ziemlich neu bei Haskell, also hoffe ich, dass dies eine Art Verrücktheit der faulen Bewertung oder etwas anderes ist, das eine schnelle Lösung hat. Ansonsten habe ich keine Ideen mehr.
Ich benutze GHC Version 7.4.2 auf FreeBsd 9.1
Bearbeiten:
Durch das Ersetzen des Downloads durch statisches html wurde das Problem beseitigt, also habe ich mich darauf beschränkt, wie ich http-Conduit nutze. Ich habe den obigen Code bearbeitet, um meinen Netzwerkcode einzuschließen. Die hackage docs erwähnen, einen Manager zu teilen, also habe ich das getan. Und es sagt auch, dass für http
explizit Verbindungen geschlossen werden müssen, aber ich glaube nicht, dass ich das für httpLbs
tun muss.
Hier ist mein Code.
%Vor%und es wird ausgegeben:
%Vor%Die Beseitigung der Regex-Berechnungen hat das Problem behoben, aber es scheint, dass der Fehler nach der Vernetzung und während der Regex passiert, vermutlich wegen etwas, das ich falsch mit http-Conduel mache. Irgendwelche Ideen?
Auch wenn ich versuche, mit aktivierter Profilerstellung zu kompilieren, bekomme ich diesen Fehler:
%Vor% Tatsächlich habe ich keine Profiling-Bibliotheken für http-conduit
installiert und ich weiß nicht wie.
Ich habe mein eigenes Problem gelöst. Es scheint ein GHC-Bug auf FreeBSD zu sein. Ich habe einen Fehlerbericht eingereicht und bin zu Linux gewechselt, und jetzt läuft es die letzten Tage einwandfrei.
Sie haben also ein Leck gefunden. Indem Sie mit Compiler-Optionen und Speichereinstellungen tricksen, können Sie nur den Moment verschieben, in dem Ihr Programm abstürzt, aber Sie können die Ursache des Problems nicht beseitigen, so dass Sie, egal was Sie dort einstellen, nicht genügend Speicher haben.
Ich empfehle Ihnen, vorsichtig durch den ganzen nicht-reinen Code zu gehen und vor allem den Teil, der mit Ressourcen arbeitet. Überprüfen Sie, ob alle Ressourcen korrekt freigegeben werden. Überprüfe, ob du einen akkumulierenden Zustand hast, wie einen wachsenden Ubounded-Kanal. Und natürlich, wie von nm vorgeschlagen, profile es .
Ich habe einen Scraper, der Seiten ohne Pause analysiert und Dateien herunterlädt, und das alles gleichzeitig. Ich habe es noch nie mit mehr Speicher als ~ 60M verwendet. Ich habe es mit GHC 7.4.2, GHC 7.6.1 und GHC 7.6.2 kompiliert und hatte keine Probleme mit beiden.
Es sollte angemerkt werden, dass der Grund Ihres Problems auch in den Bibliotheken sein kann, die Sie verwenden.
In meinem Schaber verwende ich http-conduit
, http-conduit-browser
, HandsomeSoup
und HXT
.