33 votes

Pourquoi clone () est le meilleur moyen de copier des tableaux?

C'est une honte pour moi, mais je ne savais pas que:

Vous devez utiliser clone de copier des tableaux, parce que c'est généralement le la façon la plus rapide de le faire.

comme Josh Bloch états dans ce blog: http://www.artima.com/intv/bloch13.html

J'ai toujours utilisé System.arraycopy(...). Les deux approches sont natifs, donc probablement sans aller plus loin dans les sources des bibliothèques, je ne peux pas comprendre, pourquoi il en est ainsi.

Ma question est simple: pourquoi est-il le plus rapide de la sorte? Quelle est la différence avec l' System.arraycopy? La différence est expliqué ici, mais il ne répond pas à la question de savoir pourquoi Josh Bloch considère clone() comme le moyen le plus rapide.

31voto

procrastinator Points 897

Je voudrais faire quelques remarques au sujet pourquoi clone() est le moyen le plus rapide pour copier un tableau qu' System.arraycopy(..) ou autres:

1. clone() n'a pas à faire la vérification de type, avant de la copie d'un tableau source à la destination de l'un comme prévu ici. Elle est toute simple alloue un nouvel espace mémoire et affecte les objets. D'autre part, System.arraycopy(..) vérifie le type et la copie d'un tableau.

2. clone() se différencie également de l'optimisation permettent d'éliminer la redondance du point zéro. Comme vous le savez, chaque alloué tableau en Java doit être initialisé avec 0s ou leurs valeurs par défaut. Cependant, JIT peut éviter la réinitialisation de ce tableau si elle voit que le tableau est rempli après la création. Qui le rend nettement plus rapide par rapport à l'évolution de la copie des valeurs existantes 0s ou leurs valeurs par défaut. Tout en utilisant System.arraycopy(..) dépense considérable de temps et de compensation de la copie de l'initialisation de tableau. Pour ce faire, j'ai effectué certains tests de référence.

@BenchmarkMode(Mode.Throughput)
@Fork(1)
@State(Scope.Thread)
@Warmup(iterations = 10, time = 1, batchSize = 1000)
@Measurement(iterations = 10, time = 1, batchSize = 1000)
public class BenchmarkTests {

    @Param({"1000","100","10","5", "1"})
    private int size;
    private int[] original;

    @Setup
    public void setup() {
        original = new int[size];
        for (int i = 0; i < size; i++) {
            original[i] = i;
        }
    }

    @Benchmark
    public int[] SystemArrayCopy() {
        final int length = size;
        int[] destination = new int[length];
        System.arraycopy(original, 0, destination, 0, length);
        return destination;
    }


    @Benchmark
    public int[] arrayClone() {
        return original.clone();
    }

}

Sortie:

Benchmark                        (size)   Mode  Cnt       Score      Error  Units
ArrayCopy.SystemArrayCopy            1  thrpt   10   26324.251 ± 1532.265  ops/s
ArrayCopy.SystemArrayCopy            5  thrpt   10   26435.562 ± 2537.114  ops/s
ArrayCopy.SystemArrayCopy           10  thrpt   10   27262.200 ± 2145.334  ops/s
ArrayCopy.SystemArrayCopy          100  thrpt   10   10524.117 ±  474.325  ops/s
ArrayCopy.SystemArrayCopy         1000  thrpt   10     984.213 ±  121.934  ops/s
ArrayCopy.arrayClone                 1  thrpt   10   55832.672 ± 4521.112  ops/s
ArrayCopy.arrayClone                 5  thrpt   10   48174.496 ± 2728.928  ops/s
ArrayCopy.arrayClone                10  thrpt   10   46267.482 ± 4641.747  ops/s
ArrayCopy.arrayClone               100  thrpt   10   19837.480 ±  364.156  ops/s
ArrayCopy.arrayClone              1000  thrpt   10    1841.145 ±  110.322  ops/s

Selon les résultats que j'obtiens qu' clone est presque deux fois plus rapide à partir d' System.arraycopy(..)

3. Aussi, en utilisant le manuel de la méthode de copie comme clone() des résultats plus rapides en sortie, car il n'a pas à effectuer des VM appels (à la différence de System.arraycopy()).

5voto

EJP Points 113412

D'une part, clone() n'a pas à effectuer la vérification typographique que System.arraycopy() fait.

5voto

egorlitvinenko Points 2028

Je veux corriger et de compléter les réponses précédentes.

  1. Objet.clone utilise pas cochée Système.arraycopy mise en œuvre pour les tableaux;
  2. La principale amélioration de la performance de l'Objet.clone, c'est l'initialisation de la mémoire BRUTE directement. Dans le cas de Système.arraycopy il tente également de combiner initialisation de tableau avec l'opération de copie, comme nous pouvons le voir dans le code source, mais aussi les différentes vérifications supplémentaires, pour cela, à la différence de l'Objet.clone. Si vous venez de désactiver cette fonction (voir ci-dessous), le rendement serait très proche (en particulier sur mon matériel).
  3. Une chose plus intéressante est sur de Jeunes vs Vieux Général. Dans le cas où la source de la matrice de alignées et à l'intérieur de Old Gen, les deux méthodes ont fermer la performance.
  4. Quand on copie primitive des matrices du Système.arraycopy utilise toujours generate_unchecked_arraycopy.
  5. Il dépend de matériel/OS dépendante des implémentations, donc ne faites pas confiance à des repères et des hypothèses, vérifier sur que vous possédez.

Explication

Tout d'abord clone de la méthode et du Système.arraycopy sont intrinsèques. Objet.le clone et le Système.arraycopy utilisation generate_unchecked_arraycopy. Et si on aller plus loin, nous pouvions voir qu'après un HotSpot sélectionnez la mise en œuvre concrète, dépendante de l'OS, etc.

Longly. Voyons le code de Hotspot. Tout d'abord nous allons voir que l'Objet.clone (LibraryCallKit::inline_native_clone) utilise generate_arraycopy, qui est utilisé pour le Système.arraycopy en cas d' -XX:-ReduceInitialCardMarks. Sinon, il ne LibraryCallKit::copy_to_clone, qui initialiser le tableau dans la mémoire BRUTE (si l'option-XX:+ReduceBulkZeroing, qui est activé par défaut). En revanche Système.arraycopy utilise generate_arraycopy directement, essayez de vérifier ReduceBulkZeroing (et de nombreux autres cas) et d'éliminer la matrice de mise à zéro de trop, avec que mentionné, d'autres vérifications et aussi il serait faire des vérifications pour s'assurer que tous les éléments sont initialisés, à la différence de l'Objet.clone. Enfin, dans le meilleur des cas, ils utilisent tous les deux generate_unchecked_arraycopy.

Ci-dessous, je montre quelques repères pour voir cet effet sur la pratique:

  1. La première est une simple référence, la seule différence par rapport à la réponse précédente, que les tableaux ne sont pas triées; Nous voyons que arraycopy est plus lent (mais pas deux fois), les résultats https://pastebin.com/ny56Ag1z;
  2. Deuxièmement, j'ai ajouter l'option-XX:-ReduceBulkZeroing et maintenant je vois que les performances des deux méthodes est très proche. Les résultats https://pastebin.com/ZDAeQWwx;
  3. J'ai aussi supposer que nous aurons la différence entre les Anciens et les Jeunes, en raison de l'alignement des tableaux (c'est une fonctionnalité de Java GC, quand nous appelons GC, l'alignement des matrices est changé, il est facile d'observer à l'aide de JOL). J'ai été surpris de voir que les performances de devenir le même, en général, et de la décote pour les deux méthodes. Les résultats https://pastebin.com/bTt5SJ8r. Pour celui qui croit en chiffres concrets, le débit du Système.arraycopy est mieux que de l'Objet.clone.

Premier indice:

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

@State(Scope.Benchmark)
@BenchmarkMode(Mode.All)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class CloneVsArraycopy {

    @Param({"10", "1000", "100000"})
    int size;

    int[] source;

    @Setup(Level.Invocation)
    public void setup() {
        source = create(size);
    }

    @Benchmark
    public int[] clone(CloneVsArraycopy cloneVsArraycopy) {
        return cloneVsArraycopy.source.clone();
    }

    @Benchmark
    public int[] arraycopy(CloneVsArraycopy cloneVsArraycopy) {
        int[] dest = new int[cloneVsArraycopy.size];
        System.arraycopy(cloneVsArraycopy.source, 0, dest, 0, dest.length);
        return dest;
    }

    public static void main(String[] args) throws Exception {
        new Runner(new OptionsBuilder()
                .include(CloneVsArraycopy.class.getSimpleName())
                .warmupIterations(20)
                .measurementIterations(20)
                .forks(20)
                .build()).run();
    }

    private static int[] create(int size) {
        int[] a = new int[size];
        for (int i = 0; i < a.length; i++) {
            a[i] = ThreadLocalRandom.current().nextInt();
        }
        return a;
    }

}

L'exécution de ce test sur mon PC, j'ai eu ce - https://pastebin.com/ny56Ag1z. La différence n'est pas si grand, mais existe toujours.

Le deuxième point de référence, je seulement ajouter un paramètre -XX:-ReduceBulkZeroing et a obtenu ce résultats https://pastebin.com/ZDAeQWwx. Non, nous voyons que pour les Jeunes Gen la différence est beaucoup moins aussi.

Dans le troisième test, j'ai changé seulement la méthode de configuration et activer ReduceBulkZeroing option arrière:

@Setup(Level.Invocation)
public void setup() {
    source = create(size);
    // try to move to old gen/align array
    for (int i = 0; i < 10; ++i) {
        System.gc();
    }
}

La différence est beaucoup moins (peut-être dans l'erreur d'intervalle) - https://pastebin.com/bTt5SJ8r.

Avertissement

C'est aussi peut-être erroné. Vous devez vérifier sur votre propre.

En plus

Je pense qu'il est intéressant de se pencher sur les critères de référence du processus:

# Benchmark: org.egorlitvinenko.arrays.CloneVsArraycopy.arraycopy
# Parameters: (size = 50000)

# Run progress: 0,00% complete, ETA 00:07:30
# Fork: 1 of 5
# Warmup Iteration   1: 8,870 ops/ms
# Warmup Iteration   2: 10,912 ops/ms
# Warmup Iteration   3: 16,417 ops/ms <- Hooray!
# Warmup Iteration   4: 17,924 ops/ms <- Hooray!
# Warmup Iteration   5: 17,321 ops/ms <- Hooray!
# Warmup Iteration   6: 16,628 ops/ms <- What!
# Warmup Iteration   7: 14,286 ops/ms <- No, stop, why!
# Warmup Iteration   8: 13,928 ops/ms <- Are you kidding me?
# Warmup Iteration   9: 13,337 ops/ms <- pff
# Warmup Iteration  10: 13,499 ops/ms
Iteration   1: 13,873 ops/ms
Iteration   2: 16,177 ops/ms
Iteration   3: 14,265 ops/ms
Iteration   4: 13,338 ops/ms
Iteration   5: 15,496 ops/ms

Pour L'Objet.clone

# Benchmark: org.egorlitvinenko.arrays.CloneVsArraycopy.clone
# Parameters: (size = 50000)

# Run progress: 0,00% complete, ETA 00:03:45
# Fork: 1 of 5
# Warmup Iteration   1: 8,761 ops/ms
# Warmup Iteration   2: 12,673 ops/ms
# Warmup Iteration   3: 20,008 ops/ms
# Warmup Iteration   4: 20,340 ops/ms
# Warmup Iteration   5: 20,112 ops/ms
# Warmup Iteration   6: 20,061 ops/ms
# Warmup Iteration   7: 19,492 ops/ms
# Warmup Iteration   8: 18,862 ops/ms
# Warmup Iteration   9: 19,562 ops/ms
# Warmup Iteration  10: 18,786 ops/ms

Nous pouvons observer la performance downgrade ici pour le Système.arraycopy. J'ai vu des image similaire pour les cours d'eau et il y avait un bug dans les compilateurs. Je suppose que cela pourrait être un bug dans les compilateurs trop. De toute façon, il est étrange qu'au bout de 3 de chauffe de la performance des décotes.

Mise à JOUR

Ce qui est sur le typage

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

@State(Scope.Benchmark)
@BenchmarkMode(Mode.All)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class CloneVsArraycopyObject {

    @Param({"100"})
    int size;

    AtomicLong[] source;

    @Setup(Level.Invocation)
    public void setup() {
        source = create(size);
    }

    @Benchmark
    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
    public AtomicLong[] clone(CloneVsArraycopyObject cloneVsArraycopy) {
        return cloneVsArraycopy.source.clone();
    }

    @Benchmark
    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
    public AtomicLong[] arraycopy(CloneVsArraycopyObject cloneVsArraycopy) {
        AtomicLong[] dest = new AtomicLong[cloneVsArraycopy.size];
        System.arraycopy(cloneVsArraycopy.source, 0, dest, 0, dest.length);
        return dest;
    }

    public static void main(String[] args) throws Exception {
        new Runner(new OptionsBuilder()
                .include(CloneVsArraycopyObject.class.getSimpleName())
                .jvmArgs("-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining", "-XX:-ReduceBulkZeroing")
                .warmupIterations(10)
                .measurementIterations(5)
                .forks(5)
                .build())
                .run();
    }

    private static AtomicLong[] create(int size) {
        AtomicLong[] a = new AtomicLong[size];
        for (int i = 0; i < a.length; i++) {
            a[i] = new AtomicLong(ThreadLocalRandom.current().nextLong());
        }
        return a;
    }

}

La différence n'est pas observée - https://pastebin.com/ufxCZVaC. Je suppose que l'explication est simple, en tant que Système.arraycopy est chaud intrinsèque dans ce cas, la mise en œuvre réelle serait juste inline sans typecheking, etc.

Note

Je suis d'accord avec Radiodef vous pourriez trouver intéressant de lire les post de blog, l'auteur de ce blog en est le créateur (ou l'un des créateurs) de la JMH.

2voto

Sheepy Points 1538

Aussi loin que la copie est concerné System.arrayCopy est la plus rapide, alors et maintenant.

  • System.arrayCopy ne pas créer un nouveau tableau et ne peut pas être battu dans les premières vitesse de copie.
  • Arrays.copyOf simplement crée un tableau et des appels arrayCopy. La commodité.
  • Array.clone est très efficace, mais il faut valider les données copiées à tous de cache du processeur.

Si vous pouvez vous le code de réutiliser le tableau avec l' arrayCopy, d'aller pour elle. Sinon j'ai personnellement recommander copyOf compte tenu de la tendance à la hausse de cœurs de processeur et parce que le clone est considérée comme la vieille et de la problématique en général - le principal point de l' Josh Bloch blog qui a commencé cette question.

Contrairement à la pensée, de la copie des boucles (type coché ou non) sont pas bytecode Java et n'est pas optimisable par hotspot. Les boucles sont codés en C++ et est de faible niveau de la jvm.


Réponse longue:

Cette réponse est fondée sur et lien vers le code source de OpenJDK 8, qui, autant que je sais que devrait être la même pour le Soleil.

Tableau copie est peut-être plus compliqué que ce que la plupart des gens pensent. Sur C au niveau du code, il peut être divisé en trois cas:

  1. Primitifs les tableaux sont copiés directement avec une copie de la boucle.
  2. Tableaux d'objets de même classe ou sous-classe de super-classe de tableau, sont également copiés directement.
  3. Sinon, entre les tableaux de différentes classes, un type de contrôle est effectué sur chaque élément.

La vitesse absolue de la copie d'un tableau va donc varier considérablement dépend du type de pile. La vitesse relative des trois clone méthodes, il n'est pas, cependant, puisqu'ils conduisent à la même copie de la boucle, un inline C++ ou de l'assemblée de la boucle. Ainsi, les différentes dans la vitesse est causé principalement par les frais généraux et d'autres facteurs.

  • System.arrayCopy est essentiellement le type de contrôles et de vérifications de longueur, puis tout droit à la copie de la boucle. Dans mes propres tests arrayCopy est toujours plus rapide que les deux autres méthodes bien au-delà de la marge d'erreurs.

  • Arrays.copyOf simplement des appels System.arrayCopy - après la création d'un nouveau tableau. Notez qu'il n'y a pas d'appel de Tableau.clone. Contrairement à Radiodef du commentaire, il n'y a aucune indication que Java 8 contournent le zéro de l'initialisation.

  • Array.clone est intéressant. Il appelle directement tas d'allocation et de la copie de la boucle, avec un minimum de vérifications. Ainsi, son tableau de la création devrait être plus rapide que l' Arrays.copyOf, et sa copie aussi vite qu' System.arrayCopy si pas plus rapide.

Mais dans mes tests, Array.clone est légèrement plus lent que l' copyOf.

Je soupçonne que c'est à cause de la barrière de mémoire après la copie. Comme un constructeur, clone sera assurez-vous que les données copiées sont visibles à tous les threads qui ni System.arrayCopy ni Array.copyOfle faire. Cela signifie Array.clone besoin de dépenser du temps d'attente de cache du PROCESSEUR de mise à jour.

Si cela est vrai, le résultat d' Array.clone vs Arrays.copyOf dépend si le vidage du cache d' clone est plus rapide que les frais généraux de l' copyOf, et devrait être dépendants de la plateforme.

Autre que cela, puisque le clonage toujours le résultat dans un tableau de même type, les trois méthodes en fin de compte utiliser la même copie de la boucle.

Si vous ne souhaitez copier, arrayCopy est toujours la plus rapide, tout simplement parce qu'il ne veut pas créer un nouveau tableau. Pour le reste, si le java liste de diffusion est quelque chose aller près, le choix entre Arrays.copyOf et Array.clone devrait être en grande partie une question de goût.


Mon jmh résultat du test et le code ci-dessous.

  • Un test de retour copié tableau.
  • Deux façon de tests remplacer la source de la copie de la force clone de la copie "nouvelle" des données.
  • NoClone n'a pas de clone de rien et est un critère pour vous assurer le plus haut est le plus rapide.

Comme indiqué, le Clone et CopyOf est un proche de la race et de votre kilométrage peut varier.

/* # Run complete. Total time: 00:06:44

Benchmark                               Mode  Cnt          Score         Error  Units
MyBenchmark.ArrayCloneByteOneWay       thrpt   20    1048588.503 ±    2608.862  ops/s
MyBenchmark.ArrayCloneByteTwoWay       thrpt   20     523782.848 ±    1613.823  ops/s
MyBenchmark.ArrayCloneObjOneWay        thrpt   20     260903.006 ±    1311.827  ops/s
MyBenchmark.ArrayCloneObjTwoWay        thrpt   20     129448.639 ±    1179.122  ops/s
MyBenchmark.ArraysCopyOfByteOneWay     thrpt   20    1065995.804 ±    2197.919  ops/s
MyBenchmark.ArraysCopyOfByteTwoWay     thrpt   20     533025.610 ±    2831.955  ops/s
MyBenchmark.ArraysCopyOfObjOneWay      thrpt   20     266134.565 ±    1536.756  ops/s
MyBenchmark.ArraysCopyOfObjTwoWay      thrpt   20     130821.380 ±     274.325  ops/s
MyBenchmark.NoClone                    thrpt   20  308776528.157 ± 2546848.128  ops/s
MyBenchmark.SystemArrayCopyByteOneWay  thrpt   20    1232733.367 ±    8439.409  ops/s
MyBenchmark.SystemArrayCopyByteTwoWay  thrpt   20     859387.983 ±    1919.359  ops/s
MyBenchmark.SystemArrayCopyObjOneWay   thrpt   20     239532.442 ±     775.193  ops/s
MyBenchmark.SystemArrayCopyObjTwoWay   thrpt   20     167235.661 ±     503.141  ops/s
*/

import java.util.Arrays;
import java.util.Random;
import org.openjdk.jmh.annotations.*;

@Fork(2) @Warmup(iterations = 5, time = 1) @Measurement(iterations = 10, time = 1)
public class Q46230557 {
   private static final int ARRAY_SIZE = 8192;

   @State(Scope.Thread) public static class Data {
      public byte[] bytes = new byte[ ARRAY_SIZE ];
      public Object[] objs = new Object[ ARRAY_SIZE ];
      @Setup public void setup() {
         final Random RNG = new Random();
         RNG.nextBytes( bytes );
         for ( int i = 0 ; i < ARRAY_SIZE ; i++ )
            objs[i] = RNG.nextInt();
      }
   }

   @Benchmark public byte[] NoClone( final Data data ) {
      return data.bytes;
   }

   @Benchmark public byte[] SystemArrayCopyByteOneWay( final Data data ) {
      final byte[] dest = new byte[ ARRAY_SIZE ];
      System.arraycopy( data.bytes, 0, dest, 0, ARRAY_SIZE );
      return dest;
   }

   @Benchmark public byte[] SystemArrayCopyByteTwoWay( final Data data ) {
      final byte[] buf = new byte[ ARRAY_SIZE ];
      System.arraycopy( data.bytes, 0, buf, 0, ARRAY_SIZE );
      System.arraycopy( buf, 0, data.bytes, 0, ARRAY_SIZE );
      return data.bytes;
   }

   @Benchmark public byte[] ArraysCopyOfByteOneWay( final Data data ) {
      return Arrays.copyOf( data.bytes, ARRAY_SIZE );
   }

   @Benchmark public byte[] ArraysCopyOfByteTwoWay( final Data data ) {
      final byte[] buf = Arrays.copyOf( data.bytes, ARRAY_SIZE );
      return data.bytes = Arrays.copyOf( buf, ARRAY_SIZE );
   }

   @Benchmark public byte[] ArrayCloneByteOneWay( final Data data ) {
      return data.bytes.clone();
   }

   @Benchmark public byte[] ArrayCloneByteTwoWay( final Data data ) {
      final byte[] buf = data.bytes.clone();
      return data.bytes = buf.clone();
   }

   @Benchmark public Object[] SystemArrayCopyObjOneWay( final Data data ) {
      final Object[] dest = new Object[ ARRAY_SIZE ];
      System.arraycopy( data.objs, 0, dest, 0, ARRAY_SIZE );
      return dest;
   }

   @Benchmark public Object[] SystemArrayCopyObjTwoWay( final Data data ) {
      final Object[] buf = new Object[ ARRAY_SIZE ];
      System.arraycopy( data.objs, 0, buf, 0, ARRAY_SIZE );
      System.arraycopy( buf, 0, data.objs, 0, ARRAY_SIZE );
      return data.objs;
   }

   @Benchmark public Object[] ArraysCopyOfObjOneWay( final Data data ) {
      return Arrays.copyOf( data.objs, ARRAY_SIZE );
   }

   @Benchmark public Object[] ArraysCopyOfObjTwoWay( final Data data ) {
      final Object[] buf = Arrays.copyOf( data.objs, ARRAY_SIZE );
      return data.objs = Arrays.copyOf( buf, ARRAY_SIZE );
   }

   @Benchmark public Object[] ArrayCloneObjOneWay( final Data data ) {
      return data.objs.clone();
   }

   @Benchmark public Object[] ArrayCloneObjTwoWay( final Data data ) {
      final Object[] buf = data.objs.clone();
      return data.objs = buf.clone();
   }
}

0voto

Paul Smith Points 13

La différence de performances vient du fait de sauter l'étape où la matrice est mise à zéro.

 public static int[] copyUsingArraycopy(int[] original)
{
    // Memory is allocated and zeroed out
    int[] copy = new int[original.Length];
    // Memory is copied
    System.arraycopy(original, 0, copy, 0, original.length);
}

public static int[] copyUsingClone(int[] original)
{
    // Memory is allocated, but not zeroed out
    // Unitialized memory is then copied into
    return (int[])original.clone();
}
 

Cependant, dans les cas où les performances de copie d'un tableau font une différence significative, il est généralement préférable d'utiliser une double mise en mémoire tampon.

 int[] backBuffer = new int[BUFFER_SIZE];
int[] frontBuffer = new int[BUFFER_SIZE];

...

// Swap buffers
int[] temp = frontBuffer;
frontBuffer = backBuffer;
backBuffer = temp;
System.arraycopy(frontBuffer, 0, backBuffer, 0, BUFFER_SIZE);
 

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