Array.Copy et Buffer.BlockCopy font tous deux la même chose, mais BlockCopy
visent à une copie rapide de tableaux primitifs au niveau des octets, alors que Copy
c'est l'implémentation à usage général. Ma question est - dans quelles circonstances devriez-vous utiliser BlockCopy
? Devez-vous l'utiliser à tout moment lorsque vous copiez des tableaux de types primitifs, ou devriez-vous l'utiliser uniquement si vous codez pour les performances? Y a-t-il quelque chose de intrinsèquement dangereux à utiliser Buffer.BlockCopy
over Array.Copy
?
124
Marshal.Copy
:-). Eh bien, utilisezArray.Copy
pour les types de référence, les types de valeurs complexes et si le type ne change pas,Buffer.BlockCopy
pour la "conversion" entre les types de valeurs, les tableaux d'octets et la magie d'octets. Par ex. la combinaison avecStructLayout
est assez puissante si vous savez ce que vous faites. En ce qui concerne les performances, il semble qu'un appel non géré versmemcpy
/cpblk
soit le plus rapide pour cela - voir code4k.blogspot.nl/2010/10/… .byte[]
. Il n'y avait aucune différence dans la version Release. ParfoisArray.Copy
, parfoisBuffer.BlockCopy
(légèrement) plus vite.Array.Copy
est plutôt une version spécialisée - par exemple, elle ne peut copier que les mêmes tableaux de rangs.Réponses:
Étant donné que les paramètres
Buffer.BlockCopy
sont basés sur des octets plutôt que sur des index, vous êtes plus susceptible de bousiller votre code que si vous les utilisezArray.Copy
, donc je ne les utiliserais queBuffer.BlockCopy
dans une section critique pour les performances de mon code.la source
UInt16
c'est deux octets par élément. Si vous passez ce tableau à BlockCopy avec le nombre d'éléments du tableau, bien sûr, seule la moitié du tableau sera copiée. Pour que cela fonctionne correctement, vous devez passer le nombre d'éléments multiplié par la taille de chaque élément (2) comme paramètre de longueur. msdn.microsoft.com/en-us/library/… et recherchezINT_SIZE
dans les exemples.Prélude
Je rejoins la fête tardivement, mais avec 32k vues, cela vaut la peine de bien faire les choses. La plupart du code de microbenchmarking dans les réponses publiées jusqu'à présent souffrent d'une ou plusieurs failles techniques graves, notamment le fait de ne pas déplacer les allocations de mémoire hors des boucles de test (ce qui introduit de graves artefacts GC), de ne pas tester les flux d'exécution variables ou déterministes, le préchauffage JIT, et ne pas suivre la variabilité intra-test. En outre, la plupart des réponses ne testaient pas les effets des différentes tailles de tampon et des différents types de primitifs (par rapport aux systèmes 32 bits ou 64 bits). Pour aborder cette question de manière plus complète, je l'ai connectée à un cadre de microbenchmarking personnalisé que j'ai développé et qui réduit la plupart des «pièges» courants dans la mesure du possible. Les tests ont été exécutés en mode de version .NET 4.0 sur une machine 32 bits et une machine 64 bits. Les résultats ont été en moyenne sur 20 séries de tests, dans lesquelles chaque série comportait 1 million d'essais par méthode. Les types primitifs testés étaient
byte
(1 octet),int
(4 octets) etdouble
(8 octets). Trois méthodes ont été testées:Array.Copy()
,Buffer.BlockCopy()
, et simple affectation par index dans une boucle. Les données sont trop volumineuses pour être publiées ici, je vais donc résumer les points importants.Les plats à emporter
Array.Copy()
ou l' autre ouBuffer.BlockCopy()
pour les 3 types primitifs testés sur les machines 32 bits et 64 bits. De plus, la routine de copie de boucle explicite présente une variabilité de performances nettement inférieure par rapport aux deux alternatives. Les bonnes performances sont presque sûrement dues à la localité de référence exploitée par la mise en cache de la mémoire CPU L1 / L2 / L3 en conjonction avec aucune surcharge d'appel de méthode.double
tampons sur les machines 32 bits uniquement : la routine de copie en boucle explicite est meilleure que les deux alternatives pour toutes les tailles de tampon testées jusqu'à 100 Ko. L'amélioration est de 3 à 5% meilleure que les autres méthodes. Cela est dû au fait que les performances deArray.Copy()
etBuffer.BlockCopy()
deviennent totalement dégradées lors du dépassement de la largeur native de 32 bits. Je suppose donc que le même effet s'appliquerait également auxlong
tampons.byte[]
, où la copie en boucle explicite peut devenir 7x ou plus lente avec de grandes tailles de tampon.Array.Copy()
etBuffer.BlockCopy()
effectué presque de manière identique. En moyenne,Array.Copy()
semble avoir un très léger avantage d'environ 2% ou moins de temps (mais 0,2% à 0,5% de mieux est typique), bien qu'il l'Buffer.BlockCopy()
ait parfois battu. Pour des raisons inconnues,Buffer.BlockCopy()
a une variabilité intra-test sensiblement plus élevée queArray.Copy()
. Cet effet n'a pas pu être éliminé bien que j'aie essayé de multiples atténuations et que je n'ai pas de théorie exploitable sur le pourquoi.Array.Copy()
s'agit d'une méthode "plus intelligente", plus générale et beaucoup plus sûre, en plus d'être très légèrement plus rapide et d'avoir moins de variabilité en moyenne, elle devrait être préféréeBuffer.BlockCopy()
dans presque tous les cas courants. Le seul cas d'utilisation où ceBuffer.BlockCopy()
sera nettement meilleur est celui où les types de valeurs de tableau source et destination sont différents (comme indiqué dans la réponse de Ken Smith). Bien que ce scénario ne soit pas courant, ilArray.Copy()
peut fonctionner très mal ici en raison de la conversion continue de type valeur «sûre», par rapport à la distribution directe deBuffer.BlockCopy()
.Array.Copy()
sont plus rapides queBuffer.BlockCopy()
pour la copie de tableau de même type peuvent être trouvées ici .la source
Array.Clear()
première commence à battre une compensation d'affectation explicite de la boucle d'un tableau (réglage àfalse
,0
ounull
). Ceci est cohérent avec mes conclusions similaires ci-dessus. Ces benchmarks séparés ont été découverts en ligne ici: manski.net/2012/12/net-array-clear-vs-arrayx-0-performanceLoop Results for 1000000 iterations 17.9515ms. Buffer.BlockCopy Results for 1000000 iterations 39.8937ms. Array.Copy Results for 1000000 iterations 45.9059ms
Cependant, si la taille de la copie> ~ 20 octets, la boucle explicite est nettement plus lente.Un autre exemple où il est judicieux d'utiliser
Buffer.BlockCopy()
est lorsque vous êtes fourni avec un tableau de primitives (par exemple, des courts-circuits) et que vous devez le convertir en un tableau d'octets (par exemple, pour une transmission sur un réseau). J'utilise fréquemment cette méthode lorsque je traite l'audio du Silverlight AudioSink. Il fournit l'exemple sous forme deshort[]
tableau, mais vous devez le convertir enbyte[]
tableau lorsque vous créez le paquet auquel vous soumettezSocket.SendAsync()
. Vous pouvez utiliserBitConverter
et parcourir le tableau un par un, mais c'est beaucoup plus rapide (environ 20x dans mes tests) juste pour faire ceci:Et la même astuce fonctionne également en sens inverse:
C'est à peu près aussi proche que vous obtenez en C # sécurisé du
(void *)
type de gestion de la mémoire qui est si courant en C et C ++.la source
MemoryMarshal.AsBytes<T>
ouMemoryMarshal.Cast<TFrom, TTo>
vous permet d'interpréter votre séquence d'une primitive comme une séquence d'une autre primitive.D'après mes tests, les performances ne sont pas une raison pour préférer Buffer.BlockCopy à Array.Copy. D'après mes tests, Array.Copy est en fait plus rapide que Buffer.BlockCopy.
Exemple de sortie:
la source
ArrayCopy est plus intelligent que BlockCopy. Il explique comment copier des éléments si la source et la destination sont le même tableau.
Si nous remplissons un tableau int avec 0,1,2,3,4 et appliquons:
nous nous retrouvons avec 0,0,1,2,3 comme prévu.
Essayez ceci avec BlockCopy et nous obtenons: 0,0,2,3,4. Si
array[0]=-1
j'assigne après cela, cela devient -1,0,2,3,4 comme prévu, mais si la longueur du tableau est paire, comme 6, nous obtenons -1,256,2,3,4,5. Des trucs dangereux. N'utilisez BlockCopy que pour copier un tableau d'octets dans un autre.Il existe un autre cas où vous ne pouvez utiliser Array.Copy: si la taille du tableau est plus longue que 2 ^ 31. Array.Copy a une surcharge avec un
long
paramètre de taille. BlockCopy n'a pas cela.la source
Pour peser sur cet argument, si l'on ne fait pas attention à la manière dont ils créent ce repère, ils pourraient facilement être induits en erreur. J'ai écrit un test très simple pour illustrer cela. Dans mon test ci-dessous, si je permute l'ordre de mes tests entre le démarrage de Buffer.BlockCopy en premier ou Array.Copy celui qui commence est presque toujours le plus lent (bien que ce soit un proche). Cela signifie que pour un tas de raisons que je n'entrerai pas dans le simple fait d'exécuter les tests plusieurs fois, les uns après les autres ne donneront pas de résultats précis.
J'ai eu recours au maintien du test tel quel avec 1000000 essais chacun pour un tableau de 1000000 doubles séquentiels. Cependant, je néglige ensuite les 900000 premiers cycles et fait la moyenne du reste. Dans ce cas, le tampon est supérieur.
https://github.com/chivandikwa/Random-Benchmarks
la source
Je veux juste ajouter mon cas de test qui montre à nouveau que BlockCopy n'a aucun avantage 'PERFORMANCE' sur Array.Copy. Ils semblent avoir les mêmes performances en mode release sur ma machine (les deux prennent environ 66 ms pour copier 50 millions d'entiers). En mode débogage, BlockCopy est légèrement plus rapide.
la source