98 votes

Est-il préférable d'utiliser System.arraycopy(...) qu'une boucle for pour copier des tableaux?

Je veux créer un nouveau tableau d'objets en mettant ensemble deux tableaux plus petits.

Ils ne peuvent pas être nuls, mais leur taille peut être 0.

Je ne peux pas choisir entre ces deux façons : sont-elles équivalentes ou l'une est-elle plus efficace (par exemple, system.arraycopy() copie des morceaux entiers) ?

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);

ou

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

``

La seule différence est-elle dans l'apparence du code ?

EDIT: merci pour la question liée, mais ils semblent avoir une discussion non résolue :

Est-ce vraiment plus rapide si ce n'est pas pour les types natifs : byte[], Object[], char[] ? dans tous les autres cas, une vérification de type est exécutée, ce qui serait mon cas et donc serait équivalent... non ?

Dans une autre question liée, ils disent que la taille compte énormément, pour une taille >24 system.arraycopy() l'emporte, pour moins de 10, la boucle manuelle est meilleure...

Maintenant je suis vraiment confus.

``

17 votes

arraycopy() est un appel natif, qui est très certainement plus rapide.

0 votes

Pourriez-vous avoir la gentillesse de répondre et d'expliquer pourquoi ? Merci !

4 votes

Avez-vous essayé de faire des benchmarks des deux implémentations différentes ?

90voto

Trent Small Points 266
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);
}

Je sais que les tests JUnit ne sont pas vraiment les meilleurs pour les tests de performances, mais
testHardCopyBytes a pris 0,157s pour être complété
et
testArrayCopyBytes a pris 0,086s pour être complété.

Je pense que cela dépend de la machine virtuelle, mais il semble copier des blocs de mémoire au lieu de copier des éléments individuels d'un tableau. Cela augmenterait certainement les performances.

EDIT:
Il semble que les performances de System.arraycopy soient aléatoires. Lorsque des chaînes de caractères sont utilisées au lieu de bytes, et que les tableaux sont petits (taille 10), Je reçois ces résultats:

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

Voici ce que cela donne lorsque les tableaux ont une taille de 0x1000000. Il semble que System.arraycopy gagne définitivement avec de plus grands tableaux.

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

Quelle curiosité!

Merci Daren d'avoir souligné que les références sont copiées différemment. Cela a rendu ce problème beaucoup plus intéressant!

2 votes

Merci pour vos efforts, mais vous avez omis de points apparemment cruciaux : les types non natifs (créez une classe aléatoire avec n'importe quoi pour que le tableau contienne des références) et la taille... il semble que pour une taille de tableau plus petite, la boucle for manuelle est plus rapide. Souhaitez-vous corriger cela?

2 votes

Oh la la, tu as raison! C'est intéressant. Placer des chaînes de caractères dans ces tableaux au lieu d'octets fait une énorme différence: <<>> <<>>

0 votes

Quels sont les résultats que vous obtenez alors? Il serait aussi intéressant d'essayer avec une taille de tableau = 10... merci! (J'aimerais avoir mon IDE ici, je code sans compilateur).

36voto

Philipp Sander Points 4916

Arrays.copyOf(T[], int) est plus facile à lire. Internalement, il utilise System.arraycopy() qui est un appel natif.

Vous ne pouvez pas le rendre plus rapide!

0 votes

Il semble que vous puissiez en fonction de plusieurs choses, mais merci d'avoir signalé cette fonction que je ne connaissais pas et qui est en effet plus facile à lire. :)

0 votes

Oui! cela dépend de plusieurs choses comme l'a dit @Svetoslav Tsolov. je voulais simplement souligner Arrays.copyOf

2 votes

copyOf ne peut pas toujours remplacer arraycopy, mais il est approprié pour ce cas d'utilisation.

19voto

s.ts Points 648

Cela dépend de la machine virtuelle, mais System.arraycopy devrait vous donner les performances les plus proches du natif.

J'ai travaillé pendant 2 ans en tant que développeur Java pour les systèmes embarqués (où la performance est une priorité importante) et partout où System.arraycopy pouvait être utilisé, je l'ai principalement utilisé / vu utilisé dans le code existant. Il est toujours préféré aux boucles lorsque la performance est en jeu. Si la performance n'est pas un gros problème, je choisirais la boucle. Beaucoup plus facile à lire.

0 votes

Les choses comme la taille et le type de tableau (de base vs hérité) semblent affecter les performances.

2 votes

Oui, ce n'est pas une "performance native" en soi, c'est pourquoi j'ai dit que je l'utilisais "principalement" là où je peux (vous remarquerez qu'elle l'emporte surtout sur la copie de boucle). Je suppose que la raison est la suivante : quand il s'agit d'un petit tableau d'un type primitif, le "coût de l'appel" est plus grand que l'augmentation des performances. L'utilisation de JNI peut dégrader les performances pour la même raison - le code natif lui-même est rapide, mais l'appeler à partir d'un processus Java - pas tellement.

0 votes

Correction mineure, Arrays.copy n'est pas JNI, c'est une intrinsèque. Les intrinsèques sont beaucoup beaucoup plus rapides que JNI. Comment et quand le compilateur JIT le transforme en intrinsèque dépend du JVM/compilateur utilisé.

6voto

Rahul Tripathi Points 1

Exécuter des méthodes natives comme Arrays.copyOf(T[], int) a un certain surcoût, mais cela ne signifie pas que ce n'est pas rapide car vous l'exécutez en utilisant JNI.

Le moyen le plus simple est d'écrire un banc d'essai et de tester.

Vous pouvez vérifier que Arrays.copyOf(T[], int) est plus rapide que votre boucle for normale.

Le code de benchmark provenant de ici :-

public void test(int copySize, int copyCount, int testRep) {
    System.out.println("Taille de la copie = " + copySize);
    System.out.println("Nombre de copies = " + 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() utilise JNI (Java Native Interface) pour copier un tableau (ou des parties de celui-ci), donc c'est extrêmement rapide, comme vous pouvez le confirmer ici

0 votes

Ce code utilise int[] pouvez-vous essayer avec String[] (initialisé avec différentes valeurs : "1", "2", etc. car ils sont immuables)

1 votes

JNI est très lent. System.arraycopy ne l'utilise pas.

0 votes

Non, System.arraycopy n'utilise pas JNI, qui est seulement pour appeler des bibliothèques tierces. Au lieu de cela, c'est un appel natif, ce qui signifie qu'il y a une implementation native dans le VM pour cela.

4voto

Nandkumar Tekale Points 8534

System.arraycopy() est un appel natif qui effectue une opération de copie directement en mémoire. Une seule copie en mémoire sera toujours plus rapide que votre boucle for

3 votes

J'ai lu que pour les types non natifs (toute classe créée, comme la mienne) cela peut ne pas être si efficace... et pour les petites tailles (mon cas) une boucle manuelle peut être meilleure... avez-vous un commentaire à faire?

0 votes

En effet, System.arraycopy() a un certain surcoût donc pour de petits tableaux (n = ~10) une boucle est en réalité plus rapide

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X