2016-08-02 5 views
1

나는 부모 객체의 크기를 계산할 수 있도록 DateTimeOffset 구조체를 내 코드에서 가져 오려고 노력했다. 문제는 sizeof 연산자도 Marshal.SizeOf 함수도이 목적으로 작동하지 않는다는 것입니다.x64 비트 Azure 컴퓨터에서 DateTimeOffset의 평균 크기는 얼마입니까?

안전하지 않은 플래그로 컴파일해야하기 때문에 sizeof가 작동하지 않습니다.이 기능은이를 수행하는 데 충분하지 않습니다. Marshal.SizeOf에서 예외가 발생합니다 :

유형 'System.DateTimeOffset은'관리되지 않는 구조로 마샬링 할 수 없습니다; 의미있는 크기 또는 오프셋을 계산할 수 없습니다.

공격/블로그 게시물의 모든 행에 오류가 하나씩 발생하기 때문에 이런 구조체의 크기를 계산할 때 포기했습니다.

누군가가 64 비트 Azure 웹 서버에 DateTimeOffset의 평균 크기가 무엇인지 말해 줄 수 있습니까?

답변

9

허용되는 대답이 잘못되었습니다. 정렬을 무시합니다. 구조체의 크기는 단순히 멤버의 합계가 아닙니다. 필드 사이의 추가 공간과 구조체의 끝은 프로세서가 필드를 효율적으로 읽고 .NET 메모리 모델에서 제공되는 원 자성 보장을 구현하는 데 필요할 수 있습니다.

DateTimeOffset에 추가로 복잡한 코드입니다. DateTimeOffset 구조체를 작성한 Microsoft 프로그래머는 DateTime 구조체를 복사/붙여 넣기하여 코드를 작성하여 큰 실수를 저지른 것입니다. 하나가 아닌 두 개의 필드가 있기 때문에 DateTimeOffset의 중요한 문제는 역사적인 실수입니다. Sequential 대신 LayoutKind.Auto를 사용합니다. 최적으로 필드를 배치하는 CLR에 여유를 준다 Reference Source

에서 쉽게 볼 32- 비트 모드가 정상적으로 같이 8 바이트 Int64를 정렬되지 것이다 어떤 모드가 실행된다. 그렇지만 4 바이트입니다. 필드 사이에 채우기가 적어지면 크기는 12 바이트입니다.

마찬가지로 64 비트 모드에서 필드를 8로 정렬하는 것이 좋습니다. 필드 사이에 더 많은 패딩이 생성됩니다.

이것을 보는 유일한 방법은 디버거를 사용하는 것입니다. 코드의이 비트를 실행 브레이크 포인트 히트는, 디버그를 사용하는 경우

static void Main(string[] args) { 
     var arr = new DateTimeOffset[] { 
      new DateTimeOffset(0x123456789abcdef0, TimeSpan.FromMinutes(60)), 
      new DateTimeOffset(0x123456789abcdef0, TimeSpan.FromMinutes(60)), 
     }; 
     System.Diagnostics.Debugger.Break(); 
    } 

> 주소 상자에 윈도우> 메모리> 메모리 1 및 유형 &arr[0] 배열의 내용을보고.

0x00000115DC4FBA30 3c 00 00 00 00 00 00 00 f0 76 f8 38 70 56 34 12 <.......ðvø8pV4. 
0x00000115DC4FBA40 3c 00 00 00 00 00 00 00 f0 76 f8 38 70 56 34 12 <.......ðvø8pV4. 
0x00000115DC4FBA50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 

오프셋 필드 (60 = 0x003c)와 날짜 시간 필드를 쉽게 볼 수 있습니다.창 크기를 조정하여 2 가지 요소를 분명하게 만들었으므로 컴퓨터에서 깨끗하게 보이지 않습니다. 그러나 반복 할 때까지 바이트를 계산하면 DateTimeOffset은 16 바이트을 64 비트 모드로 사용합니다.

크기가 32 비트와 64 비트 모드 사이에서 다르다는 사실은 일반적으로 당신을 상당히 걱정해야합니다. 이 버그는 수정해야 할 필요가 전혀 없으며 DateTimeOffset에 대한 합리적인 interop 스토리가 없으며 동일한 관리되지 않는 유형과 일치하지 않습니다. WinRT (일명 UWP)에서 지정된 interop 유형이지만 CLR에 내장 된 언어 프로젝션이 문제를 숨 깁니다.

1

10 바이트를 차지합니다.

source에서 보면 Int16이고 DateTime입니다. DateTime에는 하나의 UInt64이 있습니다.

나는 Marshal.SizeOf이 그것을 측정 할 수없는 이유를 알지 못합니다.

1

이것은 완전히 신뢰할만한 것은 아니며 (배열 오버 헤드가 일정하다고 가정합니다. 제너럴 조건 하에서는 사실 일 수 있습니다), 아래는이 질문에 경험적으로 대답하는 간단한 방법입니다. 어떤 이유로 당신이 디버거를 결여 환경에서 테스트하고자하는 경우,이 방법은 전성 검사를위한 좋은 수 있습니다 :

void Main() 
{ 
    PrintSize<DateTimeOffset>(); 
} 

public static void PrintSize<T>() 
{ 
    GC.Collect(); 
    long gc1 = GC.GetTotalMemory(true); 
    const int sz = 100000; 
    T[] foo = new T[sz]; 
    long gc2 = GC.GetTotalMemory(true); 
    GC.KeepAlive(foo); 
    Console.WriteLine($"{(gc2 - gc1)/(double)sz} bytes per {typeof(T)}"); 
} 

내가 32 비트 같이 시험 할 때, (+/- 0.001) (12)를 출력한다.
64 비트로 테스트했을 때 16 (+/- 0.001)을 인쇄했습니다.

이러한 유형의 테스트는 독립 실행 형 응용 프로그램으로 수행하는 것이 가장 좋습니다. 더 복잡한 응용 프로그램에서는 RAM 사용량에 예측할 수없는 변화가 발생할 가능성이 큽니다.

+0

+1 원래 문제를 해결해 주셔서 감사합니다. 내 웹 응용 프로그램에서이 일을 생각하고 있었지만, 다중 요청이 발생할 수 있으므로 스레드 안전성을 보장 할 수 없습니다. –

+0

어느정도 sz를 높이면 다른 영향에 대한이 트릭의 감도를 줄일 수 있습니다. 그러나이 코드는 일시적으로 메모리 조각을 태 웁니다. 따라서 프로덕션 용도로 이상적이지 않습니다. 그러나 새로운 Azure 인스턴스를 회전시키고 코드를 실행 한 다음 인스턴스를 삭제할 수 있습니다. 어쨌든 한스의 접근 방식은 신뢰할 수있는 정확한 숫자가 필요할 때 훨씬 좋습니다. 그러나, 이것은 새로운 애플리케이션 기능의 메모리 영향을 추측하고자 할 때 사용하는 경향이 있습니다 (대개 온전한 메모리 크기 예측 예측). – Brian