development

내 구조체 배열이 왜 그렇게 많은 메모리를 차지합니까?

big-blog 2020. 12. 9. 21:09
반응형

내 구조체 배열이 왜 그렇게 많은 메모리를 차지합니까?


질문 : Micro Framework는 구조체 배열에 메모리를 어떻게 할당합니까?

복제 할 코드가있는 BitBucket 저장소 입니다.

컨텍스트 및 세부 정보

USB 키보드에서 키 입력을 처리 할 때 지연을 삽입하기 위해 고정 크기 배열을 사용하여 대기열을 만들고 있습니다. struct키 위아래 이벤트와 지연을 나타내는 데 사용하고 있습니다.

public struct QueuedEvent
{
    public readonly EventType Type;        // Byte
    public readonly byte KeyPressed;
    public readonly TinyTimeSpan Delay;    // Int16

    public readonly static QueuedEvent Empty = new QueuedEvent();
}

public enum EventType : byte
{
    None = 0,
    Delay = 1,
    KeyDown = 2,
    KeyUp = 3,
    KeyPress = 4,
}

public class FixedSizeQueue
{
    private readonly QueuedEvent[] _Array;
    private int _Head = 0;
    private int _Tail = 0;

    public FixedSizeQueue(int size)
    {
        _Array = new QueuedEvent[size];
    }

    // Enqueue and Dequeue methods follow.
}

메모리에서 4 바이트를 QueuedEvent차지할 것이라고 생각 했지만 가비지 수집기 (특히 유형) 의 디버그 출력을 보면 실제로는 각각 84 바이트를 차지합니다 ! 이것은 나를 과잉으로 공격합니다! (그리고 실제로는 각각 84 바이트 인 것 같습니다. 512 개를 할당하면 얻을 수 있기 때문 입니다. ~ 20kB의 RAM을 사용할 수 있으므로 512에서 쉽게 할당 할 수 있어야합니다.)VALUETYPESZARRAYOutOfMemoryException

질문 (다시) : Micro Framework는 4에 들어갈 수있는 구조체에 84 바이트를 어떻게 할당합니까?

가비지 수집기 피규어

다음은 다양한 크기의 배열 테이블입니다 QueuedEvent(0을 할당 할 때 금액을 뺀 후).

+--------+-----------+-----------+---------+------------+-------+
| Number | VALUETYPE | B/Q'dEvnt | SZARRAY | B/Q'edEvnt | Total |
| 16     | 1152      | 72        | 192     | 12         | 84    |
| 32     | 2304      | 72        | 384     | 12         | 84    |
| 64     | 4608      | 72        | 768     | 12         | 84    |
| 128    | 9216      | 72        | 1536    | 12         | 84    |
+--------+-----------+-----------+---------+------------+-------+

SZARRAY숫자를 기반으로 QueuedEvent필드가 Int32 경계에 정렬되어 12 바이트 를 차지 한다고 생각 합니다. 그러나 나는 여분의 72 바이트가 어디에서 오는지 전혀 모른다.

편집 :Debug.GC(true) 디버거 출력에서 ​​얻은 덤프 를 호출 하고 관찰하여 이러한 숫자를 얻습니다. 각 숫자의 의미를 정확히 식별하는 참조를 찾지 못했습니다.

나는 단순히를 할당 할 수 있다는 것을 깨달았 int[]지만 그것은 내가 멋진 캡슐화와 구조체의 모든 타입 안전성을 잃는다는 것을 의미합니다. 마이크로 프레임 워크에서 구조체의 실제 비용이 얼마인지 정말 알고 싶습니다.


My TinyTimeSpan100ns 틱을 나타내는 Int64 대신 밀리 초 수를 나타내는 데 TimeSpan사용하는 것을 제외 하면 일반과 매우 유사합니다 Int16.

public struct TinyTimeSpan
{
    public static readonly TinyTimeSpan Zero = new TinyTimeSpan(0);
    private short _Milliseconds;

    public TinyTimeSpan(short milliseconds)
    {
        _Milliseconds = milliseconds;
    }
    public TinyTimeSpan(TimeSpan ts)
    {
        _Milliseconds = (short)(ts.Ticks / TimeSpan.TicksPerMillisecond);
    }

    public int Milliseconds { get { return _Milliseconds; } }
    public int Seconds { get { return _Milliseconds * 1000; } }
}

FEZ Domino 를 하드웨어로 사용하고 있습니다. 하드웨어에 따라 다를 수 있습니다. 또한 Micro Framework 4.1.

편집-추가 테스트 및 댓글 답변

나는 훨씬 더 많은 테스트를 실행했습니다 (이번에는 에뮬레이터에서 실제 하드웨어가 아니라 숫자 QueuedEvent가 동일하므로 내 하드웨어가 다른 테스트에서도 동일하다고 가정합니다).

복제 할 코드가있는 BitBucket 저장소 입니다.

다음 정수 유형 및 구조체는 다음과 같이 오버 헤드를 유발하지 않습니다 VALUETYPE.

  • 바이트 (1 바이트)
  • Int32 (4 바이트)
  • Int16 (2 바이트)
  • Int64 (8 바이트)
  • Double (8 바이트)
  • TimeSpan (12 바이트-내부 멤버가 Int64이므로 이상 함)
  • DateTime (12 바이트-이상 함)

그러나 Guid각각 36 바이트를 사용합니다.

The empty static member does allocate VALUETYPE, using 72 bytes (12 bytes less than the same struct in an array).

Allocating the array as a static member does not change anything.

Running in Debug or Release modes makes no difference. I don't know how to get the GC debug info without a debugger attached though. But Micro Framework is interpreted, so I don't know what effect a non-attached debugger would have anyway.

Micro Framework does not support unsafe code. Nor does it support StructLayout Explicit (well, technically it does, but there is no FieldOffset attribute) . StructLayout Auto and Sequential make no difference.

Here are are few more structs and their measured memory allocation:

// Uses 12 bytes in SZARRAY and 24 in VALUETYPE, total = 36 each
public struct JustAnInt32
{
    public readonly Int32 Value;
}


// Uses 12 bytes in SZARRAY and 48 in VALUETYPE, total = 60 each
// Same as original QueuedEvent but only uses integral types.
public struct QueuedEventSimple
{
    public readonly byte Type;
    public readonly byte KeyPressed;
    public readonly short DelayMilliseconds;
    // Replacing the short with TimeSpan does not change memory usage.
}

// Uses 12 bytes in SZARRAY and 12 in VALUETYPE, total = 24 each
// I have to admit 24 bytes is a bit much for an empty struct!!
public struct Empty
{ 
}

It seems every time I use a custom struct, I incur some sort of overhead. And no matter what I include in the struct, it always requires 12 bytes in SZARRAY. So I tried this:

// Uses 12 bytes in SZARRAY and 36 in VALUETYPE, total = 48 each
public struct DifferentEntity
{
    public readonly Double D;
    public readonly TimeSpan T;
}

// Uses 12 bytes in SZARRAY and 108 in VALUETYPE, total = 120 each
public struct MultipleEntities
{
    public readonly DifferentEntity E1;
    public readonly DifferentEntity E2;
}

// Uses 12 bytes in SZARRAY and 60 in VALUETYPE, total = 72 each
// This is equivalent to MultipleEntities, but has quite different memory usage.
public struct TwoDoublesAndTimeSpans
{
    public readonly double D1;
    public readonly TimeSpan T1;
    public readonly double D2;
    public readonly TimeSpan T2;
}

Minor Edit

After posting my own answer, I realised there was always a 12 byte overhead in SZARRAY per item. So I tested an object[]. Reference types consume 12 bytes each in the Micro Framework.

An empty struct public struct Empty { } consumes 24 bytes each.


Based on my tests, I'm guessing ValueTypes in the Micro Framework are not true value types as we're used to on the desktop CLR. At the very least, they are being boxed. And there may be another level of indirection involved too. These costs are incurred in a (quite substantial for an embedded platform) memory overhead.

I'll be converting to an int[] in my FixedSizedQueue .

Actually, I ended up using UInt32[] and added some extension methods to wrap around the bit bashing.

I poked around a bit in the source code, but couldn't find anything helpful (and I really don't know what to look for either).

참고URL : https://stackoverflow.com/questions/12445185/why-do-my-array-of-structs-take-up-so-much-memory

반응형