Array.Copy und Buffer.BlockCopy machen beide dasselbe, BlockCopy
zielen jedoch auf schnelles primitives Array-Kopieren auf Byte-Ebene ab, während dies Copy
die allgemeine Implementierung ist. Meine Frage ist - unter welchen Umständen sollten Sie verwenden BlockCopy
? Sollten Sie es zu irgendeinem Zeitpunkt verwenden, wenn Sie Arrays vom primitiven Typ kopieren, oder sollten Sie es nur verwenden, wenn Sie für die Leistung codieren? Ist die Verwendung von Buffer.BlockCopy
Over von Natur aus gefährlich Array.Copy
?
124
Marshal.Copy
:-). Nun, verwenden SieArray.Copy
für Referenztypen, komplexe Werttypen und, wenn sich der Typ nicht ändert,Buffer.BlockCopy
für die "Konvertierung" zwischen Werttypen, Bytearrays und Bytemagie. F.ex. Die Kombination mitStructLayout
ist ziemlich mächtig, wenn Sie wissen, was Sie tun. In Bezug auf die Leistung scheint ein nicht verwalteter Aufruf vonmemcpy
/cpblk
der schnellste zu sein - siehe code4k.blogspot.nl/2010/10/… .byte[]
. Es gab keinen Unterschied in der Release-Version. ManchmalArray.Copy
, manchmalBuffer.BlockCopy
(etwas) schneller.Array.Copy
ist eher eine spezialisierte Version - zum Beispiel kann sie nur die gleichen Rang-Arrays kopieren.Antworten:
Da die Parameter
Buffer.BlockCopy
eher bytebasiert als indexbasiert sind, ist es wahrscheinlicher, dass Sie Ihren Code vermasseln, als wenn Sie ihn verwendenArray.Copy
. Daher würde ich ihn nurBuffer.BlockCopy
in einem leistungskritischen Abschnitt meines Codes verwenden.quelle
UInt16
ist zwei Bytes pro Element. Wenn Sie dieses Array zusammen mit der Anzahl der Elemente im Array an BlockCopy übergeben, wird natürlich nur die Hälfte des Arrays kopiert. Damit dies ordnungsgemäß funktioniert, müssen Sie die Anzahl der Elemente mal die Größe jedes Elements (2) als Längenparameter übergeben. msdn.microsoft.com/en-us/library/… und suchen SieINT_SIZE
in den Beispielen nach.Auftakt
Ich bin spät auf der Party, aber mit 32.000 Views lohnt es sich, das richtig zu machen. Der größte Teil des Mikrobenchmarking-Codes in den bisher veröffentlichten Antworten weist einen oder mehrere schwerwiegende technische Mängel auf, darunter das Nichtverschieben von Speicherzuordnungen aus den Testschleifen (was zu schwerwiegenden GC-Artefakten führt), das Nicht-Testen von variablen vs. deterministischen Ausführungsabläufen, JIT-Aufwärmen, und nicht die Variabilität innerhalb des Tests verfolgen. Darüber hinaus testeten die meisten Antworten nicht die Auswirkungen unterschiedlicher Puffergrößen und unterschiedlicher primitiver Typen (in Bezug auf 32-Bit- oder 64-Bit-Systeme). Um diese Frage umfassender zu beantworten, habe ich sie an ein von mir entwickeltes benutzerdefiniertes Microbenchmarking-Framework angeschlossen, das die meisten gängigen "Fallstricke" so weit wie möglich reduziert. Die Tests wurden im .NET 4.0-Release-Modus sowohl auf einem 32-Bit-Computer als auch auf einem 64-Bit-Computer ausgeführt. Die Ergebnisse wurden über 20 Testläufe gemittelt, in denen jeder Lauf 1 Million Versuche pro Methode aufwies. Die getesteten primitiven Typen waren
byte
(1 Byte),int
(4 Byte) unddouble
(8 Byte). Drei Methoden wurden getestet:Array.Copy()
,Buffer.BlockCopy()
und einfach per-Indexzuordnung in einer Schleife. Die Daten sind zu umfangreich, um hier veröffentlicht zu werden, daher werde ich die wichtigen Punkte zusammenfassen.Die Imbissbuden
Array.Copy()
oderBuffer.BlockCopy()
alle drei primitiven Typen, die sowohl auf 32-Bit- als auch auf 64-Bit-Computern getestet wurden. Darüber hinaus weist die explizite Schleifenkopierroutine im Vergleich zu den beiden Alternativen eine deutlich geringere Variabilität der Leistung auf. Die gute Leistung ist fast sicher auf die Lokalität der Referenz zurückzuführen, die vom Speicher-Caching der CPU L1 / L2 / L3 in Verbindung mit keinem Overhead für Methodenaufrufe ausgenutzt wird.double
Puffer auf 32-Bit-Computern : Die explizite Schleifenkopierroutine ist für alle getesteten Puffergrößen bis zu 100.000 besser als beide Alternativen. Die Verbesserung ist 3-5% besser als bei den anderen Methoden. Dies liegt daran, dass die Leistung vonArray.Copy()
und dasBuffer.BlockCopy()
Übergeben der nativen 32-Bit-Breite vollständig beeinträchtigt wird. Daher gehe ich davon aus, dass der gleiche Effekt auch fürlong
Puffer gelten würde .byte[]
, wo das explizite Kopieren von Schleifen bei großen Puffergrößen 7x oder langsamer werden kann.Array.Copy()
undBuffer.BlockCopy()
nahezu identisch durchgeführt. Im DurchschnittArray.Copy()
scheint eine sehr leichte Kante von etwa 2% oder weniger Zeit in Anspruch genommen zu haben (aber 0,2% - 0,5% besser ist typisch), obwohlBuffer.BlockCopy()
sie gelegentlich geschlagen wurde. Hat aus unbekannten GründenBuffer.BlockCopy()
eine deutlich höhere Variabilität innerhalb des Tests alsArray.Copy()
. Dieser Effekt konnte nicht beseitigt werden, obwohl ich mehrere Abschwächungen versuchte und keine funktionsfähige Theorie darüber hatte, warum.Array.Copy()
es sich um eine "intelligentere", allgemeinere und viel sicherere Methode handelt, die nicht nur geringfügig schneller ist und im Durchschnitt eine geringere Variabilität aufweist, sollte sieBuffer.BlockCopy()
in fast allen gängigen Fällen vorgezogen werden. Der einzige AnwendungsfallBuffer.BlockCopy()
, der wesentlich besser ist, besteht darin, dass die Werttypen des Quell- und Zielarrays unterschiedlich sind (wie in Ken Smiths Antwort ausgeführt). Obwohl dieses Szenario nicht üblich ist,Array.Copy()
kann es hier aufgrund des kontinuierlichen "sicheren" Wertgusstyps im Vergleich zum direkten Gießen von sehr schlecht abschneidenBuffer.BlockCopy()
.Array.Copy()
die schneller sind alsBuffer.BlockCopy()
beim Kopieren von Arrays gleichen Typs, finden Sie hier .quelle
Array.Clear()
zuerst beginnt (der Einstellung eine explizite Schleifenbelegung Clearing eines Arrays zu schlagenfalse
,0
odernull
). Dies steht im Einklang mit meinen ähnlichen Befunden oben. Diese separaten Benchmarks wurden hier online entdeckt: 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
Wenn die Kopiergröße jedoch> ~ 20 Byte ist, ist die explizite Schleife erheblich langsamer.Ein weiteres Beispiel für die Verwendung
Buffer.BlockCopy()
ist, wenn Sie ein Array von Grundelementen (z. B. Kurzschlüsse) erhalten und dieses in ein Array von Bytes konvertieren müssen (z. B. für die Übertragung über ein Netzwerk). Ich verwende diese Methode häufig, wenn ich mit Audio vom Silverlight AudioSink arbeite. Das Beispiel wird alsshort[]
Array bereitgestellt, Sie müssen es jedoch in einbyte[]
Array konvertieren, wenn Sie das Paket erstellen, an das Sie sendenSocket.SendAsync()
. Sie könnenBitConverter
das Array einzeln verwenden und durchlaufen, aber es ist viel schneller (ungefähr 20x in meinen Tests), nur um dies zu tun:Und der gleiche Trick funktioniert auch umgekehrt:
Dies ist ungefähr so nah wie in sicherem C # an der
(void *)
in C und C ++ üblichen Art der Speicherverwaltung.quelle
MemoryMarshal.AsBytes<T>
oderMemoryMarshal.Cast<TFrom, TTo>
lassen Sie Ihre Sequenz eines Primitivs als Sequenz eines anderen Primitivs interpretieren.Aufgrund meiner Tests ist die Leistung kein Grund, Buffer.BlockCopy Array.Copy vorzuziehen. Nach meinem Test ist Array.Copy tatsächlich schneller als Buffer.BlockCopy.
Beispielausgabe:
quelle
ArrayCopy ist schlauer als BlockCopy. Es wird herausgefunden, wie Elemente kopiert werden, wenn Quelle und Ziel dasselbe Array sind.
Wenn wir ein int-Array mit 0,1,2,3,4 füllen und anwenden:
Am Ende haben wir erwartungsgemäß 0,0,1,2,3.
Versuchen Sie dies mit BlockCopy und wir erhalten: 0,0,2,3,4. Wenn ich
array[0]=-1
danach zuweise, wird es wie erwartet -1,0,2,3,4, aber wenn die Array-Länge gerade ist, wie 6, erhalten wir -1,256,2,3,4,5. Gefährliches Zeug. Verwenden Sie BlockCopy nur zum Kopieren eines Byte-Arrays in ein anderes.Es gibt einen anderen Fall, in dem Sie Array.Copy nur verwenden können: Wenn die Arraygröße länger als 2 ^ 31 ist. Array.Copy hat eine Überladung mit einem Größenparameter
long
. BlockCopy hat das nicht.quelle
Um dieses Argument abzuwägen: Wenn man nicht aufpasst, wie sie diesen Benchmark erstellen, können sie leicht irregeführt werden. Ich habe einen sehr einfachen Test geschrieben, um dies zu veranschaulichen. Wenn ich in meinem Test unten die Reihenfolge meiner Tests zwischen dem Starten von Buffer.BlockCopy zuerst oder Array.Copy vertausche, ist diejenige, die zuerst geht, fast immer die langsamste (obwohl es eine enge ist). Dies bedeutet, dass ich aus einer Reihe von Gründen, auf die ich nicht eingehen werde, die Tests einfach mehrmals ausführen werde, insbesondere nacheinander, keine genauen Ergebnisse liefert.
Ich habe darauf zurückgegriffen, den Test so beizubehalten, wie er bei jeweils 1000000 Versuchen für ein Array von 1000000 sequentiellen Doppeln ist. Allerdings ignoriere ich dann dann die ersten 900000 Zyklen und mittle den Rest. In diesem Fall ist der Puffer überlegen.
https://github.com/chivandikwa/Random-Benchmarks
quelle
Ich möchte nur meinen Testfall hinzufügen, der erneut zeigt, dass BlockCopy keinen 'PERFORMANCE'-Vorteil gegenüber Array.Copy hat. Sie scheinen im Release-Modus auf meinem Computer dieselbe Leistung zu haben (beide benötigen ungefähr 66 ms, um 50 Millionen Ganzzahlen zu kopieren). Im Debug-Modus ist BlockCopy nur geringfügig schneller.
quelle