Как я могу скопировать неуправляемые данные на С# и насколько это быстро?
У меня есть два неуправляемых указателя в форме IntPtr
и вы хотите скопировать данные между ними. Как я могу это сделать? Я знаю метод Marshal.Copy
, но он может копировать только между неуправляемыми и управляемыми.
И вторая часть: копирует неуправляемые данные с С# медленнее, чем делает это в неуправляемом C/С++, используя memcpy?
Изменить: Мне было бы особенно интересно реализовать независимую платформу.
Ответы
Ответ 1
Вы можете использовать win32 memcpy через P-Invoke.
[DllImport("msvcrt.dll", SetLastError = false)]
static extern IntPtr memcpy(IntPtr dest, IntPtr src, int count);
Помимо (незначительных) накладных расходов, вызывающих функцию win32 из управляемого кода, фактическая производительность копирования должна быть такой же, как и код C/С++, который использует ту же функцию.
Не забывайте, что вы также можете использовать небезопасный блок (и параметр компилятора) и просто копировать данные по одному байту /int/long за раз:
unsafe
{
// srcPtr and destPtr are IntPtr pointing to valid memory locations
// size is the number of long (normally 4 bytes) to copy
long* src = (long*)srcPtr;
long* dest = (long*)destPtr;
for (int i = 0; i < size / sizeof(long); i++)
{
dest[i] = src[i];
}
}
Это устраняет зависимость платформы, но вам нужно быть очень осторожным с проверкой границ и арифметикой указателя.
Ответ 2
Попробуйте System.Buffer.MemoryCopy
, см. В нижней части страницы поддерживаемые целевые платформы.
Я считаю, что основное различие между этим и другими решениями, использующими P/Invoke, состоит в том, что этот метод позволяет избежать P/Invoke для меньших размеров и просто выполняет копирование напрямую.
Вот смелость реализации в .NET Core (последний по состоянию на 2019-07-23).
Ответ 3
Не комментируя производительность, просто потому, что я ее не тестировал. Вы можете достичь той же производительности, что и неуправляемая копия, используя CopyMemory или MoveMemory из Kernel32 через interop.
Вот объявление для CopyMemory
[DllImport("kernel32.dll")]
static extern void CopyMemory(IntPtr destination, IntPtr source, uint length);
Ответ 4
CopyMemory
aka RtlCopyMemory
aka memcpy()
будет так же быстро, как вызывается из С# или C (кроме крошечных накладных расходов PInvoking самого метода).
Что-то, о чем следует помнить, заключается в том, что CopyMemory
следует использовать, только если вы уверены, что диапазоны источника и назначения не перекрываются. Если они перекрываются, вам нужно использовать MoveMemory
, что будет медленнее.
Вот объявление для CopyMeSomeMemory
, показывающее, как много разных способов сделать то же самое в .Net:
[DllImport("kernel32.dll", EntryPoint = "RtlCopyMemory")]
public static extern void CopyMeSomeMemory(IntPtr Destination,
IntPtr Source, uint Length);
Для записи, я думаю, Buffer.BlockCopy
в .Net просто обертывает одну из этих функций.
Ответ 5
Вы можете посмотреть на System.Runtime.CompilerServices.Unsafe.CopyBlock
Кажется, он позволяет копировать байты с адреса источника (обозначенного пустым знаком *) по адресу назначения (обозначенному пустым знаком *).
Он также переопределяет поддержку ref byte
как источника и получателя.
[править] Неутешительно это, кажется, не реализовано в Mono
[edit] Для тех, кто заинтересован в этом и использует Unity, вы должны вместо этого обратиться к Unity UnsafeUtility.MemCpy