Technisch gesehen, warum sind processe in Erlang effizienter als OS-Threads?

Erlangs Eigenschaften

Aus Erlang Programmierung (2009):

Erlang Concurrency ist schnell und skalierbar. Seine processe sind leichtgewichtig, da die virtuelle Erlang-Maschine keinen Betriebssystem-Thread für jeden erstellten process erstellt. Sie werden in der VM erstellt, geplant und verarbeitet, unabhängig vom zugrunde liegenden Betriebssystem. Infolgedessen ist die process-Erzeugungszeit in der Größenordnung von Mikrosekunden und unabhängig von der Anzahl der gleichzeitig existierenden processe. Vergleichen Sie dies mit Java und C #, wo für jeden process ein zugrundeliegender OS-Thread erstellt wird: Sie erhalten einige sehr wettbewerbsfähige Vergleiche, wobei Erlang beide Sprachen deutlich übertrifft.

Aus der Concurrency-orientierten Programmierung in Erlang (pdf) (Folien) (2003):

Wir beobachten, dass die Zeit für die Erstellung eines Erlang-processes konstant 1 μs bis zu 2.500 processe beträgt. Danach erhöht es sich auf ca. 3μs für bis zu 30.000 processe. Die performance von Java und C # wird oben in der Abbildung angezeigt. Für eine kleine Anzahl von processen benötigt man etwa 300μs, um einen process zu erstellen. Es ist unmöglich, mehr als zweitausend processe zu erstellen.

Wir sehen, dass für bis zu 30.000 processe die Zeit zum Senden einer Nachricht zwischen zwei Erlang-processen etwa 0,8 μs beträgt. Für C # dauert es ungefähr 50μs pro Nachricht, bis zur maximalen Anzahl von processen (was ungefähr 1800 processe waren). Java war noch schlimmer, für bis zu 100 processe dauerte es etwa 50μs pro Nachricht, danach stieg es schnell auf 10ms pro Nachricht, als es etwa 1000 Java-processe gab.

Meine Gedanken

Ich verstehe technisch nicht vollständig, warum Erlang-processe so viel effizienter sind, neue processe hervorzubringen, und pro process viel kleinere Speicherabdrücke haben. Sowohl das Betriebssystem als auch die Erlang-VM müssen Scheduling, Kontextwechsel durchführen und die Werte in den Registern verfolgen und so weiter …

Warum werden OS-Threads nicht genauso implementiert wie processe in Erlang? Müssen sie etwas mehr unterstützen? Und warum benötigen sie einen größeren Speicherbedarf? Und warum haben sie langsamer Laich und Kommunikation?

Technisch gesehen, warum sind processe in Erlang effizienter als OS-Threads, wenn es um das Laichen und die Kommunikation geht? Und warum können Threads im Betriebssystem nicht auf die gleiche effiziente Weise implementiert und verwaltet werden? Und warum haben OS-Threads einen größeren Speicherbedarf sowie langsameres Laichen und Kommunizieren?

Mehr lesen

  • In der Erlang VM mit Fokus auf SMP (2008)
  • Parallelität in Java und in Erlang (pdf) (2004)
  • performancesmessungen von Threads in Java und processen in Erlang (1998)

Es gibt mehrere Faktoren:

  1. Erlang-processe sind keine OS-processe. Sie werden von der Erlang-VM unter Verwendung eines leichtgewichtigen kooperativen Threading-Modells implementiert (präemptiv auf der Erlang-Ebene, aber unter der Kontrolle einer kooperativ geplanten Laufzeit). Dies bedeutet, dass es viel billiger ist, den Kontext zu wechseln, da sie nur an bekannten, kontrollierten Punkten schalten und daher nicht den gesamten CPU-Zustand speichern müssen (normale Register, SSE- und FPU-Register, Adressraum-Mapping usw.).
  2. Erlang-processe verwenden dynamisch zugewiesene Stapel, die sehr klein beginnen und bei Bedarf wachsen. Dies ermöglicht das Erschaffen von vielen Tausenden – sogar Millionen – Erlang-processen, ohne den gesamten verfügbaren RAM aufzusaugen.
  3. Früher war Erlang single-threaded, was bedeutet, dass keine Thread-Sicherheit zwischen processen sichergestellt werden musste. Es unterstützt jetzt SMP, aber die Interaktion zwischen Erlang-processen auf dem gleichen Scheduler / core ist immer noch sehr leicht (es gibt separate Laufwarteschlangen pro core).

Nach ein paar weiteren Recherchen fand ich eine Präsentation von Joe Armstrong.

Von Erlang – Software für eine konkurrierende Welt (Präsentation) (in 13 Minuten):

[Erlang] ist eine gleichzeitige Sprache – damit meine ich, dass Threads Teil der Programmiersprache sind, sie gehören nicht zum Betriebssystem. Das ist wirklich falsch bei Programmiersprachen wie Java und C ++. Es sind Threads nicht in der Programmiersprache, Threads sind etwas im Betriebssystem – und sie erben alle Probleme, die sie im Betriebssystem haben. Eines der Probleme ist die Granularität des Speicherverwaltungssystems. Die Speicherverwaltung im Betriebssystem schützt ganze Seiten des Speichers, so dass die kleinste Größe, die ein Thread sein kann, die kleinste Größe einer Seite ist. Das ist eigentlich zu groß.

Wenn Sie mehr Speicher zu Ihrem Computer hinzufügen – Sie haben die gleiche Anzahl von Bits, die den Speicher schützt, so dass die Granularität der Seitentabellen steigt – am Ende sagen Sie 64kB für einen process, der in einigen hundert Bytes ausgeführt wird.

Ich denke, es beantwortet, wenn nicht alle, zumindest ein paar meiner Fragen

Ich habe Coroutinen in Assembler implementiert und die performance gemessen.

Das Umschalten zwischen Coroutinen, auch Erlang-processen genannt, dauert etwa 16 statementen und 20 Nanosekunden auf einem modernen processor. Außerdem kennen Sie oft den process, zu dem Sie wechseln (Beispiel: Ein process, der eine Nachricht in seiner Warteschlange empfängt, kann als direkte Weiterleitung vom aufrufenden process zum empfangenden process implementiert werden), sodass der Scheduler nicht ins Spiel kommt es ist eine O (1) -Operation.

Um zwischen OS-Threads zu wechseln, dauert es etwa 500-1000 Nanosekunden, da Sie den coreel aufrufen. Der OS-Thread-Scheduler läuft möglicherweise in O (log (n)) oder O (log (log (n))) -Zeit, was sich bemerkbar macht, wenn Sie Zehntausende oder gar Millionen von Threads haben.

Daher sind Erlang-processe schneller und skalieren besser, da sowohl der grundlegende Vorgang des Umschaltens schneller ist als auch der Scheduler weniger häufig ausgeführt wird.

Erlang-processe entsprechen (ungefähr) grünen Fäden in anderen Sprachen; Es gibt keine vom Betriebssystem erzwungene Trennung zwischen den processen. (Es kann durchaus eine sprachgetriebene Trennung geben, aber das ist ein geringerer Schutz, obwohl Erlang einen besseren Job macht als die meisten anderen.) Weil sie so viel leichter sind, können sie viel umfassender verwendet werden.

OS-Threads können dagegen einfach auf verschiedenen CPU-coreen geplant werden und sind (meistens) in der Lage, eine unabhängige CPU-gebundene Verarbeitung zu unterstützen. OS-processe sind wie OS-Threads, jedoch mit einer viel stärkeren vom Betriebssystem erzwungenen Trennung. Der Preis dieser Fähigkeiten ist, dass OS-Threads und (noch mehr) processe teurer sind.


Eine andere Möglichkeit, den Unterschied zu verstehen, ist dies. Angenommen, Sie würden eine Implementierung von Erlang auf die JVM schreiben (kein besonders verrückter Vorschlag), dann würden Sie jeden Erlang-process zu einem Objekt mit einem bestimmten Status machen. Sie hätten dann einen Pool von Thread-Instanzen (normalerweise nach der Anzahl der coree in Ihrem Host-System bemessen; das ist ein abstimmbarer Parameter in echten Erlang-Laufzeiten BTW), der die Erlang-processe ausführt. Dies wiederum wird die Arbeit, die zu leisten ist, auf die verfügbaren realen Systemressourcen verteilen. Es ist eine ziemlich nette Art, Dinge zu tun, beruht aber völlig auf der Tatsache, dass jeder einzelne Erlang-process nicht viel tut. Das ist natürlich in Ordnung; Erlang ist so strukturiert, dass es nicht erforderlich ist, dass diese einzelnen processe schwergewichtig sind, da das gesamte Ensemble das Programm ausführt.

In vielerlei Hinsicht ist das eigentliche Problem eine Terminologie. Die Dinge, die Erlang processe nennt (und die dem gleichen Konzept in CSP, CCS und insbesondere dem π-Kalkül stark entsprechen), sind einfach nicht die gleichen wie die Sprachen mit einem C-Erbe (einschließlich C ++, Java, C # und viele andere) rufen einen process oder einen Thread auf. Es gibt einige Ähnlichkeiten (alle beinhalten eine Vorstellung von gleichzeitiger Ausführung), aber es gibt definitiv keine Äquivalenz. Sei also vorsichtig, wenn dir jemand “process” sagt; sie könnten es so verstehen, dass es etwas völlig anderes bedeutet …

Ich denke, Jonas wollte Zahlen, um OS-Threads mit Erlang-processen zu vergleichen. Der Autor von Programming Erlang, Joe Armstrong, hat vor einiger Zeit die Skalierbarkeit des Erlangens von Erlang-processen auf OS-Threads getestet. Er schrieb einen einfachen Webserver in Erlang und testete ihn gegen Multi-Threaded Apache (da Apache OS-Threads verwendet). Es gibt eine alte Website mit den Daten aus dem Jahr 1998. Ich habe es geschafft, diese Seite nur einmal zu finden. Also kann ich keinen Link angeben. Aber die Information ist da draußen. Der Hauptpunkt der Studie zeigte, dass Apache knapp 8K-processe ausschöpfte, während sein handgeschriebener Erlang-Server 10K + -processe behandelte.

Da der Erlang-Interpreter sich nur um sich selbst kümmern muss, hat das OS viele andere Dinge zu beachten.

einer der Gründe ist Erlang process ist nicht im Betriebssystem, sondern in der evm (erlang virtuelle Maschine), so dass die Kosten kleiner ist.