C# - Improving Array Performance with .NET 7
Vercidium
Posted on February 10, 2023
.NET 7 introduced a new static class called NativeMemory, which can be used to allocate unmanaged memory.
Normally when working with arrays in C#, the .NET runtime manages the allocation of memory and frees it when it's no longer used, hence the name managed memory.
When using NativeMemory
, you're on your own. The .NET garbage collector won't free it when you no longer need it, and the .NET runtime won't perform bounds checks when reading or writing to it. The advantage is it's faster to work with as the bounds checks performed by .NET aren't present.
Warning! Allocating 1024 integers and then writing to the 1025th one will overwrite memory that may be in use elsewhere. Be careful!
Usage
Allocate a 1kb block of unmanaged memory:
var byteCount = 1024;
void* data = NativeMemory.Alloc(byteCount);
Allocate and zero a block of unmanaged memory:
void* data = NativeMemory.AllocZeroed(1024);
Resize a block of unmanaged memory to 2kb:
NativeMemory.Realloc(data, 2048);
Free a block of unmanaged memory:
NativeMemory.Free(data);
data = null;
Example - Accumulating an Array
Using managed memory:
// Allocate managed memory
const int LENGTH = 1024;
int[] data = new int[LENGTH];
// Accumulate
int total = 0;
for (int i = 0; i < LENGTH ; i++)
total += data[i];
// Log the result
Console.WriteLine(total);
Using unmanaged memory:
// Allocate unmanaged memory
const int LENGTH = 1024;
int byteCount = LENGTH * sizeof(int);
int* data = (int*)NativeMemory.Alloc(byteCount);
// Option 1
// - Same syntax as the managed example
int total = 0;
for (int i = 0; i < LENGTH; i++)
total += data[i];
// Option 2
// - Different syntax, same functionality
for (int i = 0; i < LENGTH; i++)
total += *(data + i);
// Option 3 -
// - Make a copy of the data pointer, read from it, then increment it
// - This works because pointers are essentially a 32 or 64 bit
// integer, meaning we can increment and compare them
var readPtr = data;
var endPtr = data + LENGTH;
while (readPtr < endPtr)
{
total += *readPtr;
readPtr++;
}
// Log the result
Console.WriteLine(total);
Benchmarks
The above managed and unmanaged examples were benchmarked using BenchmarkDotNet, with an array length of 32768.
Posted on February 10, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.