Nach einer Frage bezüglich der Art und Weise, wie die JVM die Erzeugung von Strings basierend auf char [] implementiert, habe ich erwähnt, dass keine Iteration stattfindet, wenn char [] in das Innere der neuen Zeichenfolge kopiert wird, da System.arraycopy aufgerufen wird schließlich aufgerufen, die den gewünschten Speicher mit einer Funktion wie memcpy auf einer nativen, implementierungsabhängigen Ebene kopiert ( die ursprüngliche Frage ).
Ich wollte das selbst überprüfen, also habe ich den Quellcode von Openjdk 7 heruntergeladen und angefangen, ihn zu durchsuchen.
Ich fand die Implementierung von System.arraycopy im OpenJDK C ++ - Quellcode in openjdx/hotspot/src/share/vm/oops/objArrayKlass.cpp
:
Wenn die Elemente keine Typprüfungen benötigen (das ist beispielsweise der Fall bei primitiven Datentypenarrays), wird Copy :: conjoin_oops_atomic aufgerufen.
Die Funktion Copy::conjoint_oops_atomic
befindet sich in 'copy.hpp':
Jetzt sind wir plattformabhängig, da die Kopieroperation eine andere Implementierung hat, basierend auf OS / Architektur. Ich werde mit Windows als Beispiel gehen. openjdk\hotspot\src\os_cpu\windows_x86\vm\copy_windows_x86.inline.hpp
:
Und zu meiner Überraschung durchläuft es die Elemente (die OOP-Werte) und kopiert sie (scheinbar) nacheinander. Kann jemand erklären, warum die Kopie auch auf der nativen Ebene durchläuft, indem ich durch die Elemente im Array iteriere?
Weil die jint
am ehesten der int
entspricht, die am ehesten der alten Hardwarearchitektur WORD
entspricht, die im Grunde die gleiche Größe wie die Breite des Datenbusses hat.
Die Speicherarchitekturen und die CPU-Verarbeitung von heute sind so ausgelegt, dass sie selbst bei einem Cache-Fehlversuch eine Verarbeitung versuchen, und Speicherstellen tendieren dazu, Blöcke vorzufräsen. Der Code, den Sie betrachten, ist nicht ganz so "schlecht" in der Leistung, wie Sie vielleicht denken. Die Hardware ist intelligenter, und wenn Sie nicht wirklich profilieren, können Ihre "intelligenten" Abrufroutinen tatsächlich nichts hinzufügen (oder sogar die Verarbeitung verlangsamen).
Wenn Sie mit Hardwarearchitekturen vertraut sind, müssen Sie mit einfachen vorgestellt werden. Moderne Systeme tun viel mehr, daher kann man nicht davon ausgehen, dass Code, der ineffizient aussieht, tatsächlich ineffizient ist. Wenn zum Beispiel eine Speichersuche durchgeführt wird, um die Bedingung für eine if-Anweisung zu bewerten, werden oft beide Zweige der if-Anweisung ausgeführt, während die Suche stattfindet, und der "falsche" Zweig der Verarbeitung wird verworfen, nachdem die Daten zur Auswertung verfügbar sind die Bedingung. Wenn Sie effizient sein möchten, müssen Sie profilieren und dann die profilierten Daten bearbeiten.
Sehen Sie sich den Zweig im JVM-Opcode-Bereich an. Sie werden sehen, dass es (oder vielleicht gerade) eine ifdef-Makro-Kuriosität war, (auf einmal) drei verschiedene Arten zu unterstützen, zu dem Code zu springen, der den Opcode gehandhabt hat. Das lag daran, dass die drei verschiedenen Möglichkeiten tatsächlich einen erheblichen Leistungsunterschied bei den verschiedenen Windows-, Linux- und Solaris-Architekturen bewirkten.
Vielleicht hätten sie auch MMX-Routinen einbauen können, aber sie sagten mir nicht, dass SUN es nicht für ausreichend gehalten hätte, auf moderner Hardware Leistungsverbesserungen zu machen.