Zuerst einige allgemeine Codebereinigung:
In Ihrer Funktion %code% ordnen Sie explizit veränderbare Vektoren zu und füllen diese. Für diesen genauen Zweck gibt es jedoch eine vorgefertigte Funktion, nämlich %code% . %code% kann daher als
umgeschrieben werden %Vor%Ebenso kann der Graph-Generierungscode durch %code% und %code% ersetzt werden:
%Vor%Beachten Sie, dass dadurch jegliche Notwendigkeit für eine Mutation vollständig beseitigt wird, ohne dass eine Leistung verloren geht.
Nun zu den eigentlichen Fragen:
Meiner Erfahrung nach ist %code% sehr nützlich, aber nur als schnelle Lösung für Platzlecks wie diese. Das grundlegende Problem besteht nicht darin, dass Sie die Ergebnisse erzwingen müssen, nachdem Sie sie erstellt haben. Stattdessen bedeutet die Verwendung von %code% , dass Sie die Struktur von Anfang an strikter aufgebaut haben sollten. In der Tat, wenn Sie ein Knallmuster in Ihrem Vektorerstellungscode wie folgt hinzufügen:
%Vor%Dann ist das Problem behoben ohne %code% . Beachten Sie, dass dies nicht mit dem %code% -Code funktioniert, da %code% aus irgendeinem Grund (ich könnte eine Feature-Anfrage dafür erstellen) keine strikten Funktionen für umrahmte Vektoren bietet. Wenn ich jedoch in der Antwort auf Frage 3 zu ungekoppelten Vektoren komme, die streng sind, arbeiten beide Ansätze ohne Striktheitsannotationen.
Soweit ich weiß, ist das Muster der wiederholten Erzeugung neuer Vektoren idiomatisch. Das einzige, was nicht idiomatisch ist, ist die Verwendung von Mutabilität - außer wenn sie unbedingt notwendig sind, werden mutable Vektoren im Allgemeinen davon abgeraten.
Es gibt ein paar Dinge zu tun:
Am einfachsten können Sie %code% durch %code% ersetzen. Da dies nicht wirklich der langsame Punkt der Funktion ist, spielt dies keine Rolle, aber %code% kann bei hohen Arbeitslasten viel schneller sein.
Sie können zur Verwendung von nicht eingebetteten Vektoren wechseln. Obwohl der äußere Vektor eingerahmt bleiben muss, kann der innere Vektor sein, da Vektoren von Vektoren nicht entkoppelt werden können. Dies löst auch Ihr Striktheitsproblem - weil ungeschachtelte Vektoren in ihren Elementen streng sind, erhalten Sie kein Platzleck. Beachten Sie, dass dies auf meinem Computer die Leistung von 4,1 Sekunden auf 1,3 Sekunden verbessert, so dass das Unboxing sehr hilfreich ist.
Sie können den Vektor in einen einzigen reduzieren und Multiplikation und Division verwenden, um zwischen zweidimensionalen Indizes und einzelnen Indizes umzuschalten. Ich empfehle das nicht, da es ein bisschen involviert, ziemlich hässlich ist und aufgrund der Aufteilung den Code auf meinem Rechner verlangsamt.
Sie können %code% verwenden. Dies hat den großen Vorteil, dass Sie Ihren Code automatisch parallelisieren. Beachten Sie, dass %code% seine Arrays abflacht und die Divisionen, die zum Ausfüllen benötigt werden, anscheinend nicht korrekt entfernt (es ist möglich, verschachtelte Loops zu verwenden, aber ich denke, dass es eine einzelne Schleife und eine Division verwendet) gleiche Leistungseinbußen wie oben erwähnt, die Laufzeit von 1,3 Sekunden auf 1,8 bringen. Wenn Sie jedoch Parallelität aktivieren und einen Multicore-Computer verwenden, sehen Sie einige Vorteile. Unglücklicherweise ist dein aktueller Testfall zu klein, um viel Nutzen zu sehen. Also sehe ich ihn auf meiner 6-Kern-Maschine auf 1,2 Sekunden zurückfallen. Wenn ich die Größe wieder auf %code% anstelle von %code% zurückstelle, wird sie durch die Parallelität von 32 Sekunden auf 13 erhöht. Vermutlich, wenn Sie diesem Programm eine größere Eingabe geben, sehen Sie möglicherweise mehr Nutzen.
Wenn Sie interessiert sind, habe ich meine %code% -ifizierte Version hier gepostet.
BEARBEITEN: Verwende %code% . Testen auf meinem Computer, mit %code% , bekomme ich 14,7 Sekunden ohne Parallelität, was fast so gut ist wie ohne %code% und mit Parallelität. Im Allgemeinen kann LLVM arraybasierten Code wie diesen sehr gut verarbeiten.
Ich wollte eine effiziente Implementierung des Floyd-Warshall-Algorithmus für alle Paare in Haskell mit %code% s schreiben, um eine gute Leistung zu erzielen.
Die Implementierung ist recht einfach, aber anstatt ein 3-dimensionales | V | × | V | × | V | zu verwenden Matrix wird ein 2-dimensionaler Vektor verwendet, da wir immer nur den vorherigen %code% -Wert gelesen haben.
Somit ist der Algorithmus wirklich nur eine Reihe von Schritten, bei denen ein 2D-Vektor übergeben wird und ein neuer 2D-Vektor erzeugt wird. Der letzte 2D-Vektor enthält die kürzesten Wege zwischen allen Knoten (i, j).
Meine Intuition sagte mir, dass es wichtig wäre, sicherzustellen, dass der vorherige 2D-Vektor vor jedem Schritt evaluiert wurde, also benutzte ich %code% für das %code% Argument für die %code% Funktion und das strikte %code% :
%Vor%Wenn Sie dieses Programm jedoch mit einem 1000-Node-Graphen mit 47978 Kanten ausführen, sieht die Sache gar nicht gut aus. Die Speicherauslastung ist sehr hoch und das Programm dauert viel zu lange. Das Programm wurde mit %code% kompiliert.
Ich habe das Programm für das Profiling neu erstellt und die Anzahl der Iterationen auf 50 begrenzt:
%Vor%Ich habe dann das Programm mit %code% und %code% :
ausgeführt
Das ist ... interessant, aber ich denke, es zeigt, dass es Tonnen von Thunks sammelt. Nicht gut.
Ok, also habe ich nach einigen Aufnahmen im Dunkeln ein %code% in %code% hinzugefügt, um sicherzustellen, dass %code% wirklich ausgewertet wird:
%Vor%Jetzt sieht die Sache besser aus, und ich kann das Programm tatsächlich mit konstanter Speicherauslastung ausführen. Es ist offensichtlich, dass der Knall auf dem Argument %code% nicht genug war.
Zum Vergleich mit den vorherigen Graphen ist hier die Speicherbelegung für 50 Iterationen nach dem Hinzufügen von %code% :
Ok, also sind die Dinge besser, aber ich habe noch ein paar Fragen:
Für Referenzen, ist hier %code% : Ссылка
Hier ist %code% :
%Vor%