Fachbeitrag
Elixir und Nebenläufigkeit: gemessen unter Last
25. August 2026 · Software und Automatisierung

Wer über Elixir und Nebenläufigkeit liest, bekommt fast immer dieselbe Zusage: Hunderttausende Prozesse, leichtgewichtig, über alle Kerne verteilt. Der Satz stimmt, und er hat aufgehört, ein Argument zu sein.
Interessant ist etwas anderes an der Maschine unter Elixir: Kein Prozess kann einen anderen anhalten. Das ist messbar, es kostet Antwortzeit unter Last, und für viele Anwendungen ist es die falsche Eigenschaft.
Wann also ist das Nebenläufigkeitsmodell der Erlang-Maschine ein Grund, eine Anwendung darauf zu bauen? Zweimal in diesem Text steht, warum wir von Elixir abraten würden, obwohl es zu unserem eigenen Angebot gehört.
Nebenläufig kann inzwischen jeder
Go hat Goroutinen, Python hat asyncio, und Java hat seit Version 21 virtuelle Threads. Bemerkenswert ist, wie nüchtern der zugehörige JEP formuliert ist: Virtuelle Threads seien keine schnelleren Threads, heißt es dort, und dann wörtlich: They exist to provide scale (higher throughput), not speed (lower latency).
Viele gleichzeitig heißt also Durchsatz; über die Antwortzeit sagt es nichts. Die Frage ist damit gestellt: Was passiert, wenn sich einer davon daneben benimmt?
Drei Eigenschaften, und keine davon gehört der Sprache
Was gleich gemessen wird, ist eine Eigenschaft der Laufzeitumgebung unter Elixir. Die BEAM stammt aus dem Bau von Telefonvermittlungsstellen; Erlang, Gleam und LFE laufen ebenfalls auf ihr.
Erstens: eigener Speicher je Prozess. Jeder Prozess hat laut Erlang-Dokumentation Stack und Heap im selben Speicherblock, und aufgeräumt wird, wenn beide aufeinandertreffen. Es gibt deshalb keinen Zeitpunkt, an dem alles steht, weil einer aufräumt.
Zweitens: erzwungene Unterbrechung. Ein Prozess gibt ab, wenn seine Zeitscheibe aufgebraucht ist. Die Herstellerdokumentation zu nativen Funktionen nennt dafür die Größenordnung einer Millisekunde und begründet damit, warum eine native Funktion in dieser Zeit zurückkehren soll. Virtuelle Threads in Java geben ihren Träger bei blockierender Ein- und Ausgabe ab, mitten in einer Rechnung dagegen nicht.
Drittens: Isolation als Entwurfsentscheidung. In der Dissertation, die Erlang begründet hat, steht der Anspruch in einem Satz: Two processes operating on the same machine must be as independent as if they ran on physically separated machines. Daraus folgt der Rest: kein gemeinsamer Speicher, Nachrichten als einziger Weg, und Nachrichten, die niemanden aufhalten.
Die Messung
Die folgende Messung lässt sich in jeder Elixir-Installation wiederholen. Sie zwingt die Maschine auf einen Rechenkern, startet dort eine wachsende Zahl rechnender Prozesse und misst, wie lange eine Nachricht an einen anderen Prozess und zurück braucht.
defmodule P do
def last(0), do: last(50_000_000)
def last(n), do: last(n - 1)
def echo, do: (receive do {p, r} -> send(p, r) end; echo())
end
echo = spawn(&P.echo/0)
for _ <- 1..200, do: spawn(fn -> P.last(0) end)
Process.sleep(300)
w = for _ <- 1..2000 do
r = make_ref(); t = System.monotonic_time(:microsecond)
send(echo, {self(), r}); receive do ^r -> :ok end
System.monotonic_time(:microsecond) - t
end |> Enum.sort()
IO.puts("Median #{Enum.at(w, 999)} us, p99 #{Enum.at(w, 1979)} us")Gestartet wird sie mit:
elixir --erl "+S 1" messung.exsDie 200 Prozesse aus Zeile 8 rechnen ohne Unterbrechung und ohne eine einzige blockierende Stelle; sie geben nie freiwillig ab. Bei kooperativem Scheduling wäre die Messung an dieser Stelle zu Ende, weil der erste dieser Prozesse den Kern nie wieder hergibt.
Gemessen am 25. August 2026 mit Elixir 1.14.0 und Erlang/OTP 25, je 2.000 Umläufe:
| Rechnende Prozesse auf einem Kern | Median | 99. Perzentil |
|---|---|---|
| 0 | 31 µs | 101 µs |
| 1 | 15 µs | 29 µs |
| 10 | 138 µs | 309 µs |
| 100 | 1.427 µs | 3.146 µs |
| 200 | 2.815 µs | 5.628 µs |
| 1.000 | 14.325 µs | 30.750 µs |
Die Zahlen sagen zweierlei. Erstens verhungert nichts: Tausend Prozesse, die auf einem Kern ohne Pause rechnen, halten eine Nachricht um vierzehn Millisekunden auf, und keine wartet unbestimmt lange.
Zweitens ist der Zuwachs linear: Von zehn bis tausend Prozessen kommen je zusätzlichem Prozess rund 14 Mikrosekunden dazu, über zwei Größenordnungen hinweg mit derselben Steigung. So verhält sich eine Warteschlange, die der Reihe nach abgearbeitet wird.
Eine Zeile passt nicht ins Bild, und wir lassen sie stehen: Ohne Last liegt der Median höher als mit einem Lastprozess. Lässt man den Scheduler aktiv warten statt schlafen (+sbwt very_long), liegt der Median ohne Last bei 29 Mikrosekunden. Woran es liegt, wissen wir nicht.
Wo diese Messung aufhört zu gelten
Sie gilt für einen erzwungenen Rechenkern auf einem geteilten Wirtssystem, mit Elixir 1.14.0 und Erlang/OTP 25. Der gepflegte Stand ist Elixir 1.20.3 vom 4. August 2026, und der verlangt Erlang/OTP 27. An der Frage, ob unterbrochen wird, ändert das nichts; an jeder einzelnen Zahl oben möglicherweise einiges.
Über Durchsatz, über Java und über Go sagt die Messung nichts. Über Ihre Anwendung ebenso wenig, denn dort hängt die Warteschlange vermutlich an der Datenbank.
Der Absturz, den der Nachbar nicht merkt

Die zweite Hälfte der Eigenschaft ist der Wiederanlauf. Ein Supervisor startet einen abgestürzten Prozess neu, ohne dass irgendjemand sonst etwas davon mitbekommt.
Was dabei fast nie mitgesagt wird: Der Zustand ist nach dem Neustart weg. Ein GenServer, der einen Zähler hält, antwortet nach einem erzwungenen Abschuss sofort wieder und beginnt bei null. Genau das ist das Versprechen, Wiederanlauf in einen bekannten Ausgangszustand.
Jede Angabe, die den Neustart überleben soll, muss außerhalb des Prozesses liegen. Wer den Satz let it crash gehört hat und das nicht mitplant, bekommt eine Anwendung, die zuverlässig weiterläuft und dabei zuverlässig Arbeit verliert.
Wo die Eigenschaft aufhört: beim Rechnen
Numerik läuft auf der BEAM in nativen Funktionen neben dem Erlang-Code, und für die gilt die Unterbrechung nicht. Die Herstellerdokumentation sagt das offen: Eine native Funktion, die sich nicht in Abschnitte von je einer Millisekunde zerlegen lässt, heißt dort dirty NIF und muss angemeldet werden, damit sie auf einem getrennten Satz von Kernen läuft. Der Grund steht daneben: A native function doing lengthy work before returning degrades responsiveness of the VM.
Genau so ist die Rechenschicht von Elixir gebaut: In exla/c_src/exla/exla.cc sind das Übersetzen und das Ausführen eines Rechengraphen als ERL_NIF_DIRTY_JOB_CPU_BOUND angemeldet, die Übergabe von Daten an das Rechengerät (CPU oder GPU) als ERL_NIF_DIRTY_JOB_IO_BOUND. Das ist richtig gebaut und kein Mangel des Projekts. Es heißt nur: Die Eigenschaft, wegen der man die BEAM wählt, gilt für den wartenden Teil einer Anwendung; für den rechnenden gilt sie nicht.
Der Preis, in Zahlen
Alle folgenden Zahlen sind am 25. August 2026 erhoben. Hex weist 26.151 Pakete in 260.189 Versionen aus, PyPI am selben Tag 879.677 Projekte in 9.447.916 Releases. Das ist der Faktor 34, und er beschreibt die Wahrscheinlichkeit, dass es das, was Sie brauchen, schon fertig gibt.
Stack Overflow führt 9.641 Fragen zum Schlagwort elixir und 9.696 zu erlang, gegenüber 1.914.604 zu java und 2.204.905 zu python. Wenn Ihr Team feststeckt, findet es die Antwort seltener durch Nachschlagen.
Die dritte Zahlenreihe steht nirgends zusammengestellt: die Versionsnummern der Pakete selbst. Nach Semantic Versioning, dem die Hex-Pakete folgen, bedeutet eine Null vor dem ersten Punkt, dass sich die Schnittstelle noch ändern darf.
| Bereich | Paket | Version am 25.08.2026 | Abrufe insgesamt |
|---|---|---|---|
| Web | phoenix | 1.8.13 | 154.107.177 |
| Web | phoenix_live_view | 1.2.10 | 44.158.728 |
| Datenbank | ecto | 3.14.2 | 145.667.001 |
| Zahlen | nx | 0.13.1 | 1.594.290 |
| Zahlen | exla | 0.13.1 | 800.437 |
| Tabellen | explorer | 0.12.0 | 1.483.360 |
| Lernverfahren | axon | 0.8.1 | 539.576 |
| Modelle | bumblebee | 0.7.1 | 454.276 |
| Statistik | scholar | 0.4.2 | 277.914 |
Die Weboberfläche ist fertig, die Datenseite ist es nicht. Phoenix steht bei 1.8 und ist 154 Millionen Mal abgerufen worden; nx steht bei 0.13 und ist 1,6 Millionen Mal abgerufen worden, also gerade einmal ein Hundertstel davon. Auf der anderen Seite: numpy 2.5.2, pandas 3.0.5, scikit-learn 1.9.0, torch 2.13.0.
Wovon wir abraten, obwohl es in unserem Werkzeugkasten steht
Auf unserer Leistungsseite steht „Elixir mit Phoenix“ im Absatz zu Software und Automatisierung. Der folgende Satz nimmt uns Aufträge weg und bleibt trotzdem stehen: Für die Vorhaben, nach denen wir oft gefragt werden, also Unterlagen auswerten, Belege lesen, ein Sprachmodell anbinden, würden wir Elixir nicht vorschlagen. Die Bibliotheken dafür stehen bei 0.x, und die Eigenschaft, für die man die BEAM wählt, greift beim Rechnen nicht.
Der zweite unbequeme Satz betrifft den Normalfall. Wenn Ihre Anwendung nie mehr als ein paar hundert gleichzeitige Anfragen bedient und die Antwortzeit an der Datenbank hängt, lautet die Antwort auf „sollen wir das in Elixir bauen“ nein. Sie brauchen die Eigenschaft dann nicht und bezahlen den Preis aus dem vorigen Abschnitt trotzdem.
Wann Sie Elixir einsetzen sollten
Drei Fälle, in denen die Rechnung aufgeht:
- Viele gleichzeitig offene Verbindungen mit Zustand. Eine Oberfläche mit LiveView hält je geöffneter Seite einen eigenen Prozess, und der Bedarf wächst mit der Zahl der offenen Seiten.
- Die Streuung ist die Anforderung. Wo eine zugesagte obere Schranke im Vertrag steht, zählt das schlechte Prozent; wo nur ein Mittelwert berichtet wird, sehen Sie den Unterschied nie.
- Ein Teil darf ausfallen, ohne den Rest anzuhalten. Dafür wurde die Isolation entworfen, und nur hier spielt sie ihren Aufwand von selbst wieder ein.
Quellen
Quellen zuletzt geprüft am 25. August 2026.
- OpenJDK: JEP 444: Virtual Threads, ausgeliefert mit JDK 21. openjdk.org/jeps/444
- Ericsson: Erlang Garbage Collector, Dokumentation der Laufzeitumgebung ERTS. erlang.org
- Ericsson: erl_nif, Dokumentation der Laufzeitumgebung ERTS. erlang.org
- Royal Institute of Technology, Stockholm: Making reliable distributed systems in the presence of software errors, Dissertation, Dezember 2003, Abschnitt 2.4.3. erlang.org
- elixir-lang: Elixir v1.20 released, 3. Juni 2026, und das Änderungsprotokoll zu 1.20.3 vom 4. August 2026. elixir-lang.org
- elixir-nx: EXLA, Datei exla/c_src/exla/exla.cc. github.com/elixir-nx/nx
- Hex: Zählwerte der Startseite sowie Versionsstände und Abrufzahlen der neun Pakete. hex.pm
- Python Software Foundation: Zählwerte der Startseite von PyPI und die vier Versionsstände. pypi.org
- Stack Exchange: Fragen je Schlagwort über tags/info. api.stackexchange.com
Eigene Messung
Aus eigener Messung stammen die Tabelle mit Median und 99. Perzentil, die Steigung von rund 14 Mikrosekunden je zusätzlichem Prozess, der Gegenversuch mit aktivem Warten und das Neustartverhalten des Zählers. Gemessen am 25. August 2026 mit Elixir 1.14.0 und Erlang/OTP 25 (erts-13.1.5), über +S 1 auf einen Schedulerthread erzwungen, je 2.000 Umläufe je Laststufe; das Skript steht oben vollständig. Das Wirtssystem ist geteilt, Fremdlast ist nicht ausgeschlossen, deshalb berichten wir keine Höchstwerte.
- Veröffentlicht
- 25. August 2026
- Quellen zuletzt geprüft
- 25. August 2026
- Eigene Messung
- Elixir 1.14.0, Erlang/OTP 25 (erts-13.1.5), 25. August 2026
- Gepflegter Stand
- Elixir 1.20.3, 4. August 2026, verlangt OTP 27
- Paketstände
- Phoenix 1.8.13, Nx 0.13.1, EXLA 0.13.1
- Verzeichnisgröße
- Hex 26.151 gegen PyPI 879.677 Pakete
- Im Werkzeugkasten
- Elixir mit Phoenix, siehe Leistungen
- Autor
- phi ec
info@phiec.de
Zwei Zahlen aus Ihrem Monitoring
Schicken Sie uns den Mittelwert Ihrer Antwortzeiten und das 99. Perzentil. Daraus sagen wir Ihnen, ob die Laufzeitumgebung überhaupt Ihr Thema ist.