C # -Leistung - Verwenden unsicherer Zeiger anstelle von IntPtr und Marshal

Frage

Ich portiere eine C-Anwendung in C #. Die C-App ruft viele Funktionen von einer DLL eines Drittanbieters auf, daher habe ich P / Invoke-Wrapper für diese Funktionen in C # geschrieben. Einige dieser C-Funktionen weisen Daten zu, die ich in der C # -App verwenden muss, also habe ich verwendetIntPtr's,Marshal.PtrToStructure undMarshal.Copy um die nativen Daten (Arrays und Strukturen) in verwaltete Variablen zu kopieren.

Leider erwies sich die C # -App als viel langsamer als die C-Version. Eine schnelle Leistungsanalyse ergab, dass das oben erwähnte Kopieren von Marshalling-basierten Daten der Engpass ist.Ich überlege, den C # -Code zu beschleunigen, indem ich ihn umschreibe, um stattdessen Zeiger zu verwenden. Da ich keine Erfahrung mit unsicherem Code und Zeigern in C # habe, benötige ich eine Expertenmeinung in Bezug auf FolgendesFragen:

Was sind die Nachteile der Verwendungunsafe Code und Zeiger stattIntPtr undMarshaling? Ist es zum Beispiel in irgendeiner Weise unsicherer (Wortspiel beabsichtigt)? Die Leute scheinen lieber zu marshallen, aber ich weiß nicht warum.Ist die Verwendung von P / Invoking-Zeigern wirklich schneller als die Verwendung von Marshalling? Wie viel Beschleunigung ist ungefähr zu erwarten? Ich konnte keine Benchmark-Tests dafür finden.Beispielcode

Um die Situation klarer zu machen, habe ich einen kleinen Beispielcode gehackt (der echte Code ist viel komplexer). Ich hoffe, dieses Beispiel zeigt, was ich meine, wenn ich von "unsicherem Code und Zeigern" im Vergleich zu "IntPtr und Marshal" spreche.

C-Bibliothek (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 <stdlib.h>
#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 Anwendung

Haupt c

#include <stdio.h>
#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 # -Anwendung, die verwendetIntPtr undMarshal

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 # -Anwendung, die verwendetunsafe Code und Zeiger

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);
  }
}

Antworten auf die Frage(7)

Ihre Antwort auf die Frage