Wie viel Speicher braucht man 2024, um 1 Million gleichzeitige Tasks auszuführen?
(hez2010.github.io)Es wurde die Speichereffizienz verglichen, indem Programme ausgeführt wurden, die 1 Million gleichzeitige Tasks mit aktuellen Versionen von Rust, C#, Python, Go, Java und NodeJS verarbeiten.
C# (NativeAOT) und Rust zeigten die beste Speichereffizienz, während Go wegen eines höher als erwarteten Speicherverbrauchs schlechter abschnitt. Insgesamt stachen die Ergebnisse von .NET und Rust hervor, und Java (GraalVM) zeigte eine überraschend starke Verbesserung.
Konkret verwendete Rust mit rund 29 MB am wenigsten Speicher, gefolgt von C# NativeAOT mit etwa 71 MB. NodeJS kam auf 232 MB, Python auf 339 MB. Go zeigte mit 753 MB einen vergleichsweise hohen Speicherverbrauch und schnitt daher enttäuschend ab. Java (GraalVM) zeigte mit 92 MB eine deutliche Verbesserung.
10 Kommentare
Wenn man sich den Benchmark-Code ansieht, scheint es bei Rust und Python so zu sein, dass dort in Wirklichkeit keine concurrent tasks erzeugt werden, sondern lediglich Future-Objekte, die zwar asynchron sind, aber nicht parallel zu anderen Tasks laufen können. Ich vermute, dass C# ein ähnlicher Fall sein könnte. Im Gegensatz dazu erzeugt der Go-Code Goroutinen, also Tasks mit eigenem Call Stack usw. Ich denke, darin liegt auch der Grund, warum der Speicherverbrauch von Go im Fall von 1 Million so stark heraussticht.
Um Go einmal zu verteidigen: Go funktioniert auch dann, wenn in einer der 1 Million laufenden Funktionen irgendeine Bibliothek steckt. Man muss nur
godavorsetzen. Bei anderen Sprachen auf asynchroner Basis kommt man in die verrückte Situation, dass schon eine Bibliothek, die zwischendurch auf synchrone Weise etwas Zeit frisst, die Vorteile der Asynchronität komplett zunichtemacht.Um die Vorteile der Asynchronität zu 100 % auszuschöpfen, muss man alle auch nur ein wenig zeitfressenden Funktionen asynchron umschreiben.
Java VirtualThread ist hm ... Wir haben in unserer Firma dieses Mal auf Java VirtualThread gesetzt, mussten wegen Bibliothekskompatibilität am Ende aber doch wieder auf normale Threads wechseln, und das Ende vom Lied war, dass wir mehrere Dutzend Instanzen hochfahren mussten.
Könnten Sie noch etwas mehr zur Kompatibilität erzählen? :eyes:
Man kann wohl sagen, dass die oft gehörte Aussage im Spring-Umfeld, „Um WebFlux richtig zu nutzen, muss man statt JPA R2DBC verwenden, erst dann zeigt es seinen wahren Wert“, so nicht mehr gilt.
Ich habe gehört, dass die
msal-Bibliothek von Microsoft auf Virtual Threads nicht funktioniert.Ich denke, dass die von Ihnen als Beispiel genannte
msal-Bibliothek auch im Fall von Go ähnlich zu bewerten ist, wenn die Bibliothek Datentypen oder Strukturen verwendet, die nicht thread-safe sind.Was hat das mit Thread-Safety zu tun?
goroutineist doch von vornherein kein System, das Thread-Safety garantiert.Vielen Dank für die Informationen.
Ich hätte dazu eine Frage.
Heißt das, dass bei anderen Sprachen außer Go, wenn es wie von Ihnen beschrieben eine synchrone Bibliothek gibt, alles zusammenbricht?
Oder gibt es unter den anderen Sprachen vielleicht auch welche, die wie Go vollständige Asynchronität unterstützen?
Es gibt den Ausdruck „colorless“. Go ist die einzige Sprache, bei der man nicht zwischen asynchron und synchron unterscheiden muss. Aus Sicht der Nutzer hat Go, wenn man Programmierung mit benötigter Nebenläufigkeit betreibt, überwältigende Stärken in Bezug auf Schwierigkeitsgrad und Bedienbarkeit.
Bei der Performance ist es gegenüber optimierter asynchroner Programmierung vielleicht etwas unterlegen.
Entschuldigung für die Korinthenkackerei, aber ich möchte nur auf die falschen Teile bei den Formulierungen „geht kaputt“ und „perfekte Asynchronität“ hinweisen. Auch in einer asynchronen Umgebung ist es in Ordnung, synchron arbeitende Bibliotheken zu verwenden – vorausgesetzt, es ist garantiert, dass sie in kurzer Zeit abgeschlossen werden. Problematisch wird es, wenn es synchron ausgeführte, lang laufende Aufrufe gibt, weil sich dann die Verarbeitung anderer asynchroner Aufgaben verzögert. Bei Go kann man mit Goroutinen sogar Aufgaben in der Größenordnung von Hunderten Millionen zuweisen, daher gibt es im Sprachdesign selbst kein Konzept von Asynchronität. Aus Sicht der Nutzenden ist das extrem bequem, weil praktisch jede Funktion einfach mit
goaufgerufen werden kann und dann parallel läuft. Persönlich würde ich sagen, dass JavaScript, dessen sprachliches Fundament selbst asynchron ist, der perfekte Kandidat für „perfekte Asynchronität“ ist.