[C#] struct의 메모리 복사 비용
카테고리: CSharp
태그: Memory
struct 타입의 메모리 복사 성능에 대해 테스트하고 작성한 글이다.
struct의 메모리 복사 비용
struct는 기본적으로 크기와 관계없이Stack에 할당된다- 일정 크기 이상일 경우 
Heap에 할당된다는 정보가 있어 유니티에서 1024kb 크기의struct를 사용해 테스트해 본 결과GC Alloc은 발생하지 않음 - 몇몇의 경우는 
Stack에 할당이 된다. ex)class의 멤버 변수 타입으로 사용, 인스턴싱된struct객체를object로boxing하는 경우, … 
- 일정 크기 이상일 경우 
 Stack에 메모리가 할당된다는 의미는?struct타입의 변수를 정의할 때struct크기만큼 메모리를Stack에 복사해야 한다는 의미이다- 아래 코드의 성능을 측정했을 때 
struct의 경우 크기가 작으면 성능이 비슷하게 나오지만 크기가 클수록 복사 비용이 늘어나 성능 하락이 발생한다 class의 경우는 포인터 방식의 접근으므로 메모리 복사가 이루어지지 않아 크기와 관계없이 일정한 성능을 유지한다- 결과적으로 
struct타입의 크기가 크면Stack에 메모리를 복사하지 않고Reference타입과 같이 포인터 방식으로 접근해 메모리 복사 비용을 낮춰야 한다 
테스트 코드
public struct LittleStruct
{
    public int int1;
    public int int2;
    public int int3;
}
public struct BigStruct
{
    public long long1;
    public long long2;
    public long long3;
    public long long4;
    public long long5;
    public long long6;
    public long long7;
    public long long8;
    public long long9;
    public long long10;
    public long long11;
    public long long12;
    public long long13;
}
public class LittleClass
{
    public int int1;
    public int int2;
    public int int3;
}
public class BigClass
{
    public long long1;
    public long long2;
    public long long3;
    public long long4;
    public long long5;
    public long long6;
    public long long7;
    public long long8;
    public long long9;
    public long long10;
    public long long11;
    public long long12;
    public long long13;
}
public class PerformanceTest
{
    private static readonly LittleStruct[] LittleStructArr = new LittleStruct[100];
    private static readonly BigStruct[] BigStructArr = new BigStruct[100];
    public static readonly LittleClass[] LittleClassArr = new LittleClass[100];
    public static readonly BigClass[] BigClassArr = new BigClass[100];
    static PerformanceTest()
    {
        for (int i = 0; i < LittleClassArr.Length; i++)
            LittleClassArr[i] = new LittleClass();
        for (int i = 0; i < BigClassArr.Length; i++)
            BigClassArr[i] = new BigClass();
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
    [Benchmark]
    public void NoCopied_LittleStruct()
    {
        int count = 0;
        for (int i = 0; i < LittleStructArr.Length; ++i)
        {
            if (LittleStructArr[i].int1 == 1)
                ++count;
            if (LittleStructArr[i].int2 == 2)
                ++count;
            if (LittleStructArr[i].int3 == 3)
                ++count;
        }
    }
    [Benchmark]
    public void Copied_LittleStruct()
    {
        int count = 0;
        for (int i = 0; i < LittleStructArr.Length; ++i)
        {
            LittleStruct data = LittleStructArr[i];
            if (data.int1 == 1)
                ++count;
            if (data.int2 == 2)
                ++count;
            if (data.int3 == 3)
                ++count;
        }
    }
    [Benchmark]
    public void NoCopied_BigStruct()
    {
        int count = 0;
        for (int i = 0; i < BigStructArr.Length; ++i)
        {
            if (BigStructArr[i].long1 == 1)
                ++count;
            if (BigStructArr[i].long2 == 2)
                ++count;
            if (BigStructArr[i].long3 == 3)
                ++count;
        }
    }
    [Benchmark]
    public void Copied_BigStruct()
    {
        int count = 0;
        for (int i = 0; i < BigStructArr.Length; ++i)
        {
            BigStruct data = BigStructArr[i];
            if (data.long1 == 1)
                ++count;
            if (data.long2 == 2)
                ++count;
            if (data.long3 == 3)
                ++count;
        }
    }
    [Benchmark]
    public void NoCopied_LittleClass()
    {
        int count = 0;
        for (int i = 0; i < LittleClassArr.Length; ++i)
        {
            if (LittleClassArr[i].int1 == 1)
                ++count;
            if (LittleClassArr[i].int2 == 2)
                ++count;
            if (LittleClassArr[i].int3 == 3)
                ++count;
        }
    }
    [Benchmark]
    public void Copied_LittleClass()
    {
        int count = 0;
        for (int i = 0; i < LittleClassArr.Length; ++i)
        {
            LittleClass data = LittleClassArr[i];
            if (data.int1 == 1)
                ++count;
            if (data.int2 == 2)
                ++count;
            if (data.int3 == 3)
                ++count;
        }
    }
    [Benchmark]
    public void NoCopied_BigClass()
    {
        int count = 0;
        for (int i = 0; i < BigClassArr.Length; ++i)
        {
            if (BigClassArr[i].long1 == 1)
                ++count;
            if (BigClassArr[i].long2 == 2)
                ++count;
            if (BigClassArr[i].long3 == 3)
                ++count;
        }
    }
    [Benchmark]
    public void Copied_BigClass()
    {
        int count = 0;
        for (int i = 0; i < BigClassArr.Length; ++i)
        {
            BigClass data = BigClassArr[i];
            if (data.long1 == 1)
                ++count;
            if (data.long2 == 2)
                ++count;
            if (data.long3 == 3)
                ++count;
        }
    }
}
💻 열심히 공부해서 작성 중이니 오류나 틀린 부분이 있을 경우 
  언제든지 댓글 혹은 메일로 알려주시면 감사하겠습니다! 😸
댓글 남기기