2013-09-05 2 views
72

Ich möchte ein neues Array von Objekten erstellen, die zwei kleinere Arrays zusammensetzen.Ist es besser, System.arraycopy (...) als eine for-Schleife zum Kopieren von Arrays zu verwenden?

Sie können nicht null sein, aber die Größe sein kann 0.

ich nicht zwischen diesen beiden Möglichkeiten wählen können: sie sind gleichwertig oder ist ein effizienter (zB System.arraycopy() kopiert ganze Brocken)

MyObject[] things = new MyObject[publicThings.length+privateThings.length]; 
System.arraycopy(publicThings, 0, things, 0, publicThings.length); 
System.arraycopy(privateThings, 0, things, publicThings.length, privateThings.length); 

oder

MyObject[] things = new MyObject[publicThings.length+privateThings.length]; 
for (int i = 0; i < things.length; i++) { 
    if (i<publicThings.length){ 
     things[i] = publicThings[i] 
    } else { 
     things[i] = privateThings[i-publicThings.length]   
    } 
} 

ist der einzige Unterschied das Aussehen des Codes?

EDIT: Dank für verknüpfte Frage, aber sie scheinen eine ungelöste Diskussion zu haben:

Ist es wirklich schneller, wenn it is not for native types: byte [], Object [], char []? in allen anderen Fällen wird eine Typprüfung durchgeführt, was mein Fall wäre und das wäre äquivalent ... nein?

Auf einer anderen verknüpft Frage, sie sagen, dass the size matters a lot, für Größe> 24 System.arraycopy() gewinnt, für weniger als 10, Handbuch für Schleife besser ist ...

Jetzt bin ich wirklich verwirrt.

+15

'arraycopy()' ist ein nativer Aufruf, der sicherlich schneller ist. –

+0

würden Sie so freundlich sein, zu antworten und zu erklären, warum? Vielen Dank! – Daren

+4

Haben Sie versucht, die beiden verschiedenen Implementierungen zu vergleichen? – Alex

Antwort

68
public void testHardCopyBytes() 
{ 
    byte[] bytes = new byte[0x5000000]; /*~83mb buffer*/ 
    byte[] out = new byte[bytes.length]; 
    for(int i = 0; i < out.length; i++) 
    { 
     out[i] = bytes[i]; 
    } 
} 

public void testArrayCopyBytes() 
{ 
    byte[] bytes = new byte[0x5000000]; /*~83mb buffer*/ 
    byte[] out = new byte[bytes.length]; 
    System.arraycopy(bytes, 0, out, 0, out.length); 
} 

ich weiß, JUnit-Tests sind nicht wirklich das beste für Benchmarking, aber
testHardCopyBytes nahm 0.157s abzuschließen
und
testArrayCopyBytes dauerte 0,086s, um abzuschließen.

Ich denke, es hängt von der virtuellen Maschine ab, aber es sieht so aus, als ob es Blöcke des Speichers kopiert, anstatt einzelne Array-Elemente zu kopieren. Dies würde die Leistung absolut erhöhen.

EDIT:
Es ist wie System.arraycopy die Leistung aussieht, ist ganz über dem Platz. statt Bytes Wenn Strings verwendet, und Arrays sind klein (Größe 10), ich diese Ergebnisse:

String HC: 60306 ns 
    String AC: 4812 ns 
    byte HC: 4490 ns 
    byte AC: 9945 ns 

Hier ist, wie es aussieht, wenn Arrays sind in Größe 0x1000000. Es scheint, dass System.arraycopy definitiv mit größeren Arrays gewinnt.

Strs HC: 51730575 ns 
    Strs AC: 24033154 ns 
    Bytes HC: 28521827 ns 
    Bytes AC: 5264961 ns 

Wie seltsam!

Danke, Daren, für den Hinweis, dass Referenzen anders kopieren. Es machte das ein viel interessanteres Problem!

+2

Vielen Dank für Ihre Mühe, aber Sie vermissten scheinbar entscheidende Punkte: nicht-native Typen (erstellen Sie eine zufällige Klasse mit anythign, so dass das Array Referenzen enthält) und Größe ... scheint für kleinere Array-Größe, manuelle for-Schleife ist schneller.Gehe, um das zu korrigieren? – Daren

+2

Oh wow, du hast recht! Das ist interstring. Das Platzieren von Strings in diesen Arrays anstelle von Bytes macht einen großen Unterschied: <<< testHardCopyStrs: 0.161s> >> <<< testArrayCopyStrs: 0.170s >>> –

+0

Welche Ergebnisse bekommst du dann? Auch interessant wäre, mit Array-Größe = 10 zu versuchen ... danke! (Ich wünschte, ich hätte meine IDE hier, ich bin cod ohne Compiler). – Daren

12

Es hängt von der virtuellen Maschine ab, aber System.arraycopy sollte Ihnen die bestmögliche native Leistung bieten.

Ich habe 2 Jahre lang als Java-Entwickler für Embedded-Systeme gearbeitet (wo Leistung eine große Priorität hat) und überall konnte System.arraycopy verwendet werden, ich habe es meistens benutzt/gesehen in existierendem Code. Overloops werden immer bevorzugt, wenn Leistung ein Problem darstellt. Wenn Leistung nicht ein großes Problem ist, würde ich mit der Schleife gehen, obwohl. Viel einfacher zu lesen.

+0

Dinge wie Arraygröße und -typ (basic vs geerbt) scheinen die Preformance zu beeinflussen. – Daren

+2

Ja, es ist nicht "native Leistung" per se, weshalb ich sagte, dass ich es "meistens" benutze, wo ich kann (du wirst bemerken, dass es meistens das Kopieren von Schleifen übertrifft). Ich schätze, der Grund ist: Wenn es sich um ein kleines Array eines primitiven Typs handelt, ist die "cost-to-call" größer als die Leistungssteigerung. Die Verwendung von JNI kann die Leistung aus dem gleichen Grund beeinträchtigen - der native Code selbst ist zwar schnell, aber von einem Java-Prozess aus aufzurufen - nicht so sehr. –

+0

Ich sehe, vielen Dank für Ihre Eingabe! – Daren

30

Arrays.copyOf(T[], int) ist einfacher zu lesen. Intern verwendet es System.arraycopy(), das ein systemeigener Aufruf ist.

Sie können es nicht schneller bekommen!

+0

es scheint, dass Sie abhängig von ziemlich einigen Sachen, aber Dank für das Zeigen dieser Funktion, die ich nicht wusste und tatsächlich leichter zu lesen ist. :) – Daren

+0

ja! Es hängt von ziemlich vielen Dingen ab, wie @Svetoslav Tsolov sagte. ich wollte nur auf Arrays hinweisen. copyOf –

+1

'copyOf' kann nicht immer' arraycopy' ersetzen, aber es ist für diesen Anwendungsfall geeignet. – Blaisorblade

2

System.arraycopy() ist ein systemeigener Aufruf, der den Vorgang direkt im Arbeitsspeicher kopiert. Eine einzelne Speicherkopie wäre immer schneller als Ihre for-Schleife

+2

Ich habe gelesen, dass für nicht native Typen (jede erstellte Klasse, wie meins) es nicht so effizient sein kann ... und für kleine Größen (mein Fall) manuelle Schleife kann besser sein ... kümmern sich um einen Kommentar? – Daren

+0

In der Tat, System.arraycopy() hat einige Overhead so für kleine Arrays (n = ~ 10) ist eine Schleife tatsächlich schneller – RecursiveExceptionException

6

Die Ausführung nativer Methoden wie Arrays.copyOf(T[], int) hat zwar einige Gemeinkosten, aber es bedeutet nicht, dass es nicht schnell ist, da Sie es mit JNI ausführen.

Der einfachste Weg ist, einen Benchmark zu schreiben und zu testen.

Sie können überprüfen, dass Arrays.copyOf(T[], int) schneller ist als Ihre normale for Schleife.

Der Benchmark-Code von here: -

public void test(int copySize, int copyCount, int testRep) { 
    System.out.println("Copy size = " + copySize); 
    System.out.println("Copy count = " + copyCount); 
    System.out.println(); 
    for (int i = testRep; i > 0; --i) { 
     copy(copySize, copyCount); 
     loop(copySize, copyCount); 
    } 
    System.out.println(); 
} 

public void copy(int copySize, int copyCount) { 
    int[] src = newSrc(copySize + 1); 
    int[] dst = new int[copySize + 1]; 
    long begin = System.nanoTime(); 
    for (int count = copyCount; count > 0; --count) { 
     System.arraycopy(src, 1, dst, 0, copySize); 
     dst[copySize] = src[copySize] + 1; 
     System.arraycopy(dst, 0, src, 0, copySize); 
     src[copySize] = dst[copySize]; 
    } 
    long end = System.nanoTime(); 
    System.out.println("Arraycopy: " + (end - begin)/1e9 + " s"); 
} 

public void loop(int copySize, int copyCount) { 
    int[] src = newSrc(copySize + 1); 
    int[] dst = new int[copySize + 1]; 
    long begin = System.nanoTime(); 
    for (int count = copyCount; count > 0; --count) { 
     for (int i = copySize - 1; i >= 0; --i) { 
      dst[i] = src[i + 1]; 
     } 
     dst[copySize] = src[copySize] + 1; 
     for (int i = copySize - 1; i >= 0; --i) { 
      src[i] = dst[i]; 
     } 
     src[copySize] = dst[copySize]; 
    } 
    long end = System.nanoTime(); 
    System.out.println("Man. loop: " + (end - begin)/1e9 + " s"); 
} 

public int[] newSrc(int arraySize) { 
    int[] src = new int[arraySize]; 
    for (int i = arraySize - 1; i >= 0; --i) { 
     src[i] = i; 
    } 
    return src; 
} 

System.arraycopy() verwendet JNI (Java Native Interface) ein Array (oder Teile davon) zu kopieren, so dass es unglaublich schnell ist, wie Siehere bestätigen

+0

Dieser Code verwendet int [] können Sie es mit String versuchen [] (mit verschiedenen Werten initialisiert: "1" "2", usw., da sie unveränderlich sind – Daren

Verwandte Themen