Warum verwendete das Team von LMAX Java und entwarf die Architektur, um GC um jeden Preis zu vermeiden?


24

Warum hat das Team von LMAX den LMAX-Disruptor in Java entwickelt, aber all seine Konstruktionsaspekte zielen darauf ab, den GC-Einsatz zu minimieren? Wenn man GC nicht ausführen lassen möchte, warum sollte man dann eine Garbage-Collected-Sprache verwenden?

Ihre Optimierungen, das Niveau der Hardware-Kenntnisse und der Gedanke, den sie setzen, sind einfach fantastisch, aber warum Java?

Ich bin nicht gegen Java oder so, aber warum eine GC-Sprache? Warum nicht so etwas wie D oder eine andere Sprache ohne GC verwenden, aber effizienten Code zulassen? Ist es so, dass das Team am besten mit Java vertraut ist oder hat Java einen einzigartigen Vorteil, den ich nicht sehe?

Angenommen, sie entwickeln es mithilfe von D mit manueller Speicherverwaltung. Was wäre der Unterschied? Sie müssten an ein niedriges Niveau denken (was sie bereits sind), aber sie können die beste Leistung aus dem System herausholen, da es ursprünglich ist.


6
Ich weiß sehr wenig über dieses Projekt, aber es scheint eine Art Rahmen zu sein, auf den andere aufbauen können. Und wenn Sie es schaffen, dies in Java zu schreiben (und anderen zu erlauben, in Java zu programmieren und von den Vorteilen zu profitieren), haben Sie einen VIEL größeren "Kundenstamm", als wenn Sie es in D geschrieben hätten.
Joachim Sauer

6
@kadaj: Es spielt keine Rolle, ob der Verbraucher öffentlich oder intern ist. Wenn Sie ihn in einer allgemein bekannten Sprache zugänglich machen, ist er sogar für die interne Entwicklung nützlicher. Wenn Sie Ihr (hypothetisches) Argument mit "Angenommen, jeder kennt D so gut wie Java, ..." beginnen, dann vermissen Sie wahrscheinlich etwas.
Joachim Sauer

6
Manche Leute benutzen gerne Hämmer für alle möglichen Probleme. Wenn Sie eine raue Kante haben, die Sie abhobeln möchten, schlagen Sie sie mit Ihrem Hammer auf, bis sie glatt ist. Besorgen Sie sich eine Schraube, die Sie einschlagen müssen, und hauen Sie sie mit einem Hammer ein, bis sie eingedrungen ist. Besorgen Sie sich ein zartes Ornament, das Sie abschleifen müssen, hauen Sie es mit einem Hammer ein und beschuldigen Sie das Ornament dann für das "Saugen". C oder C ++ wäre eine bessere Wahl gewesen als D, schon allein für die vorhandene Wissensbasis. Ich bin mir nicht sicher, warum Sie D als Beispiel für eine TBH heraufbeschworen haben.
Gbjbaanb

2
@gbjbaanb Ich habe D erwähnt, weil es eine Speicherbereinigung bietet (in Fällen, in denen Abstraktionen auf hoher Ebene erforderlich sind und das Fummeln mit dem Speicher zu schwierig für das Gehirn ist), aber auch die manuelle Speicherverwaltung mit Malloc im C-Stil und kostenlos ermöglicht. D ist wie Objective-C mit ARC (kein richtiger GC), aber besser als das. Aber ja, C / C ++ würde die Rechnung passen.

4
@kadaj Ich sehe, dass Sie etwas Flak bekommen haben, weil Sie D angesprochen haben, aber ich möchte sagen, dass ich von dem Ton, den andere verwenden, enttäuscht bin und erkläre, warum ich denke, dass D für die vorliegende Frage von zentraler Bedeutung ist. Während D in der Tat nicht weit verbreitet ist, bietet D einige Konstrukte auf hoher Ebene, die ich vielleicht in Java oder C #, aber nicht in C ++ (zumindest im alten Stil) erwarten würde. Es ist immer noch möglich, verwaltet und nicht verwaltet zu mischen - das ist so gut wie die einzige Sprache, die ich dazu kenne! D ist also nicht nur eine Haustierwahl, sondern eine, deren Ziele mit den ursprünglichen Fragen rund um GC übereinstimmen.
J Trana

Antworten:


20

Denn es gibt einen großen Unterschied zwischen der Optimierung der Leistung und der vollständigen Abschaltung einer Sicherheit

Durch die Reduzierung der Anzahl der GCs reagiert das Framework schneller und kann (vermutlich) schneller ausgeführt werden. Wenn Sie nun für den Garbage Collector optimieren, bedeutet dies nicht, dass Sie niemals eine Garbage Collection durchführen. Es bedeutet nur, dass sie es seltener machen und wenn sie es tun, läuft es sehr schnell. Diese Art der Optimierung umfasst:

  1. Minimieren der Anzahl von Objekten, die sich in ein Überlebensfeld bewegen (dh mindestens eine Speicherbereinigung überlebt haben), indem kleine Wegwerfobjekte verwendet werden. Objekte, die in den Überlebensraum verschoben wurden, sind schwerer zu sammeln, und eine Speicherbereinigung hier impliziert manchmal das Einfrieren der gesamten JVM.
  2. Ordnen Sie zunächst nicht zu viele Objekte zu. Dies kann nach hinten losgehen, wenn Sie nicht vorsichtig sind, da die Objekte der jungen Generation super billig zuzuordnen und zu sammeln sind.
  3. Stellen Sie sicher, dass das neue Objekt auf das alte zeigt (und nicht umgekehrt), damit das junge Objekt leicht zu sammeln ist, da es keinen Verweis darauf gibt, durch den es aufbewahrt wird

Wenn Sie die Leistung einstellen, stellen Sie normalerweise einen bestimmten "Hot Spot" ein, während Sie Code ignorieren, der nicht häufig ausgeführt wird. Wenn Sie das in Java tun, können Sie den Garbage Collector immer noch für diese dunklen Ecken sorgen lassen (da dies keinen großen Unterschied macht), während Sie sehr sorgfältig für Bereiche optimieren, die in einer engen Schleife laufen. So können Sie entscheiden, wo Sie optimieren möchten und wo nicht. So können Sie Ihre Anstrengungen dort konzentrieren, wo es darauf ankommt.


Wenn Sie nun die Speicherbereinigung vollständig deaktivieren, können Sie keine Auswahl treffen. Sie müssen jedes Objekt manuell entsorgen . Wird diese Methode höchstens einmal am Tag aufgerufen? In Java können Sie dies zulassen, da die Auswirkungen auf die Leistung vernachlässigbar sind (es kann in Ordnung sein, jeden Monat eine vollständige GC durchzuführen). In C ++ verlieren Sie immer noch Ressourcen, sodass Sie sich auch um diese undurchsichtige Methode kümmern müssen. Sie müssen also den Preis für das Ressourcenmanagement in jedem einzelnen Teil Ihrer Anwendung bezahlen , während Sie sich in Java konzentrieren können.


Aber es wird schlimmer.

Was ist, wenn Sie einen Fehler haben, beispielsweise in einer dunklen Ecke Ihrer Anwendung, auf die nur am Montag bei Vollmond zugegriffen wird? Java hat eine starke Sicherheitsgarantie. Es gibt wenig bis gar kein "undefiniertes Verhalten". Wenn Sie etwas falsches verwenden, wird eine Ausnahme ausgelöst, Ihr Programm wird gestoppt und es treten keine Datenbeschädigungen auf. Sie sind sich also ziemlich sicher, dass nichts passieren kann, ohne dass Sie es merken.

Aber in etwas wie D können Sie einen schlechten Zeigerzugriff oder einen Pufferüberlauf haben, und Sie können Ihren Speicher beschädigen, aber Ihr Programm wird es nicht wissen (Sie haben die Sicherheit ausgeschaltet, merken Sie sich das?) Und wird mit seiner falschen fortfahren Daten, und machen Sie einige ziemlich böse Dinge und beschädigen Sie Ihre Daten, und Sie wissen es nicht, und wenn mehr Korruption auftritt, werden Ihre Daten immer falscher, und dann brechen sie plötzlich, und es war in einer lebenswichtigen Anwendung, und Bei der Berechnung einer Rakete ist ein Fehler aufgetreten, und daher funktioniert die Rakete nicht. Die Rakete explodiert, und jemand stirbt, und Ihre Firma steht auf der Titelseite jeder Zeitung, und Ihr Chef zeigt mit dem Finger auf Sie und sagt: "Sie sind Der Ingenieur, der vorschlug, dass wir D zur Leistungsoptimierung verwenden, warum haben Sie nicht an Sicherheit gedacht?". Und es ist deine Schuld. Du hast diese Leute mit deinem dummen Versuch getötet, aufzutreten.


OK, ok, meistens ist es viel weniger dramatisch. Aber selbst eine geschäftskritische Anwendung oder nur eine GPS-App oder zum Beispiel eine Website für das Gesundheitswesen kann bei Fehlern ziemlich negative Konsequenzen haben. In der Regel ist es eine sehr gute Idee, eine Sprache zu verwenden, die sie entweder vollständig verhindert oder bei ihrem Auftreten ausfällt.

Das Ausschalten einer Sicherheit ist mit Kosten verbunden. Einheimische zu werden macht nicht immer Sinn. Manchmal ist es viel einfacher und sicherer, nur ein bisschen eine sichere Sprache zu optimieren, um all-in für eine Sprache zu gehen, in der Sie sich selbst in den Fuß schießen können. Korrektheit und Sicherheit übertreffen in vielen Fällen die wenigen Nano-Sekunden, die Sie durch die vollständige Eliminierung des GC verschrottet hätten. Disruptor kann in solchen Situationen verwendet werden, daher denke ich, dass LMAX-Exchange den richtigen Anruf getätigt hat.

Aber was ist mit D im Besonderen? Sie haben einen GC, wenn Sie möchten, für die dunklen Ecken, und die SafeD-Untergruppe (die ich vor der Bearbeitung nicht kannte) entfernt undefiniertes Verhalten (wenn Sie daran denken, es zu verwenden!).

Nun, in diesem Fall ist es eine einfache Frage der Reife. Das Java-Ökosystem ist voller gut geschriebener Tools und ausgereifter Bibliotheken (besser für die Entwicklung). Sehr viel mehr Entwickler kennen Java als D (besser für die Wartung). Es wäre keine gute Idee gewesen, sich für eine neue und weniger beliebte Sprache zu entscheiden, die so wichtig ist wie eine Finanzanwendung. Wenn Sie ein Problem mit einer weniger bekannten Sprache haben, können Ihnen nur wenige helfen, und die Bibliotheken, die Sie finden, weisen tendenziell mehr Fehler auf, da sie weniger Personen ausgesetzt waren.

Mein letzter Punkt ist also: Wenn Sie Probleme mit schwerwiegenden Folgen vermeiden möchten, bleiben Sie bei sicheren Entscheidungen. Zu diesem Zeitpunkt im Leben von D sind seine Kunden die kleinen Start-ups, die bereit sind, verrückte Risiken einzugehen. Wenn ein Problem Millionen kosten kann, bleiben Sie besser in der Innovationsglockenkurve .


2
Der ursprüngliche Beitrag nennt D. Es gibt tatsächlich einen großen Unterschied zwischen C ++ und D in Bezug auf die Granularität der Auswahl. Selbst wenn Sie sich dafür entscheiden, in der SafeD-Untergruppe vollständig verwaltet zu werden, haben Sie meines Erachtens erheblich mehr Kontrolle über bestimmte Aspekte der Erfassung und des Zeitplans (Aktivieren / Deaktivieren, Erfassen, Minimieren). Sehen Sie sich die Strategien
J Trana

2
lmax absichtlich Seite Schritt einige der Sicherheit, die Java bietet
James

Dies wäre eine großartige Antwort, mit der Ausnahme, dass Java nicht für unternehmenskritische Software lizenziert ist. Wenn Sie einen Kernreaktor haben, wird dieser in C ++ und nicht in Java geschrieben, was den gesamten "Sicherheits" -Aspekt auslöscht.
Gbjbaanb

@ gbjbaanb, [Zitat benötigt]. Die Zuverlässigkeitsstandards / -richtlinien, die ich gesehen habe, empfehlen, zuerst C / C ++ zugunsten anderer Sprachen zu vermeiden. und wenn Sie sich darauf einlassen, sollten Sie stark eingeschränkte Versionen der Sprachen (MISRA usw.) verwenden. Und wenn Sie einmal Einschränkungen akzeptiert haben, verstehe ich nicht, warum Sie mit keiner anderen Sprache dasselbe machen konnten. Wenn Sie darüber nachgedacht haben, dass Java Licence im Abschnitt EINSCHRÄNKUNGEN "Nicht für kerntechnische Anlagen" erwähnt hat, hat sich dies vor einiger Zeit geändert. Stattdessen heißt es nur "Vorsicht, nicht unsere Verantwortung". Trotzdem
gehe

(...) Der ursprüngliche Wortlaut entsprach genau den Lizenzen von gcc und clang: Keine Garantie für bestimmte Zwecke. Sie würden sie also nicht für etwas verwenden, das Zuverlässigkeit erfordert, und stattdessen müssten Sie einen zertifizierten Compiler verwenden, wenn Sie nicht eine bestimmte Sprache für den Job verwenden (Ada?).
Hmijail

4

Es scheint, dass der Grund, warum es in Java geschrieben wurde, darin besteht, dass sie Java-Fachkenntnisse im eigenen Haus haben und dass es wahrscheinlich geschrieben wurde (obwohl es sich noch in der aktiven Entwicklung befindet), bevor C ++ mit C ++ 0x / 11 zusammenarbeitet.

Ihr Code ist eigentlich nur Java mit Namen, sie verwenden sun.misc.Unsafe ziemlich viel, was die Art von Java besiegt und die Sicherheit angeblich gibt. Ich habe einen C ++ - Port des Disruptors geschrieben, der den von ihm gelieferten Java-Code übertrifft (ich habe nicht viel Zeit damit verbracht, die JVM zu optimieren).

Das heißt, die Prinzipien, denen der Disruptor folgt, sind nicht sprachspezifisch. Erwarten Sie z. B. keinen C ++ - Code mit geringer Latenz, der den Heap zuweist oder davon befreit.


Können Sie auf Ihre Implementierung verweisen? Ich habe ein paar solcher Neuimplementierungen gesehen, die eine höhere Leistung beanspruchten, aber beide mit Vereinfachungen betrogen haben: Zum Beispiel 1 Produzent + 1 Konsument fest verdrahtet, anstatt wie der ursprüngliche Disruptor Multi-Produzent / Konsument-fähig zu sein. Der Autor des Disruptors selbst erwähnte in einem Google Groups-Thread, dass die Leistung durch festverdrahtete Parameter in der Java-Version verbessert werden könnte.
Hmijail

4

Diese Frage gibt eine falsche Prämisse als Tatsache an und argumentiert dann über diese falsche Prämisse.

Lassen Sie uns näher darauf eingehen. "Alle ihre Design-Punkte zur Minimierung des GC-Gebrauchs" - ist einfach nicht wahr. Die Innovation im Disruptor hat wenig mit GC zu tun. Die Leistung des Disruptors beruht darauf, dass sein Design die Funktionsweise moderner Computer auf clevere Weise berücksichtigt - etwas, das viel seltener vorkommt, als man erwarten würde. Eine Diskussion finden Sie unter Cliff Clicks Diskussion http://www.azulsystems.com/events/javaone_2009/session/2009_J1_HardwareCrashCourse.pdf .

Es ist bekannt, dass LMax Kunden von Azul sind. Ich weiß aus erster Hand, dass GCs mit Azul einfach ein Non-Issue sind - auch mit Haufen von 175 GB.


Da steckt ein Körnchen Wahrheit dahinter. Sie starten die VM jede Nacht neu, um eine größere Sammlung zu vermeiden. Das hat jedenfalls Martin Fowler geschrieben, und er ist kein Dummy: "Wie der Rest des Systems werden die Disruptoren über Nacht gebounct. Diese Bounce-Methode dient hauptsächlich dazu, den Speicher zu löschen, damit die Wahrscheinlichkeit eines teuren Garbage Collection-Ereignisses während des Handels geringer ist." martinfowler.com/articles/lmax.html
JimmyJames

2
Nicht ganz. Wir haben jede Nacht einen manuellen GC in einer Handelslücke von 5 Minuten ausgelöst und so eingestellt, dass dies der einzige größere GC an einem Tag ist. Das wurde mit Azul Zing überflüssig. (Quelle: Ich habe bis vor kurzem bei LMAX gearbeitet)
Tom Johnson

@TomJohnson Ich liebe es, die Insider-Informationen zu bekommen. Wollen Sie damit sagen, dass Martin Fowlers Beschreibung falsch ist? Ist es möglich, dass sich die Lösung im Laufe der Zeit entwickelt hat?
JimmyJames

2
Ich sage, er war in einigen kleinen Details nicht genau richtig. Wir haben unsere Systeme nie täglich gepuffert, aber wir haben einige End-of-Day-Aufräumarbeiten durchgeführt.
Tom Johnson

3

Sie müssten auf niedrigem Niveau denken

Oben ist die Hälfte der Antwort, die Sie suchen. Sie können eine andere Hälfte finden , um die Argumentation nicht weiter zu vervollständigen als im LMAX-Blog :

Es ist zwar sehr effizient, kann aber zu einer Reihe von Fehlern führen, da es sehr einfach ist, Fehler zu machen ...

Wie die LMAX-Entwickler zugeben, kann es schwierig sein, solchen Code zu entwickeln, zu verstehen und zu debuggen - auch in Java. Wenn Sie eine niedrigere Version als die aktuelle Version verwenden, wird dies das Problem nur noch verschlimmern, wie im Wikipedia-Artikel zu niedrigen Programmiersprachen ausgeführt :

Ein Programm, das in einer einfachen Sprache geschrieben wurde, kann sehr schnell und mit sehr geringem Speicherbedarf ausgeführt werden. Ein gleichwertiges Programm in einer höheren Sprache wird mehr Gewicht haben. Low-Level-Sprachen sind einfach, gelten aber aufgrund der zahlreichen technischen Details, die beachtet werden müssen, als schwierig zu verwenden .

Im Vergleich dazu isoliert eine Programmiersprache auf hoher Ebene die Ausführungssemantik einer Computerarchitektur von der Spezifikation des Programms, was die Entwicklung vereinfacht .


3

Wenn Sie Java als Syntaxsprache verwenden und die JDK-Bibliotheken meiden, kann dies genauso schnell sein wie eine kompilierte Nicht-GC-Sprache. GC ist nicht für Echtzeitsysteme geeignet, aber es ist möglich, Systeme in Java zu entwickeln, die keinen Müll hinterlassen. Infolgedessen löst der GC niemals aus.

Wir glauben, dass die Java-Sprache und -Plattform viele Vorteile gegenüber C / C ++ haben, und wir haben einige Java-Komponenten mit extrem geringer Latenz entwickelt und getestet, um dies zu beweisen. Wir sprechen in diesem Artikel über die entsprechenden Techniken: Java-Entwicklung ohne GC .


2
Es gibt Müllsammler, die für Echtzeitsysteme geeignet sind. Der Standardkollektor der JVM ist möglicherweise nicht derselbe. Dies bedeutet jedoch nicht, dass GC im Allgemeinen nicht für Echtzeit geeignet ist. Aber malloc/freeauch für Echtzeit ist plain nicht geeignet, da die Allokationszeit aufgrund von Fragmentierung unbegrenzt ist.
Doval

1
Wir befürworten die Verwendung schneller Objektpools für alles, damit nach dem Aufwärmen keine Zuordnung erfolgt.
Rdalmeida

2

LMAX ist eine leistungsstarke Inter-Thread-Messaging-Bibliothek.

Um nützlich zu sein, muss jemand anderes den Code schreiben, damit jeder Thread nützliche Arbeit leistet. Angesichts der Tatsache, dass der Code höchstwahrscheinlich in Java oder C # vorliegt und es nur sehr wenige Sprachoptionen gibt, die gut mit ihnen zusammenarbeiten.

Die Verwendung von C oder C ++ ist keine gute Option, es sei denn, Sie möchten Ihre Benutzer auf ein einzelnes Betriebssystem beschränken, da in ihnen kein Threading-Modell definiert ist.

Java ist heutzutage der Standard für viele Softwareentwicklungen. Wenn Sie also keinen guten Grund dafür haben, ist Java die beste Wahl. (Man soll sich an örtliche Gepflogenheiten halten…)

Das Schreiben von Hochleistungssoftware in Java (oder C #) wird häufig durchgeführt, um einen Punkt zu beweisen…


1
Der neue C ++ 11-Standard unterstützt Multithreading ...
Casey

@Casey und wie viele reale C ++ - Compiler verwenden es? Und wie viel kosten diese Compiler? Vielleicht wird es in 20 Jahren nützlich sein, bis dahin kann man sich nicht darauf verlassen.
Ian

Disruptor verwendet sun.misc.Unsafe, was zeigt, dass Sie in Java keinen Code mit geringer Latenz schreiben können, ohne Ihren Zeh in C Land zu tauchen
James

3
Gcc unterstützt C ++ - Threads und es ist kostenlos
James

@ Ian: 2 Jahre später und alle allgemein verwendeten Compiler unterstützen es;). Sogar die, die frei sind.
Rutix
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.