Prelude
I'm joining the party late, but with 32k views, it's worth getting this right. Most of the microbenchmarking code in the posted answers thus far suffer from one or more severe technical flaws, including not moving memory allocations out of the test loops (which introduces severe GC artifacts), not testing variable vs. deterministic execution flows, JIT warmup, and not tracking intra-test variability. In addition, most answers did not test the effects of varying buffer sizes and varying primitive types (with respect to either 32-bit or 64-bit systems). To address this question more comprehensively, I hooked it up to a custom microbenchmarking framework I developed that reduces most of the common "gotchas" to the extent possible. Tests were run in .NET 4.0 Release mode on both a 32-bit machine and a 64-bit machine. Results were averaged over 20 testing runs, in which each run had 1 million trials per method. Primitive types tested were byte
(1 byte), int
(4 bytes), and double
(8 bytes). Three methods were tested: Array.Copy()
, Buffer.BlockCopy()
, and simple per-index assignment in a loop. The data is too voluminous to post here, so I will summarize the important points.
The Takeaways
Array.Copy()
or Buffer.BlockCopy()
for all 3 primitive types tested on both 32-bit and 64-bit machines. Additionly, the explicit loop copy routine has noticeably lower variability in performance compared to the two alternatives. The good performance is almost surely due to locality of reference exploited by CPU L1/L2/L3 memory caching in conjunction with no method call overhead.
double
buffers on 32-bit machines only: The explicit loop copy routine is better than both alternatives for all buffer sizes tested up to 100k. The improvement is 3-5% better than the other methods. This is because the performance of Array.Copy()
and Buffer.BlockCopy()
become totally degraded upon passing the native 32-bit width. Thus I assume the same effect would apply to long
buffers as well.byte[]
, where explicit loop copying can become 7x or more slower at large buffer sizes.Array.Copy()
and Buffer.BlockCopy()
performed almost identically. On average, Array.Copy()
seems to have a very slight edge of about 2% or less time taken (but 0.2% - 0.5% better is typical), although Buffer.BlockCopy()
did occasionally beat it. For unknown reasons, Buffer.BlockCopy()
has noticeably higher intra-test variability than Array.Copy()
. This effect could not be eliminated despite me trying multiple mitigations and not having an operable theory on why.Array.Copy()
is a "smarter", more general, and much safer method, in addition to being very slightly faster and having less variability on average, it should be preferred to Buffer.BlockCopy()
in almost all common cases. The only use case where Buffer.BlockCopy()
will be significantly better is when the source and destination array value types are different (as pointed out in Ken Smith's answer). While this scenario is not common, Array.Copy()
can perform very poorly here due to the continual "safe" value type casting, compared to the direct casting of Buffer.BlockCopy()
.Array.Copy()
is faster than Buffer.BlockCopy()
for same-type array copying can be found here.Another example of when it makes sense to use Buffer.BlockCopy()
is when you're provided with an array of primitives (say, shorts), and need to convert it to an array of bytes (say, for transmission over a network). I use this method frequently when dealing with audio from the Silverlight AudioSink. It provides the sample as a short[]
array, but you need to convert it to a byte[]
array when you're building the packet that you submit to Socket.SendAsync()
. You could use BitConverter
, and iterate through the array one-by-one, but it's a lot faster (about 20x in my testing) just to do this:
Buffer.BlockCopy(shortSamples, 0, packetBytes, 0, shortSamples.Length * sizeof(short)).
And the same trick works in reverse as well:
Buffer.BlockCopy(packetBytes, readPosition, shortSamples, 0, payloadLength);
This is about as close as you get in safe C# to the (void *)
sort of memory management that's so common in C and C++.
Since the parameters to Buffer.BlockCopy
are byte-based rather than index-based, you're more likely to screw up your code than if you use Array.Copy
, so I would only use Buffer.BlockCopy
in a performance-critical section of my code.
Based on my testing, performance is not a reason to prefer Buffer.BlockCopy over Array.Copy. From my testing Array.Copy is actually faster than Buffer.BlockCopy.
var buffer = File.ReadAllBytes(...);
var length = buffer.Length;
var copy = new byte[length];
var stopwatch = new Stopwatch();
TimeSpan blockCopyTotal = TimeSpan.Zero, arrayCopyTotal = TimeSpan.Zero;
const int times = 20;
for (int i = 0; i < times; ++i)
{
stopwatch.Start();
Buffer.BlockCopy(buffer, 0, copy, 0, length);
stopwatch.Stop();
blockCopyTotal += stopwatch.Elapsed;
stopwatch.Reset();
stopwatch.Start();
Array.Copy(buffer, 0, copy, 0, length);
stopwatch.Stop();
arrayCopyTotal += stopwatch.Elapsed;
stopwatch.Reset();
}
Console.WriteLine("bufferLength: {0}", length);
Console.WriteLine("BlockCopy: {0}", blockCopyTotal);
Console.WriteLine("ArrayCopy: {0}", arrayCopyTotal);
Console.WriteLine("BlockCopy (average): {0}", TimeSpan.FromMilliseconds(blockCopyTotal.TotalMilliseconds / times));
Console.WriteLine("ArrayCopy (average): {0}", TimeSpan.FromMilliseconds(arrayCopyTotal.TotalMilliseconds / times));
Example Output:
bufferLength: 396011520
BlockCopy: 00:00:02.0441855
ArrayCopy: 00:00:01.8876299
BlockCopy (average): 00:00:00.1020000
ArrayCopy (average): 00:00:00.0940000
ArrayCopy is smarter than BlockCopy. It figures out how to copy elements if the source and destination are the same array.
If we populate an int array with 0,1,2,3,4 and apply:
Array.Copy(array, 0, array, 1, array.Length - 1);
we end up with 0,0,1,2,3 as expected.
Try this with BlockCopy and we get: 0,0,2,3,4. If I assign array[0]=-1
after that, it becomes -1,0,2,3,4 as expected, but if the array length is even, like 6, we get -1,256,2,3,4,5. Dangerous stuff. Don't use BlockCopy other than for copying one byte array into another.
There is another case where you can only use Array.Copy: if the array size is longer than 2^31. Array.Copy has an overload with a long
size parameter. BlockCopy does not have that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With