[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;
}
}
}
💻 열심히 공부해서 작성 중이니 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 알려주시면 감사하겠습니다! 😸
댓글 남기기