Производительность C # - использование небезопасных указателей вместо IntPtr и Marshal

QuestionI»

m портирование приложения C на C #. Приложение C вызывает множество функций из сторонних DLL, поэтому я написал оболочки P / Invoke для этих функций в C #. Некоторые из этих функций C выделяют данные, которые я должен использовать в приложении C #, поэтому я использовал 'IntPtrs,Marshal.PtrToStructure а такжеMarshal.Copy копировать собственные данные (массивы и структуры) в управляемые переменные.

К сожалению, приложение C # оказалось намного медленнее, чем версия C. Быстрый анализ производительности показал, что упомянутое выше копирование данных на основе маршалинга является узким местом. Я'Я собираюсь ускорить код C #, переписав его, чтобы вместо него использовать указатели. Так как я нея не имею опыта работы с небезопасным кодом и указателями в C #, мне нужно мнение эксперта относительно следующеговопросы:

Каковы недостатки использованияunsafe код и указатели вместоIntPtr а такжеMarshalING? Например, это более опасно (каламбур) в любом случае? Люди, кажется, предпочитают маршалинг, но я нене знаю почему.Использование указателей для P / Invoking действительно быстрее, чем маршалинг? Сколько ускорения можно ожидать приблизительно? Я не могНе найти никаких тестов для этого.Пример кода

Чтобы сделать ситуацию более понятной, я собрал небольшой пример кода (реальный код гораздо сложнее). Я надеюсь, что этот пример показывает, что я имею в виду, когда яя говорю онебезопасный код и указатели " против "ИнтПтр и Маршал ".

C библиотека (DLL)

MyLib.h

#ifndef _MY_LIB_H_
#define _MY_LIB_H_

struct MyData 
{
  int length;
  unsigned char* bytes;
};

__declspec(dllexport) void CreateMyData(struct MyData** myData, int length);
__declspec(dllexport) void DestroyMyData(struct MyData* myData);

#endif // _MY_LIB_H_

MyLib.c

#include 
#include "MyLib.h"

void CreateMyData(struct MyData** myData, int length)
{
  int i;

  *myData = (struct MyData*)malloc(sizeof(struct MyData));
  if (*myData != NULL)
  {
    (*myData)->length = length;
    (*myData)->bytes = (unsigned char*)malloc(length * sizeof(char));
    if ((*myData)->bytes != NULL)
      for (i = 0; i < length; ++i)
        (*myData)->bytes[i] = (unsigned char)(i % 256);
  }
}

void DestroyMyData(struct MyData* myData)
{
  if (myData != NULL)
  {
    if (myData->bytes != NULL)
      free(myData->bytes);
    free(myData);
  }
}
Приложение C

main.C

#include 
#include "MyLib.h"

void main()
{
  struct MyData* myData = NULL;
  int length = 100 * 1024 * 1024;

  printf("=== C++ test ===\n");
  CreateMyData(&myData, length);
  if (myData != NULL)
  {
    printf("Length: %d\n", myData->length);
    if (myData->bytes != NULL)
      printf("First: %d, last: %d\n", myData->bytes[0], myData->bytes[myData->length - 1]);
    else
      printf("myData->bytes is NULL");
  }
  else
    printf("myData is NULL\n");
  DestroyMyData(myData);
  getchar();
}
Приложение C #, которое используетIntPtr а такжеMarshal

Program.cs

using System;
using System.Runtime.InteropServices;

public static class Program
{
  [StructLayout(LayoutKind.Sequential)]
  private struct MyData
  {
    public int Length;
    public IntPtr Bytes;
  }

  [DllImport("MyLib.dll")]
  private static extern void CreateMyData(out IntPtr myData, int length);

  [DllImport("MyLib.dll")]
  private static extern void DestroyMyData(IntPtr myData);

  public static void Main()
  {
    Console.WriteLine("=== C# test, using IntPtr and Marshal ===");
    int length = 100 * 1024 * 1024;
    IntPtr myData1;
    CreateMyData(out myData1, length);
    if (myData1 != IntPtr.Zero)
    {
      MyData myData2 = (MyData)Marshal.PtrToStructure(myData1, typeof(MyData));
      Console.WriteLine("Length: {0}", myData2.Length);
      if (myData2.Bytes != IntPtr.Zero)
      {
        byte[] bytes = new byte[myData2.Length];
        Marshal.Copy(myData2.Bytes, bytes, 0, myData2.Length);
        Console.WriteLine("First: {0}, last: {1}", bytes[0], bytes[myData2.Length - 1]);
      }
      else
        Console.WriteLine("myData.Bytes is IntPtr.Zero");
    }
    else
      Console.WriteLine("myData is IntPtr.Zero");
    DestroyMyData(myData1);
    Console.ReadKey(true);
  }
}
Приложение C #, которое используетunsafe код и указатели

Program.cs

using System;
using System.Runtime.InteropServices;

public static class Program
{
  [StructLayout(LayoutKind.Sequential)]
  private unsafe struct MyData
  {
    public int Length;
    public byte* Bytes;
  }

  [DllImport("MyLib.dll")]
  private unsafe static extern void CreateMyData(out MyData* myData, int length);

  [DllImport("MyLib.dll")]
  private unsafe static extern void DestroyMyData(MyData* myData);

  public unsafe static void Main()
  {
    Console.WriteLine("=== C# test, using unsafe code ===");
    int length = 100 * 1024 * 1024;
    MyData* myData;
    CreateMyData(out myData, length);
    if (myData != null)
    {
      Console.WriteLine("Length: {0}", myData->Length);
      if (myData->Bytes != null)
        Console.WriteLine("First: {0}, last: {1}", myData->Bytes[0], myData->Bytes[myData->Length - 1]);
      else
        Console.WriteLine("myData.Bytes is null");
    }
    else
      Console.WriteLine("myData is null");
    DestroyMyData(myData);
    Console.ReadKey(true);
  }
}

Ответы на вопрос(7)

Ваш ответ на вопрос