Как я могу скопировать неуправляемые данные на С# и насколько это быстро?

У меня есть два неуправляемых указателя в форме 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