2010-05-05 10 views
74

Ich war überrascht, in der Java-Quelle zu sehen, dass System.arraycopy eine native Methode ist.Warum ist System.arraycopy in Java nativ?

Natürlich ist der Grund, weil es schneller ist. Aber welche nativen Tricks kann der Code verwenden, der ihn schneller macht?

Warum nicht einfach über das ursprüngliche Array schleifen und jeden Zeiger auf das neue Array kopieren - sicher ist das nicht so langsam und umständlich?

Antwort

70

In nativen Code, kann es mit einem einzigen memcpy/memmove erfolgen, da Operationen n deutlicher Kopie gegenüber. Der Leistungsunterschied ist beträchtlich.

+0

@ Peter, so innerhalb nativen Code können Sie mit dem Java-Speichermodell verarbeiten? (Ich hatte noch nie einen Grund, einen einheimischen Malarkey zu machen) –

+0

@James B, ich bin kein Experte dafür, aber im nativen Code sind Sie sicherlich nicht durch das Java-Speichermodell oder irgendetwas eingeschränkt - Sie können mit dem Raw herumspielen Bits, wie Sie möchten (auf eigene Gefahr, natürlich). –

+7

Tatsächlich konnten nur einige Subcases von 'arraycopy' mit' memcpy'/'memmove' implementiert werden. Andere erfordern eine Laufzeittypüberprüfung für jedes kopierte Element. –

14

Es kann nicht in Java geschrieben werden. Nativer Code ist in der Lage, den Unterschied zwischen Arrays von Object und Arrays von Primitiven zu ignorieren oder zu elimieren. Java kann das nicht, zumindest nicht effizient.

Und es kann nicht mit einem einzigen memcpy() geschrieben werden, wegen der Semantik von überlappenden Arrays erforderlich.

+2

Gut, dann 'memove' dann. Obwohl ich denke, dass es im Kontext dieser Frage keinen großen Unterschied macht. –

+0

Not memmove() entweder, siehe @Stephen C's Kommentare zu einer anderen Antwort. – EJP

+0

Sah das schon, da das zufällig meine eigene Antwort war ;-) Aber danke trotzdem. –

3

Es gibt ein paar Gründe:

  1. Der JIT ist unwahrscheinlich, da effiziente Low Level-Code als manuell geschrieben C-Code zu generieren. Die Verwendung von Low-Level C kann viele Optimierungen ermöglichen, die für einen generischen JIT-Compiler nahezu unmöglich sind.

    Siehe diesen Link für einige Tricks und Geschwindigkeit Vergleiche von Hand geschriebene C-Implementierungen (memcpy, aber das Prinzip ist das gleiche): Aktivieren Sie diese Option Optimizing Memcpy improves speed

  2. Die C-Version ziemlich unabhängig von der Art und Größe die Array-Mitglieder. Es ist nicht möglich, dasselbe in Java zu tun, da es keine Möglichkeit gibt, den Array-Inhalt als einen rohen Speicherblock zu erhalten (z. B. Zeiger).

+1

Java-Code kann optimiert werden. Tatsächlich wird ein Maschinencode erzeugt, der effizienter ist als der C. –

+0

Ich stimme zu, dass JIT-Code manchmal besser optimiert wird, da er weiß, auf welchem ​​Prozessor er läuft. Da es "just in time" ist, wird es jedoch niemals alle nicht-lokalen Optimierungen verwenden können, deren Ausführung länger dauert. Außerdem wird es niemals in der Lage sein, den handgefertigten C-Code zu finden (der auch den Prozessor berücksichtigen und die JIT-Vorteile teilweise negieren könnte, entweder durch Kompilieren für einen bestimmten Prozessor oder durch irgendeine Art von Laufzeitprüfung). –

+1

Ich denke, dass das Sun JIT Compiler-Team viele dieser Punkte bestreiten würde. Zum Beispiel glaube ich, dass HotSpot eine globale Optimierung durchführt, um unnötige Methodenverteilung zu entfernen, und es gibt keinen Grund, warum ein JIT keinen prozessorspezifischen Code generieren kann. Dann gibt es den Punkt, dass ein JIT-Compiler basierend auf dem Ausführungsverhalten des aktuellen Anwendungslaufs eine Verzweigungsoptimierung durchführen kann. –

9

Es ist natürlich implementierungsabhängig.

HotSpot wird es als "intrinsisch" behandeln und Code an der Call-Site einfügen. Das ist Maschinencode, nicht langsamer alter C-Code. Dies bedeutet auch, dass die Probleme mit der Signatur der Methode weitgehend verschwinden.

Eine einfache Kopierschleife ist einfach genug, dass offensichtliche Optimierungen darauf angewendet werden können. Zum Beispiel Loop Enrolling. Was genau passiert, ist wiederum implementierungsabhängig.

+1

das ist eine sehr anständige Antwort :), esp. die Erwähnung der intrinsics. w/o sie einfache Iteration könnte schneller sein, da es sowieso von der JIT – bestsss

4

In meinen eigenen Tests System.arraycopy() für mehrdimensionale Arrays zu kopieren ist 10 bis 20 mal schneller als Verschachtelung für Schleifen:

float[][] foo = mLoadMillionsOfPoints(); // result is a float[1200000][9] 
float[][] fooCpy = new float[foo.length][foo[0].length]; 
long lTime = System.currentTimeMillis(); 
System.arraycopy(foo, 0, fooCpy, 0, foo.length); 
System.out.println("native duration: " + (System.currentTimeMillis() - lTime) + " ms"); 
lTime = System.currentTimeMillis(); 

for (int i = 0; i < foo.length; i++) 
{ 
    for (int j = 0; j < foo[0].length; j++) 
    { 
     fooCpy[i][j] = foo[i][j]; 
    } 
} 
System.out.println("System.arraycopy() duration: " + (System.currentTimeMillis() - lTime) + " ms"); 
for (int i = 0; i < foo.length; i++) 
{ 
    for (int j = 0; j < foo[0].length; j++) 
    { 
     if (fooCpy[i][j] != foo[i][j]) 
     { 
      System.err.println("ERROR at " + i + ", " + j); 
     } 
    } 
} 

Diese Drucke:

System.arraycopy() duration: 1 ms 
loop duration: 16 ms 
+8

aufgerollt wird. Obwohl diese Frage alt ist, nur für den Rekord: Dies ist KEIN fairer Benchmark (geschweige denn die Frage, ob solch ein Benchmark in der erster Platz). 'System.arraycopy' erstellt eine flache Kopie (nur die * Referenzen * zu den inneren' float [] 's werden kopiert), während Ihre verschachtelten' for'-Schleifen eine tiefe Kopie ('float' nach' float') ausführen. Eine Änderung von 'fooCpy [i] [j]' wird in 'foo' mit' System.arraycopy' widergespiegelt, aber nicht mit den verschachtelten 'for'-Schleifen. – misberner

Verwandte Themen