F: Unter welchen Umständen ist Java viel langsamer als C++?
Antwort: Ben Maurer:
Um diese Frage zu beantworten, müssen Sie das Problem zunächst in mehrere mögliche Ursachen für die Langsamkeit unterteilen:
Garbage Collector. Dies ist ein „zweischneidiges Schwert“. Wenn Ihr Programm dem Modell „Die meisten Objekte sterben in der jungen Generation“ folgt, ist der Garbage Collector sehr vorteilhaft (weniger Fragmentierung, bessere Cache-Lokalität). Wenn das Programm jedoch nicht diesem Modell folgt, wird die JVM viele Ressourcen für die Rückgewinnung von Heap-Speicher aufwenden.
Großes Objekt. In Java verfügen alle Objekte über einen vtable-Zeiger, während in C++ kein zusätzlicher Aufwand durch die Verwendung der POD-Struktur entsteht. Darüber hinaus können alle Java-Objekte gesperrt werden. Seine Implementierung hängt von der JVM ab, was möglicherweise das Hinzufügen zusätzlicher Felder zum Objekt erfordert. Große Objekte == weniger Objekte zwischenspeichern == langsamer. (Andererseits verwendet Java 7 64-Bit-Datensätze für komprimierte Zeiger, was Teil des Problems ist.
Mangel an Inline-Objekten. In Java sind alle Klassen Zeiger. In C++ kann das Objekt sein Zusammen mit anderen Objekten oder auf dem Stapel zugewiesen. Dies kann die Lokalität des Caches verbessern und dadurch den Overhead der dynamischen Speicherzuweisung verringern. In Java kann das Kompilieren von Objekten in lokalen Code einen hohen Overhead verursachen Wenn Sie den C++-Code des Clients häufig aufrufen müssen, verursacht dies einen hohen Overhead. Wenn Sie beispielsweise einen XML-Parser schreiben möchten, verwenden Sie nur String-Objekte (ohne Zeichen). ]), wird es aufgrund des von der JVM zugewiesenen zusätzlichen Speicherplatzes langsam sein, fast alle Funktionsaufrufe sind virtuelle Funktionsaufrufe, aber in vielen Fällen kann die JVM dieses Problem nicht lösen und macht den Code langsamer 🎜> Fehlende erweiterte Kompilierung Funktionen und die Möglichkeit, in Assembler zu konvertieren, kann dazu führen, dass Java nicht gut funktioniert.
Meiner Meinung nach ist das Erzwingen mehrerer Codes das größte Problem Volle GCs im großen Speicher sind einer der wahrscheinlichsten Gründe für die Lücke zwischen Java und C++. Darüber hinaus können Probleme wie große Objekte, fehlende Inline-Objekte usw. auftreten, wenn der Arbeitssatz des Programms außerhalb des L2-Cache platziert wird Auch ineffiziente erzwungene Abstraktionen und Plattformfunktionen können zu Verlangsamungen führen, die jedoch normalerweise nur auf Low-Level-Implementierungen zurückzuführen sind. Der Code wird generiert ist normalerweise kein großes Problem
A: Todd Lipcon
Ich stimme grundsätzlich mit Ben Maurer (hey Ben!) überein. Es gibt ein paar kleine Unterschiede:
In der Die neueste JVM-Escape-Analyse funktioniert, wenn die Zuweisung niemals aus (a) einer lokalen Funktion oder (b) einem lokalen Thread entweicht. Die Bestimmung einer festen Zuweisung, das heißt, wenn die Zuweisung keine Sperre erfordert, wird sie normalerweise auf einem eigenen Stapel durchgeführt In beiden Fällen handelt es sich um eine einfache „Bump the Pointer“-Zuweisung. Dies entspricht der Stapelzuweisung in C.
Anmerkung des Übersetzers:
Die Escape-Analyse ist eine Technik zur Kompilierungsoptimierung Zur Analyse des dynamischen Bereichs von Zeigern sagen wir, dass der Zeiger den Speicher im Java-Heap verlässt ist absolut regelmäßig, und der gesamte verwendete Speicher wird auf der einen Seite platziert, der freie Speicher wird auf der anderen Seite platziert und ein Zeiger wird in der Mitte als Indikator für den Grenzpunkt platziert. Der zugewiesene Speicher dient nur zum Verschieben Zeiger auf den freien Raum um einen Abstand, der der Größe des Objekts entspricht. Diese Zuordnungsmethode wird als „Zeigerkollision“ bezeichnet.
Auch ohne Escape-Analyse erfolgt die Zuweisung der jungen Generation im Thread Local Allocation Buffer (TLAB) durch Zeigerkollision, und es ist keine Synchronisierung erforderlich. Daher ist die Zuweisung kleiner Objekte in Java manchmal schneller als die in der C-Sprache implementierte malloc()-Methode. Bessere Malloc-Methoden wie tcmalloc von Google verfolgen einen ähnlichen Ansatz. Da die C-Sprache jedoch keine zugewiesenen Objekte im Speicher neu zuweisen kann, ist sie in einigen Aspekten eingeschränkt.
Obwohl es Probleme mit Inlining und virtuellen Funktionen gibt, kann Java in einigen Fällen sogar bessere Ergebnisse erzielen als C. Insbesondere kann C kein Inlining durch dynamisches Verknüpfen implementieren, da das Inlining zur Kompilierungszeit und nicht zur Laufzeit erfolgt. Java kann eine Funktion dynamisch über die Grenzen verschiedener Klassen oder Bibliotheken hinweg einbinden, selbst wenn die tatsächliche Implementierung der Klasse zur Kompilierungszeit nicht verfügbar ist. In vielen Jobs ist dieser Ansatz effizienter als virtuelle C++-Funktionsaufrufe, die immer Aufrufe an virtuelle Tabellen erfordern. Wenn zuvor dynamische Attribute verloren gegangen sind (z. B. wenn eine neue Klasse geladen wurde), kann der JIT-Compiler die Inline-Optimierung intelligent abbrechen.
Die neue Version von GCC bietet einige Optimierungen in diesem Bereich, die als „Whole-Program Optimization“ oder „Link-Time Optimization“ bezeichnet werden und das Inlining über Objektdateien hinweg innerhalb des Projektumfangs ermöglichen. Es ist jedoch grundsätzlich nicht zulässig, Inlining durch dynamische Verknüpfung zu implementieren (z. B. Aufruf von zlib durch Inlining usw.). Viele große Projekte werden implementiert, indem die Funktionalität der Standardbibliothek in ihren Code kopiert wird.