Существует ли быстрый способ выделения и инициализации нуля большого блока памяти с помощью .Net Core?
Ищите решение, которое работает как на платформах Windows, так и на Linux.
Кажется,самый быстрый метод в Windows - вызвать функцию kernel32 HeapAlloc через PInvoke, это выделяет и обнуляет блок памяти объемом 1 ГБ менее чем за 2 миллисекунды.
Однако я не нашел способа добиться подобных результатов в Linuxпока только использую .Net Core.
Я попробовал метод Marshal.AllocHGlobal
, который не обнуляет память, а затем обнуляет память с помощью Marshal.Copy, чтобы скопировать массив нулевых байтов в память, ноэто дает низкую производительность, то есть 800 миллисекунд по сравнению с 2 миллисекундами, указанными выше.
Для моего приложения распределение и обнуление должны быть выполнены менее чем за 10 миллисекунд.
Обеспечивает ли .Net Core перекрестную передачуAPI платформы, который дал бы такую же производительность, как Windows HeapAlloc или что-то вроде функции C calloc
?
Тестовый код ниже:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace MemoryApp
{
class Program
{
static void Main(string[] args)
{
const int sizeBytes = 1_000_000_000;
Console.WriteLine($"Allocating memory of size: {sizeBytes} bytes.");
const int totalAttempts = 20;
for (int i = 0; i < totalAttempts; i++)
{
var stopwatch = Stopwatch.StartNew();
var allocatedMemory = new AllocatedMemory(sizeBytes);
stopwatch.Stop();
Console.WriteLine($"Allocated memory in {stopwatch.ElapsedTicks} ticks ({stopwatch.ElapsedMilliseconds} milliseconds).");
allocatedMemory.Dispose();
}
Console.ReadLine();
}
}
public unsafe class AllocatedMemory : IDisposable
{
public byte* MemoryAddress { get; }
#if USE_WINDOWS_HEAP
private IntPtr _heapPtr;
#else
private IntPtr _memoryPtr;
#endif
public AllocatedMemory(int sizeInBytes)
{
#if USE_WINDOWS_HEAP
var heapFlags = HeapFlags.HEAP_GENERATE_EXCEPTIONS | HeapFlags.HEAP_ZERO_MEMORY;
_heapPtr = Heap.HeapCreate(heapFlags, 0, 0);
MemoryAddress = (byte*)Heap.HeapAlloc(_heapPtr, heapFlags, (uint)sizeInBytes);
#else
// Memory allocated but not zeroed
_memoryPtr = Marshal.AllocHGlobal((IntPtr)sizeInBytes);
// Zero the memory
Marshal.Copy(new byte[sizeInBytes], 0, _memoryPtr, sizeInBytes);
MemoryAddress = (byte*)_memoryPtr;
#endif
}
~AllocatedMemory()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (MemoryAddress != null)
{
#if USE_WINDOWS_HEAP
Heap.HeapDestroy(_heapPtr);
#else
Marshal.FreeHGlobal(_memoryPtr);
#endif
}
}
}
}
#if USE_WINDOWS_HEAP
public class Heap
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr HeapCreate(HeapFlags flOptions, uint dwInitialsize, uint dwMaximumSize);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr HeapAlloc(IntPtr hHeap, HeapFlags dwFlags, uint dwSize);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool HeapDestroy(IntPtr hHeap);
}
[Flags]
public enum HeapFlags
{
HEAP_NO_SERIALIZE = 0x1,
HEAP_GENERATE_EXCEPTIONS = 0x4,
HEAP_ZERO_MEMORY = 0x8
}
#endif
}