Frage Warum ist System.arraycopy in Java nativ?


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

Der Grund ist natürlich, dass es schneller ist. Aber welche nativen Tricks kann der Code verwenden, der ihn schneller macht?

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


76
2018-05-05 10:01


Ursprung


Antworten:


Im nativen Code kann es mit einem einzigen Code gemacht werden memcpy / memmove, im Gegensatz zu n verschiedene Kopiervorgänge. Der Leistungsunterschied ist beträchtlich.


72
2018-05-05 10:04



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 kippen mit einem einzigen geschrieben werden memcpy()aufgrund der Semantik, die für überlappende Arrays erforderlich ist.


15
2018-05-05 10:09



Es hängt natürlich von der Implementierung ab.

HotSpot behandelt es als "intrinsisch" und fügt Code an der Aufrufseite ein. 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.


9
2018-05-05 10:17



In meinen eigenen Tests ist System.arraycopy () zum Kopieren von Arrays mit mehreren Dimensionen 10 bis 20 mal schneller als das Interleaving von for-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);
        }
    }
}

Dies druckt:

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

4
2017-11-28 14:44



Es gibt ein paar Gründe:

  1. Es ist unwahrscheinlich, dass der JIT so effizienten Low-Level-Code generiert wie ein manuell geschriebener C-Code. Die Verwendung von Low-Level C kann viele Optimierungen ermöglichen, die für einen generischen JIT-Compiler nahezu unmöglich sind.

    Sehen Sie diesen Link für einige Tricks und Geschwindigkeitsvergleiche von Hand geschriebenen C-Implementierungen (memcpy, aber das Prinzip ist das gleiche): Überprüfen Sie dies Optimieren von Memcpy verbessert die Geschwindigkeit

  2. Die C-Version ist ziemlich unabhängig von der Art und Größe der 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).


3
2018-05-05 10:15