C # 성능-IntPtr 및 Marshal 대신 안전하지 않은 포인터 사용

안부

질문

C 애플리케이션을 C #으로 이식하고 있습니다. C 앱은 타사 DLL에서 많은 함수를 호출하므로 이러한 함수에 대한 P / Invoke 래퍼를 C #으로 작성했습니다. 이러한 C의 기능 중 일부는 내가 사용, 그래서 나는 C #을 응용 프로그램에서 사용할 수있는 데이터를 할당 IntPtr'들, Marshal.PtrToStructure그리고 Marshal.Copy관리 변수로 기본 데이터 (배열과 구조)을 복사 할 수 있습니다.

불행히도 C # 앱은 C 버전보다 훨씬 느린 것으로 판명되었습니다. 빠른 성능 분석은 위에서 언급 한 마샬링 기반 데이터 복사가 병목임을 보여주었습니다. 대신 포인터를 사용하도록 다시 작성하여 C # 코드의 속도를 높이는 것을 고려하고 있습니다. C #에서 안전하지 않은 코드와 포인터에 대한 경험이 없기 때문에 다음 질문 에 대한 전문가 의견이 필요합니다 .

  1. and ing unsafe대신 코드와 포인터 를 사용하는 단점은 무엇입니까 ? 예를 들어, 어떤 식 으로든 더 안전하지 않습니까 (의도 된 말장난)? 사람들은 마샬링을 선호하는 것 같지만 이유를 모르겠습니다.IntPtrMarshal
  2. 마샬링을 사용하는 것보다 P / Invoking에 포인터를 사용하는 것이 정말 빠릅니까? 대략 어느 정도의 속도 향상을 기대할 수 있습니까? 이에 대한 벤치 마크 테스트를 찾을 수 없습니다.

예제 코드

상황을 더 명확하게하기 위해 작은 예제 코드를 함께 해킹했습니다 (실제 코드는 훨씬 더 복잡합니다). 이 예제가 "안전하지 않은 코드 및 포인터"대 "IntPtr 및 Marshal"에 대해 말할 때 제가 의미하는 바를 보여주기를 바랍니다.

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 <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 신청

Main.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();
}

IntPtr사용하는 C # 응용 프로그램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);
  }
}

unsafe코드와 포인터 를 사용하는 C # 애플리케이션

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);
  }
}
한군 클라크 데이비스

약간 오래된 스레드이지만 최근에 C #에서 마샬링으로 과도한 성능 테스트를 수행했습니다. 며칠 동안 직렬 포트에서 많은 데이터를 역 정렬 화해야합니다. 메모리 누수가없는 것이 중요했습니다 (가장 작은 누수가 몇백 만 호출 후에 중요해지기 때문입니다). 또한 매우 큰 구조체 (> 10kb)로 많은 통계 성능 (사용 된 시간) 테스트를 수행했습니다. 그것을 위해서 (아니요, 당신은 10kb 구조체를 가져서는 안됩니다 :-))

다음 세 가지 비 정렬 화 전략을 테스트했습니다 (정렬 화도 테스트했습니다). 거의 모든 경우에 첫 번째 (MarshalMatters)가 다른 두 개를 능가했습니다. Marshal. Copy는 항상 가장 느 렸고 나머지 두 개는 레이스에서 거의 매우 가깝습니다.

안전하지 않은 코드를 사용하면 심각한 보안 위험이 발생할 수 있습니다.

먼저:

public class MarshalMatters
{
    public static T ReadUsingMarshalUnsafe<T>(byte[] data) where T : struct
    {
        unsafe
        {
            fixed (byte* p = &data[0])
            {
                return (T)Marshal.PtrToStructure(new IntPtr(p), typeof(T));
            }
        }
    }

    public unsafe static byte[] WriteUsingMarshalUnsafe<selectedT>(selectedT structure) where selectedT : struct
    {
        byte[] byteArray = new byte[Marshal.SizeOf(structure)];
        fixed (byte* byteArrayPtr = byteArray)
        {
            Marshal.StructureToPtr(structure, (IntPtr)byteArrayPtr, true);
        }
        return byteArray;
    }
}

둘째:

public class Adam_Robinson
{

    private static T BytesToStruct<T>(byte[] rawData) where T : struct
    {
        T result = default(T);
        GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
        try
        {
            IntPtr rawDataPtr = handle.AddrOfPinnedObject();
            result = (T)Marshal.PtrToStructure(rawDataPtr, typeof(T));
        }
        finally
        {
            handle.Free();
        }
        return result;
    }

    /// <summary>
    /// no Copy. no unsafe. Gets a GCHandle to the memory via Alloc
    /// </summary>
    /// <typeparam name="selectedT"></typeparam>
    /// <param name="structure"></param>
    /// <returns></returns>
    public static byte[] StructToBytes<T>(T structure) where T : struct
    {
        int size = Marshal.SizeOf(structure);
        byte[] rawData = new byte[size];
        GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
        try
        {
            IntPtr rawDataPtr = handle.AddrOfPinnedObject();
            Marshal.StructureToPtr(structure, rawDataPtr, false);
        }
        finally
        {
            handle.Free();
        }
        return rawData;
    }
}

제삼:

/// <summary>
/// http://stackoverflow.com/questions/2623761/marshal-ptrtostructure-and-back-again-and-generic-solution-for-endianness-swap
/// </summary>
public class DanB
{
    /// <summary>
    /// uses Marshal.Copy! Not run in unsafe. Uses AllocHGlobal to get new memory and copies.
    /// </summary>
    public static byte[] GetBytes<T>(T structure) where T : struct
    {
        var size = Marshal.SizeOf(structure); //or Marshal.SizeOf<selectedT>(); in .net 4.5.1
        byte[] rawData = new byte[size];
        IntPtr ptr = Marshal.AllocHGlobal(size);

        Marshal.StructureToPtr(structure, ptr, true);
        Marshal.Copy(ptr, rawData, 0, size);
        Marshal.FreeHGlobal(ptr);
        return rawData;
    }

    public static T FromBytes<T>(byte[] bytes) where T : struct
    {
        var structure = new T();
        int size = Marshal.SizeOf(structure);  //or Marshal.SizeOf<selectedT>(); in .net 4.5.1
        IntPtr ptr = Marshal.AllocHGlobal(size);

        Marshal.Copy(bytes, 0, ptr, size);

        structure = (T)Marshal.PtrToStructure(ptr, structure.GetType());
        Marshal.FreeHGlobal(ptr);

        return structure;
    }
}

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

안전하지 않은 반사 액세스 대 toCharArray (성능)

분류에서Dev

사용하기 전에 포인터를 확인하고 계십니까? 필요성과 대안, C ++ UE4

분류에서Dev

포인터 C ++ 사용에 대해 확실하지 않음

분류에서Dev

Marshal.PtrToStringAuto (IntPtr)를 사용하여 C ++ char []를 C # 문자열로 마샬링 할 수 없습니다.

분류에서Dev

C에서 명명되지 않은 구조체에 대한 포인터 사용?

분류에서Dev

스레드로부터 안전하지 않은 난수 생성기를 사용하여 C의 pi monte carlo에 대한 OpenMP pragma 수정

분류에서Dev

Marshal 2 IntPtr이 같은 기능에 있습니까?

분류에서Dev

기존 포인터 대신 C ++ stl 반복기를 사용하는 올바른 방법은 무엇입니까?

분류에서Dev

C ++ : "get"함수와 함께 'this'포인터를 사용하여 Cout 문을 대체 할 수있는 가능성

분류에서Dev

C # 안전하지 않은 포인터 삭제

분류에서Dev

C에서 포인터를 사용하는 배열의 최대 및 최소

분류에서Dev

포인터 및 이중 포인터를 사용하여 액세스 할 때 성능 차이

분류에서Dev

안전하지 않은 C #-안전하지 않은 포인터를 메서드에 전달

분류에서Dev

C99에서 및 사용하지 않고 포인터 인쇄의 차이점

분류에서Dev

Telnet 대신 SSH를 사용하여 연결 및 포트 가용성 테스트

분류에서Dev

Swift에서 C 라이브러리를 사용하면 안전하지 않은 포인터가 나타납니다.

분류에서Dev

Microsoft.Win32.SafeHandles 대 Marshal.AllocHGlobal 및 Marshal.FreeHGlobal 사용

분류에서Dev

c의 포인터를 사용하여 메모리 덮어 쓰기 가능성?

분류에서Dev

속성 바인딩이 안전하지 않다는 구성 요소 사용자에게 신호 보내기

분류에서Dev

AVAudioPCMBuffer를 사용하여 C # (안전하지 않거나 안전함)에서 float의 포인터 배열에 float 값 쓰기

분류에서Dev

포인터 배열 설명되지 않은 동작 (얕은 복사 사용) C ++

분류에서Dev

벡터 대신 std :: deque를 사용할 때 "C2027 정의되지 않은 유형 사용"오류

분류에서Dev

초기화되지 않은 포인터 대 NULL 및 0

분류에서Dev

초기화되지 않은 포인터 대 NULL 및 0

분류에서Dev

개체 대신 개체 포인터를 사용하여 할당 연산자 / 복사 생성자를 작성하는 방법은 무엇입니까?

분류에서Dev

C ++의 정의되지 않은 함수 및 매개 변수에 대한 포인터

분류에서Dev

성능 : 이메일이 이미 존재하는지 확인하는 대신 try (문) 및 catch (출력 오류) 사용

분류에서Dev

1 Form 및 많은 기능을 사용하여 C #으로 코드 구성

분류에서Dev

대량 할당 수정 방법 : Java에서 안전하지 않은 바인더 구성 (API 남용, 구조)

Related 관련 기사

  1. 1

    안전하지 않은 반사 액세스 대 toCharArray (성능)

  2. 2

    사용하기 전에 포인터를 확인하고 계십니까? 필요성과 대안, C ++ UE4

  3. 3

    포인터 C ++ 사용에 대해 확실하지 않음

  4. 4

    Marshal.PtrToStringAuto (IntPtr)를 사용하여 C ++ char []를 C # 문자열로 마샬링 할 수 없습니다.

  5. 5

    C에서 명명되지 않은 구조체에 대한 포인터 사용?

  6. 6

    스레드로부터 안전하지 않은 난수 생성기를 사용하여 C의 pi monte carlo에 대한 OpenMP pragma 수정

  7. 7

    Marshal 2 IntPtr이 같은 기능에 있습니까?

  8. 8

    기존 포인터 대신 C ++ stl 반복기를 사용하는 올바른 방법은 무엇입니까?

  9. 9

    C ++ : "get"함수와 함께 'this'포인터를 사용하여 Cout 문을 대체 할 수있는 가능성

  10. 10

    C # 안전하지 않은 포인터 삭제

  11. 11

    C에서 포인터를 사용하는 배열의 최대 및 최소

  12. 12

    포인터 및 이중 포인터를 사용하여 액세스 할 때 성능 차이

  13. 13

    안전하지 않은 C #-안전하지 않은 포인터를 메서드에 전달

  14. 14

    C99에서 및 사용하지 않고 포인터 인쇄의 차이점

  15. 15

    Telnet 대신 SSH를 사용하여 연결 및 포트 가용성 테스트

  16. 16

    Swift에서 C 라이브러리를 사용하면 안전하지 않은 포인터가 나타납니다.

  17. 17

    Microsoft.Win32.SafeHandles 대 Marshal.AllocHGlobal 및 Marshal.FreeHGlobal 사용

  18. 18

    c의 포인터를 사용하여 메모리 덮어 쓰기 가능성?

  19. 19

    속성 바인딩이 안전하지 않다는 구성 요소 사용자에게 신호 보내기

  20. 20

    AVAudioPCMBuffer를 사용하여 C # (안전하지 않거나 안전함)에서 float의 포인터 배열에 float 값 쓰기

  21. 21

    포인터 배열 설명되지 않은 동작 (얕은 복사 사용) C ++

  22. 22

    벡터 대신 std :: deque를 사용할 때 "C2027 정의되지 않은 유형 사용"오류

  23. 23

    초기화되지 않은 포인터 대 NULL 및 0

  24. 24

    초기화되지 않은 포인터 대 NULL 및 0

  25. 25

    개체 대신 개체 포인터를 사용하여 할당 연산자 / 복사 생성자를 작성하는 방법은 무엇입니까?

  26. 26

    C ++의 정의되지 않은 함수 및 매개 변수에 대한 포인터

  27. 27

    성능 : 이메일이 이미 존재하는지 확인하는 대신 try (문) 및 catch (출력 오류) 사용

  28. 28

    1 Form 및 많은 기능을 사용하여 C #으로 코드 구성

  29. 29

    대량 할당 수정 방법 : Java에서 안전하지 않은 바인더 구성 (API 남용, 구조)

뜨겁다태그

보관