What is the best way to copy an array?
Tags: csharp, algorithms, beginners, dotnet
Published: 2023-10-07 10:30:58

Once in a while, you come across a problem where you have an array `a` and you want to copy its values to array `b`. You use a technique you know works best or you use what is commonly suggested in the public forums.

But, have you ever wondered what is the fastest way you can copy an array? Well in this article we will benchmark different ways you can copy an array

**Prelude**
- I'll be using .Net 7.0
- We will copy values from the 0 index.
- You can find the Complete Source Code [here](https://github.com/j0nimost/ArrayCopyBenchmark.git)

## Setup
I am using BenchmarkDotNet version `0.13.9`, for testing I have 2 arrays `Destination` and a function that yields a small array and a big array.

```c#
private int[] Destination;

[GlobalSetup]
public void GlobalSetup()
{
    Destination = Enumerable.Range(0, 2048).ToArray();
}
public IEnumerable<object> GetIntArray()
{
    yield return Enumerable.Range(0, 10).ToArray();
    yield return Enumerable.Range(0, 1024).ToArray();
}
```
We will copy the yielded arrays to the big array
## ForLoop
A common way to copy an array is by use of a for loop like so;
```c#
[Benchmark]
[ArgumentsSource(nameof(GetIntArray))]
public void ForLoopCopy(int[] array)
{
    for (int i = 0; i < array.Length; i++)
    {
        Destination[i] = array[i];
    }
}
```
If benchmark this test we get this result;
![ForLoop Benchmark](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sdillridf4crx9z9efgv.png)

That's fairly quick, I bet we can make it faster.

## Array.Copy
Another technique commonly used to copy arrays is `Array.Copy`

```c#
[Benchmark]
[ArgumentsSource(nameof(GetIntArray))]
public void ArrayCopy(int[] array)
{
    Array.Copy(array, Destination, array.Length);
}
```
It's quite simple to set up and how does it fair compared to the for loop? 

![Array Copy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lr6l0twyli5bu9u8dc7z.png)

that is a HUGE Leap, for large arrays we have a 14X improvement, and for small arrays, it's 2x improvement. Let's try another approach

## Buffer.BlockCopy
This is also another way you can copy arrays.

```c#
[Benchmark]
[ArgumentsSource(nameof(GetIntArray))]
public void BufferBlockCopy(int[] array)
{
    Buffer.BlockCopy(array, 0, Destination, 0, array.Length * 4);
}
```
Why do we multiply the length by 4? Well, BlockCopy copies bytes and int has 4 bytes.

![BlockCopy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4xloqq61h91ed1n59qy.png)

It's not as fast as the ArrayCopy for big arrays but it's roughly 2X slower than the Forloop for small arrays.

## Span.CopyTo
Spans are contiguous regions of memory, the elements are tightly packed and aligned in one part of the memory. This means reading data can be much faster.

So lets create one;
```c#
[Benchmark]
[ArgumentsSource(nameof(GetIntArray))]
public void SpanCopy(int[] array)
{
    array.AsSpan().CopyTo(Destination);
}
```

**Caveate**: We have to invoke AsSpan method to cast the array to a Span. 


![Span](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oy94w9r44lknr3zdqf66.png)
It's performance is quite similar to the `Array.Copy`

## Marshal.Copy
In Marshal copy we have to copy an array from managed memory to an unmanaged memory (IntPtr) and then copy from the unmanaged memory back to managed memory. So the code looks like this.
```c#
[Benchmark]
[ArgumentsSource(nameof(GetIntArray))]
public void MarshalCopy(int[] array)
{
    int size = 4 * array.Length;
    IntPtr ptr = Marshal.AllocHGlobal(size);
    Marshal.Copy(array, 0, ptr, array.Length);
    Marshal.Copy(ptr, Destination, 0, array.Length);
    Marshal.FreeHGlobal(ptr);
}
```

How does it perform? 

![MarshalCopy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pl09q0vdpojqf0n16t8n.png)

Well significantly poorer than `Array.Copy`, though faster than for loop for large arrays, it's performance is worse for smaller arrays.

## Summary
What should you use, it is pretty clear that `Array.Copy` is more than sufficient for copying one array to another.