거창하지 않지만 중요한 개발 잡담

Little Mo' Betta - 구조체 데이터 최적화하기

NAEU 2025. 12. 13. 12:32
struct Bonfire {
    bool bIsSleeping; 
    int nMemberCount; 
    bool bIsHappy; 
    bool bCanJoinNow; 
};

다음 구조체의 크기는 몇일까?
우리가 알고 있는 지식으로는 bool이 1바이트, int는 4바이트이다
따라서 bool x3 + int x1로 7바이트라고 생각할 수 있다
그렇다면 돌려보자

int main() {
	Bonfire fire;
	std::cout << "Size of Bonfire: " << sizeof(fire) << std::endl;
	return 0;
}

 


이게 왠걸
거의 2배인 12바이트가 할당되어버렸다
왜 그럴까?
첫번째로는 애초에 7바이트라는 사이즈만 할당할 수 없다
CPU에서는 값을 읽을때 4바이트로 정렬되어있다면 값을 한번에 읽을 수 있다
그렇기 때문에 컴파일러는 ABI(Application Binary Interface)라고 하는 규약을 따라 4바이트씩 데이터를 정렬한다
따라서 모든 데이터들은 1-4-8-16... 기준으로 할당된다
자 그럼 7이 안 나오는건 이해가 됐다
그럼 8이 나와야지 왜 12가 나오는걸까?
그 이유는 값을 정렬할때 패딩(padding)이라는 것이 들어가기 때문이다
만약 저 구조체의 메모리 레이아웃을 그려본다고 하자

cpp b: bool, i: int
[b][ ][ ][ ] / [ ][ ][ ][ ] / [ ][ ][ ][ ]

우선 bIsSleeping에 해당하는 1바이트 bool을 삽입하였다
이 제 뭐 하 지 ?
다음으로는 4바이트 int를 삽입해야하는데 b 바로 다음에 넣게된다면 정렬 규칙이 깨져버리게 된다
따라서 컴파일러는 3바이트의 패딩값을 b다음에 먼저 삽입한다
그리고 그 후에 4바이트 int를 넣는다

b: bool, i: int, p: padding
[b][p][p][p] / [i][i][i][i] / [ ][ ][ ][ ]

벌써 8바이트나 먹었다
이제 bool이 2개 남았다
먼저 삽입해보자

b: bool, i: int, p: padding
[b][p][p][p] / [i][i][i][i] / [b][ ][ ][ ]

이제 1바이트 bool을 하나 더 삽입해야하지만
남은 크기를 보니 3바이트라는 무려 3배나 더 되는 크기가 남아있다
따라서 컴파일러는 b바로 다음에 bool을 하나 삽입한다

b: bool, i: int, p: padding
[b][p][p][p] / [i][i][i][i] / [b][b][ ][ ]

그리고 구조체 자체의 변수가 더 없기 때문에 이 또한 4바이트씩 맞춰줘야한다
고로, 마지막에 패딩을 넣는다

b: bool, i: int, p: padding
[b][p][p][p] / [i][i][i][i] / [b][b][p][p]

이렇게 완성된구조체의 메모리 레이아웃은 총 12바이트를 할당받고 있다
vs에서도 확인해보자

근데 이 메모리 구조를 보고있자니
내 안에서 치밀어오르는 이 화를 감출 수가 없다
아니 내가 쓰는건 7바이트 뿐인데
총 공간은 12바이트나 먹고 있다
무려 5바이트나 놀고 있는 것이다
고작 5바이트라고 생각할 수 있지만
이러한 객체가 굉장히 많아진다면
전체 용량에 약 41%를 낭비하고 있는거다
따라서 우리는 아주 살짝의 터치로 이를 보완할 수 있다

struct Bonfire { 
	int nMemberCount; 
    bool bIsSleeping;
    bool bIsHappy; 
    bool bCanJoinNow; 
    };


끝이다
bool을 한곳으로 모아두면 메모리 레이아웃은

이렇게 바뀌게 된다
총 크기 8바이트에 놀고 있는 용량도 1바이트밖에 안된다
이렇게 단순히 변수의 순서만으로도 최적화에 도움이 될 수 있다