Reputation: 2973
There are ways in Marshal
to copy raw buffer allocated by AllocHGlobal
to/from C# array. But my question is, I have two buffers both allocated by AllocHGlobal
, and I want to simply copy one buffer to another.
var buffer1 = Marshal.AllocHGlobal(size);
var buffer2 = Marshal.AllocHGlobal(size);
nativeOpOn(buffer1);
SomeCopy(buffer1, buffer2, size);
I know one possible solution is to P/Invoke system C lib for memcpy
, but then I have to write different P/Invoke for support both Windows and Linux and MacOS, since they might have different P/Invoke stuff.
Upvotes: 3
Views: 1582
Reputation: 1440
If you're running .NET Core or .NET 5+ then using the Unsafe.CopyBlock()
/Unsafe.CopyBlockUnaligned()
methods will be the fastest.
Running the following benchmarks using BenchmarkDotNet
// .NET 6.0.300
[MemoryDiagnoser]
[RPlotExporter]
public unsafe class CopyTests
{
private readonly void* source;
private readonly void* destination;
private const int SIZE = 1024;
public CopyTests()
{
source = NativeMemory.Alloc(SIZE);
destination = NativeMemory.Alloc(SIZE);
}
[Benchmark]
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public void TestUnsafeCopyBlockUnaligned()
{
Unsafe.CopyBlockUnaligned(destination, source, SIZE);
}
[Benchmark]
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public void TestBufferMemoryCopy()
{
Buffer.MemoryCopy(source, destination, SIZE, SIZE);
}
}
yields the following results:
| Method | Mean | Error | StdDev | Allocated |
|----------------------------- |---------:|---------:|---------:|----------:|
| TestUnsafeCopyBlockUnaligned | 16.63 ns | 0.350 ns | 0.360 ns | - |
| TestBufferMemoryCopy | 23.88 ns | 0.264 ns | 0.234 ns | - |
or if you like pretty diagrams:
As you can see, Unsafe.CopyBlockUnaligned()
is around 25% faster.
If you're using IntPtr
you'd obviously have to call myIntPtr.ToPointer()
first and also allow unsafe blocks in your project settings :)
Upvotes: 1
Reputation: 17850
You can use the following approaches:
1) managed buffer:
int size = sizeof(int); // for test purposes
IntPtr buffer1 = Marshal.AllocHGlobal(size);
IntPtr buffer2 = Marshal.AllocHGlobal(size);
Marshal.WriteInt32(buffer1, 0x12345678); // native op (for test purposes)
byte[] bytes = new byte[size]; // managed buffer
Marshal.Copy(buffer1, bytes, 0, size); //buffer1=>bytes
Marshal.Copy(bytes, 0, buffer2, size); //bytes=>buffer2
// use several iterations if needed
int res = Marshal.ReadInt32(buffer2, 0); // for test purposes (res==0x12345678)
2) managed read/write operations via the Marshal.ReadByte/WriteByte
methods or any Marshal.ReadXXXe/WriteXXX
methods (depending of size
). Just repeat these operations in cycle:
for(int i = 0; i < size; i++)
Marshal.WriteByte(buffer2, i, Marshal.ReadByte(buffer1, i));
3) unsafe block (read/write buffers via unsafe pointers)
Upvotes: 1
Reputation: 109587
If you are using .Net 4.6 or later, you can use Buffer.MemoryCopy()
:
unsafe
{
Buffer.MemoryCopy(p1.ToPointer(), p2.ToPointer(), destSizeBytes, sourceBytesToCopy);
}
Of course, it is unsafe
.
Upvotes: 4