我们如何复制由 Marshal.AllocHGlobal 分配的两个缓冲区?
How can we copy two buffers both allocated by Marshal.AllocHGlobal?
Marshal
中有一些方法可以复制 AllocHGlobal
to/from C# 数组分配的原始缓冲区。但我的问题是,我有两个缓冲区都由 AllocHGlobal
分配,我想简单地将一个缓冲区复制到另一个缓冲区。
var buffer1 = Marshal.AllocHGlobal(size);
var buffer2 = Marshal.AllocHGlobal(size);
nativeOpOn(buffer1);
SomeCopy(buffer1, buffer2, size);
我知道一种可能的解决方案是 P/Invoke 用于 memcpy
的系统 C 库,但是我必须编写不同的 P/Invoke 来支持 Windows 和 Linux 和 MacOS,因为它们可能有不同的 P/Invoke 内容。
您可以使用以下方法:
1) 托管缓冲区:
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) 通过 Marshal.ReadByte/WriteByte
方法或任何 Marshal.ReadXXXe/WriteXXX
方法(取决于 size
)管理 read/write 操作。循环重复这些操作即可:
for(int i = 0; i < size; i++)
Marshal.WriteByte(buffer2, i, Marshal.ReadByte(buffer1, i));
3) 不安全块(read/write 通过不安全指针缓冲)
如果您使用的是 .Net 4.6 或更高版本,您可以使用 Buffer.MemoryCopy()
:
unsafe
{
Buffer.MemoryCopy(p1.ToPointer(), p2.ToPointer(), destSizeBytes, sourceBytesToCopy);
}
当然是unsafe
.
如果您是 运行 .NET Core 或 .NET 5+,那么使用 Unsafe.CopyBlock()
/Unsafe.CopyBlockUnaligned()
方法是最快的。
运行 以下基准使用 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);
}
}
产生以下结果:
| 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 | - |
或者如果您喜欢漂亮的图表:
如您所见,Unsafe.CopyBlockUnaligned()
快了大约 25%。
如果您正在使用 IntPtr
,您显然必须先调用 myIntPtr.ToPointer()
并在您的项目设置中允许不安全的块:)
Marshal
中有一些方法可以复制 AllocHGlobal
to/from C# 数组分配的原始缓冲区。但我的问题是,我有两个缓冲区都由 AllocHGlobal
分配,我想简单地将一个缓冲区复制到另一个缓冲区。
var buffer1 = Marshal.AllocHGlobal(size);
var buffer2 = Marshal.AllocHGlobal(size);
nativeOpOn(buffer1);
SomeCopy(buffer1, buffer2, size);
我知道一种可能的解决方案是 P/Invoke 用于 memcpy
的系统 C 库,但是我必须编写不同的 P/Invoke 来支持 Windows 和 Linux 和 MacOS,因为它们可能有不同的 P/Invoke 内容。
您可以使用以下方法:
1) 托管缓冲区:
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) 通过 Marshal.ReadByte/WriteByte
方法或任何 Marshal.ReadXXXe/WriteXXX
方法(取决于 size
)管理 read/write 操作。循环重复这些操作即可:
for(int i = 0; i < size; i++)
Marshal.WriteByte(buffer2, i, Marshal.ReadByte(buffer1, i));
3) 不安全块(read/write 通过不安全指针缓冲)
如果您使用的是 .Net 4.6 或更高版本,您可以使用 Buffer.MemoryCopy()
:
unsafe
{
Buffer.MemoryCopy(p1.ToPointer(), p2.ToPointer(), destSizeBytes, sourceBytesToCopy);
}
当然是unsafe
.
如果您是 运行 .NET Core 或 .NET 5+,那么使用 Unsafe.CopyBlock()
/Unsafe.CopyBlockUnaligned()
方法是最快的。
运行 以下基准使用 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);
}
}
产生以下结果:
| 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 | - |
或者如果您喜欢漂亮的图表:
如您所见,Unsafe.CopyBlockUnaligned()
快了大约 25%。
如果您正在使用 IntPtr
,您显然必须先调用 myIntPtr.ToPointer()
并在您的项目设置中允许不安全的块:)