Funktionale Programmierung sorgt in der Regel nicht für schnellere Programme. Dies erleichtert die parallele und gleichzeitige Programmierung. Hierfür gibt es zwei Hauptschlüssel:
- Die Vermeidung eines veränderlichen Zustands verringert in der Regel die Anzahl der Fehler, die in einem Programm auftreten können, und insbesondere in einem gleichzeitig ablaufenden Programm.
- Das Vermeiden von Synchronisationsprimitiven mit gemeinsamem Speicher und auf Sperren basierenden Synchronisationsprimitiven zugunsten von Konzepten höherer Ebenen vereinfacht tendenziell die Synchronisation zwischen Codethreads.
Ein hervorragendes Beispiel für Punkt 2 ist, dass wir in Haskell klar zwischen deterministischer Parallelität und nicht deterministischer Parallelität unterscheiden . Es gibt keine bessere Erklärung, als Simon Marlows hervorragendes Buch Parallel and Concurrent Programming in Haskell zu zitieren (Zitate stammen aus Kapitel 1 ):
Ein paralleles Programm verwendet eine Vielzahl von Computerhardware (z. B. mehrere Prozessorkerne), um eine Berechnung schneller durchzuführen. Ziel ist es, die Antwort früher zu finden, indem verschiedene Teile der Berechnung an verschiedene Prozessoren delegiert werden, die zur gleichen Zeit ausgeführt werden.
Im Gegensatz dazu ist die Parallelität eine Programmstrukturierungstechnik, bei der es mehrere Steuerungsthreads gibt. Konzeptionell werden die Steuerthreads "zur gleichen Zeit" ausgeführt. Das heißt, der Benutzer sieht ihre Effekte ineinander verschachtelt. Ob sie tatsächlich zur gleichen Zeit ausgeführt werden oder nicht, ist ein Implementierungsdetail. Ein gleichzeitiges Programm kann auf einem einzelnen Prozessor durch verschachtelte Ausführung oder auf mehreren physischen Prozessoren ausgeführt werden.
Darüber hinaus erwähnt Marlow auch die Dimension des Determinismus :
Eine verwandte Unterscheidung besteht zwischen deterministischen und nicht deterministischen Programmiermodellen. Ein deterministisches Programmiermodell ist eines, bei dem jedes Programm nur ein Ergebnis liefern kann, wohingegen ein nicht deterministisches Programmiermodell Programme zulässt, die je nach Aspekt der Ausführung unterschiedliche Ergebnisse haben können. Gleichzeitige Programmiermodelle sind notwendigerweise nicht deterministisch, da sie mit externen Agenten interagieren müssen, die zu unvorhersehbaren Zeiten Ereignisse verursachen. Nichtdeterminismus hat jedoch einige bemerkenswerte Nachteile: Programme sind erheblich schwieriger zu testen und zu begründen.
Für die parallele Programmierung möchten wir möglichst deterministische Programmiermodelle verwenden. Da es nur darum geht, die Antwort schneller zu finden, möchten wir unser Programm nicht schwieriger machen, in diesem Prozess Fehler zu beheben. Deterministische Parallelprogrammierung ist das Beste aus beiden Welten: Das sequentielle Programm kann getestet, getestet und begründet werden, aber das Programm läuft schneller, wenn mehr Prozessoren hinzugefügt werden.
In Haskell sind die Parallelitäts- und Nebenläufigkeitsfunktionen auf diese Konzepte ausgelegt. Insbesondere, welche anderen Sprachen als ein Feature-Set zusammengefasst sind, teilt sich Haskell in zwei:
- Deterministische Merkmale und Bibliotheken für Parallelität .
- Nicht deterministische Funktionen und Bibliotheken für die gleichzeitige Verwendung .
Wenn Sie nur versuchen, eine reine, deterministische Berechnung zu beschleunigen, macht die deterministische Parallelität die Sache oft viel einfacher. Oft macht man einfach so etwas:
- Schreiben Sie eine Funktion, die eine Liste von Antworten erstellt, deren Berechnung teuer ist, die jedoch nicht sehr voneinander abhängen. Dies ist Haskell, also sind Listen faul - die Werte ihrer Elemente werden erst berechnet, wenn ein Verbraucher sie verlangt.
- Verwenden Sie die Strategies- Bibliothek, um die Elemente der Ergebnislisten Ihrer Funktion über mehrere Kerne hinweg parallel zu nutzen.
Ich habe das tatsächlich mit einem meiner Spielzeugprojektprogramme vor ein paar Wochen gemacht . Es war trivial, das Programm zu parallelisieren. Das Wichtigste, was ich tun musste, war tatsächlich, einen Code hinzuzufügen, der besagt, dass die Elemente dieser Liste parallel berechnet werden (Zeile 90), und ich bekam einen nahezu linearen Durchsatzschub einige meiner teureren Testfälle.
Ist mein Programm schneller als mit herkömmlichen sperrenbasierten Multithreading-Dienstprogrammen? Das bezweifle ich sehr. Das Ordentliche in meinem Fall war, so viel aus so wenig Geld herauszuholen - mein Code ist wahrscheinlich sehr suboptimal, aber weil es so einfach zu parallelisieren ist, habe ich mit viel weniger Aufwand eine große Beschleunigung erzielt, als ihn richtig zu profilieren und zu optimieren. und keine Gefahr von Rennbedingungen. Und das ist, so würde ich behaupten, die Hauptmethode, mit der Sie mit funktionaler Programmierung "schnellere" Programme schreiben können.