Wann ist ein guter Zeitpunkt, um über die Leistung in Haskell nachzudenken?

8

Simon Peyton Jones selbst erkennt an, dass es aufgrund der nicht strengen Semantik schwierig ist, über die Leistung in Haskell nachzudenken.

Ich habe noch kein bedeutendes Projekt in haskell geschrieben, daher frage ich mich: Kann ich erst zu Beginn eines Projekts (bei der Auswahl grundlegender Datenstrukturen und der E / A-Bibliothek) über die Leistung nachdenken und mich bei Problemen mit dem Profiler befassen?

Anders ausgedrückt: Ist es möglich (dh nicht zu schmerzhaft), den Umgang mit der Leistung zu verschieben, wenn Sie Leistungsprobleme haben, oder müssen Sie lernen, vorherzusagen, wie GHC Ihren Code ausführen wird (zum Beispiel: Schliessen Sie, was der Strenge-Analysator entscheidet )?

Simon Bergot
quelle
Wenn es auf Leistung ankommt.
Thomas Eding

Antworten:

7

Die anderen Antworten enthalten umfassende Hinweise zur Leistungsüberlegung. Diese Antwort befasst sich speziell mit nicht strenger Semantik.

Faulheit macht es zwar schwieriger, über Leistung nachzudenken, ist aber nicht so kompliziert, wie Sie vielleicht denken. Obwohl Faulheit in einigen Situationen sehr nützlich ist, wird eine faule Sprache meistens genauso verwendet wie eine strenge Sprache. Folglich kann die Leistungsüberlegung für strenge Sprachen (mit einigen Anpassungen) auf faule Sprachen angewendet werden.

In Bezug auf die zeitliche Komplexität macht eine eifrige Bewertung streng mehr Arbeit als eine verzögerte Bewertung. Beide Strategien führen in den meisten Fällen zum gleichen Ergebnis. (Genauer gesagt, wenn bei der eifrigen Auswertung keine Fehler auftreten, führt dies zu demselben Ergebnis wie bei der verzögerten Auswertung.) Um über die zeitliche Komplexität eines Haskell-Programms nachzudenken, können Sie so tun, als würde es eifrig auswerten. In den seltenen Situationen, in denen Faulheit wichtig ist, ist diese Schätzung zu hoch und sollte nach unten korrigiert werden.

Während eine verzögerte Auswertung eine geringere zeitliche Komplexität ergibt als eine eifrige Auswertung, führt dies manchmal zu einer höheren Raumkomplexität, dh zu Platzlecks. Eine höhere Komplexität des Speicherplatzes kann durch Hinzufügen von Strenge-Anmerkungen behoben werden, damit ein Programm schneller ausgeführt wird. Profiling-Tools sind ziemlich gut darin, die Ursache von Speicherplatzlecks aufzuspüren. Ich würde dies je nach Schweregrad entweder als Korrektheits-Debugging oder als Leistungs-Debugging kategorisieren.

Kühlkörper
quelle
In terms of time complexity, eager evaluation does strictly more work than lazy evaluation. Worauf stützen Sie diese Behauptung? Unter der Annahme, dass die eifrige Auswertung an derselben Stelle endet wie die verzögerte Auswertung (dh nicht in die träge Auswertung von unendlichen Sequenzen und dergleichen geraten), ist ein Algorithmus zur eifrigen Generierung immer so schnell oder schneller (dh weniger Arbeit) als Ein fauler Algorithmus, da er nicht den Aufwand für wiederholte Aufrufe einer Generatorroutine erfordert.
Mason Wheeler
2
Ich denke, der Grund, warum ich dich liebe, ist, dass ich kaum verstehe, wovon du sprichst.
Erik Reppen
3
@ MasonWheeler: Er spricht höchstwahrscheinlich über asymptotische Komplexität. Die konstanten Faktoren sind ein Implementierungsproblem. Selbst wenn beide Programme beendet werden, könnte das nicht strenge Programm deutlich weniger Arbeit leisten: Erwägen Sie all even [1..1e10]sowohl eine strenge als auch eine faule Version von all. Der Compiler hat auch mehr Spielraum für die Auswahl der Auswertungsreihenfolge in einer Sprache wie Haskell mit Dingen wie Loop Fusion.
Tikhon Jelvis
1
@MasonWheeler Lazy-Bewertung vermeidet die Bewertung von Begriffen, die nicht benötigt werden. Ich konnte keine maßgebliche Referenz finden, aber hier ist eine, die darauf hinweist, dass eine verzögerte Bewertung weniger Reduzierungen bewirkt als eine eifrige Bewertung: en.wikibooks.org/wiki/Haskell/…
Heatsink
1

Optimieren Sie die großen Dinge, bevor Sie programmieren, und die kleinen Dinge, wenn Sie fertig sind.

Bevor Sie mit dem Codieren beginnen, sollten Sie beispielsweise über Folgendes nachdenken:

  • Sind die Bibliotheken / Frameworks, die Sie verwenden werden, recht schnell?
  • Versuchen Sie, Ihre Datenstrukturen einfach zu halten.
  • Versuchen Sie, Ihre Algorithmen und Entwurfsmuster so einfach wie möglich zu halten.
  • Wie schnell ist die Sprache, die ich benutze?

...und so weiter.

Wenn Sie mit Ihrem Code fast fertig sind, denken Sie über die kleinen Dinge nach, z. B. welche integrierte Funktion schneller ist, sollte ich diesen Codebereich neu schreiben, um ihn effizienter zu machen usw.

Dies gilt für jede Sprache und hängt wirklich davon ab, welche Art von Software Sie schreiben. Haskell ist eine Allzwecksprache, daher machen Sie wahrscheinlich (korrigieren Sie mich, wenn ich falsch liege) nichts, was extrem schnell sein muss. In diesem Fall sollten Sie eine niedrigere Sprache verwenden. Wenn Geschwindigkeit ein Problem ist, aber nicht ausreicht, dass Sie eine einfache Sprache verwenden sollten, sollten Sie Ihre Leistung auf der Grundlage der oben genannten Punkte optimieren.

Natürlich macht jeder die Dinge anders. Wenn Sie also aufgrund Ihrer persönlichen Erfahrung der Meinung sind, dass Sie es je nach Situation anders machen sollten, sollten Sie es wahrscheinlich tun.

Dynamisch
quelle
5
Das Problem ist, dass ich in den meisten Sprachen leicht über das Laufzeitverhalten nachdenken kann. In Haskell ist dies viel schwieriger. Es ist also wahrscheinlicher, dass man einen falschen Ansatz verfolgt und die Wahrscheinlichkeit von Leistungsfehlern erhöht.
Simon Bergot
Die Antwort beginnt gut, dann sagst du "sieh die kleinen Dinge, die verbessert werden können" ... ja, nein. Profil.
Florian Margaine
3
Diese Antwort ist zu weit gefasst und für Haskell nicht sehr hilfreich. Angenommen, das OP kennt sich bereits mit Makro- und Mikrooptimierung aus. Wie trifft diese Antwort auf Haskell zu , eine nicht strenge Sprache, für die es schwieriger ist, über Laufzeit und Speicherleistung nachzudenken?
Andres F.
@FlorianMargaine Profil?
Dynamisch
0

Ich möchte die Antwort von Dynamic ergänzen:

  • Machen Sie Ihren Code modular und lose gekoppelt .
  • Verstecken Sie die Implementierungsdetails Ihrer Module.

Wenn Sie später in der Entwicklung feststellen, welche Engpässe Ihr Code hat, kann es sehr schmerzhaft sein, das gesamte Projekt umzugestalten. Wenn Ihr Code gut strukturiert ist, sind die Engpässe leichter zu finden und zu optimieren / zu beheben.

Petr Pudlák
quelle