본문 바로가기

분류대기

[C++] struct byte align

[1. 개요]

구조체 작성 시, 메모리 접근 및 구조체 크기 개선을 위한 byte align 관련 하여 정리한다.


[2. 예제]

#include <iostream>

struct A {
    char a;    // 1byte
    int b;     // 5byte
    char c[2]; // 7byte
};

struct B {
    char a;   // 1byte
    int b;    // 5byte
    char c;   // 6byte
};

struct C {
    char a;   // 1byte
    char c;   // 2byte
    int b;    // 6byte
};

struct AA {
    char a;    // 1byte
    char c[2]; // 3byte
    int b;     // 7byte
};

int main()
{
    std::cout << sizeof(A) << std::endl;
    // output is "12"
    
    std::cout << sizeof(B) << std::endl;
    // output is "12"
    
    std::cout << sizeof(C) << std::endl;
    // output is "8"

    std::cout << sizeof(AA) << std::endl;
    // output is "8"

    return 0;
}

 

구조체 안에서 변수 하나는 4byte 단위로 할당되려고 한다.

struct A 에서

  • char a 는 1byte 가 할당 된다.
  • int b 는 4byte 를 할당 받는데, char a 뒤에 이어서 연속으로 할당 될 수 없다.
  • 그래서 char a 를 위해서 4byte 공간을 할당하지만, 그 중 1byte 만 사용하는 것이다. (3byte 가 padding 됨)

그래서, 같은 멤버 구성이지만, struct B 와 struct C 의 크기가 다른 것이다.


[3. #pragma pack]

의도한만큼의 공간을 사용하려면, #pragma pack 을 이용하여 강제할 수 있다.

#include <iostream>

#pragma pack(push, 1)
// #pragma pack(push, 2)
// #pragma pack(push, 4)
// #pragma pack(push, 8)
// #pragma pack(push, 16)
struct A {
    char a;    // 1byte
    int b;     // 5byte
    char c[2]; // 7byte
};

struct B {
    char a;   // 1byte
    int b;    // 5byte
    char c;   // 6byte
};

struct C {
    char a;   // 1byte
    char c;   // 2byte
    int b;    // 6byte
};

struct AA {
    char a;    // 1byte
    char c[2]; // 3byte
    int b;     // 7byte
};
#pragma pack(pop)

int main()
{
    std::cout << sizeof(A) << std::endl;
    std::cout << sizeof(B) << std::endl;
    std::cout << sizeof(C) << std::endl;
    std::cout << sizeof(AA) << std::endl;

    return 0;
}

 

#pragma pack(push, 1)

  • 현재 메모리 정렬 방식을 스택에 푸쉬하고, 1byte 단위로 정렬한다.

#prgma pack(pop)

  • 이전에 푸쉬한 메모리 정렬 방식을 복원한다.

구조체 크기를 줄여서, 전체 메모리 사용량을 줄일 순 있으나, 

경우에 따라서는 메모리 접근 상 성능에 영향을 줄 수 있다.

  • 메모리가 정렬되지 않으면 CPU는 메모리 접근 시 추가적인 작업을 해야 할 수 있으며, 이로 인해 성능이 저하될 수 있다. 
  • 예를 들어, 4바이트 크기의 데이터가 4바이트 경계가 아닌 곳에 배치되면 CPU는 두 번의 메모리 접근을 해야 할 수도 있다.
  • 특정 아키텍처에서는 정렬되지 않은 데이터에 접근하려고 할 때 segmentation fault 가 발생 할 수 있다.

[4. 32bit vs 64bit]

구조체의 sizeof 연산의 결과는 해당 구조체 멤버 중, 가장 큰 멤버의 align 크기에 따라 달라진다.

#include <iostream>

struct A {
        char a[2];
        int b;
        double c;
};

struct B {
        int b;
        double c;
};

struct C {
        char a;
        char b;
};

struct D {
        struct A a;
        struct C c;
};

struct E {
        char a;
        long long b;
};

int main()
{
        std::cout << "sizeof(void*) : " << sizeof(void*) << std::endl;
        std::cout << "sizeof(A) : " << sizeof(struct A) << std::endl;
        std::cout << "sizeof(B) : " << sizeof(struct B) << std::endl;
        std::cout << "sizeof(C) : " << sizeof(struct C) << std::endl;
        std::cout << "sizeof(D) : " << sizeof(struct D) << std::endl;
        std::cout << "sizeof(E) : " << sizeof(struct E) << std::endl;
        return 0;
}

 

먼저 64bit 로 빌드 후, 실행 결과

sizeof(void*) : 8
sizeof(A) : 16
sizeof(B) : 16
sizeof(C) : 2
sizeof(D) : 24
sizeof(E) : 16
  • A 의 경우, double 이 있으므로 8byte 단위로 정렬되야 함.
  • B 의 경우, double 이 있으므로 8byte 단위로 정렬되야 함.
  • C 의 경우, char 만 있으므로 1byte 단위로 정렬 됨.
  • D 의 경우, 각 struct 에서 가장 큰 단위가 double 이므로, 8byte 단위로 정렬 됨.
    # struct C 가 8바이트로 표현 됨.

 

32bit 로 빌드 후, 실행 결과

sizeof(void*) : 4
sizeof(A) : 16
sizeof(B) : 12
sizeof(C) : 2
sizeof(D) : 20
sizeof(E) : 12
  • 먼저 B 를 살펴보면, 64bit 일 때와 그 결과가 다름.
    => 32bit 이므로, 4바이트 를 넘는 단위로 align 할 수 없음.
    => 즉, 4바이트 단위로 align 됨.
  • A 의 경우, char a[2] 와 int b 를 4바이트 단위로 정렬될 수 없음. 
    => 8바이트로 두 멤버 표현
    => 8바이트로 double 표현
  • C 의 경우, char 만 있으므로 1byte 단위로 정렬 됨.
  • D 의 경우, A 에 double 이지만 32bit 빌드이므로, 4byte 단위로 정렬 되게 됨.
    =>  sizeof(C) 가 4바이트로 계산
    => 총 20바이트.

 

이렇듯, 제일 큰 멤버 의 크기 (primitive 한 자료형을 기준으로) 단위로 정렬 되며,

32bit 는 8바이트 자료형(double, long long) 이 있어도, 4바이트를 초과하는 단위로 정렬 할 수 없음.


[5. 결론]

전체 메모리 사용량과 성능 사이에 tradeoff 를 고려하여 구조체를 작성하되,

  • 32bit 프로그램이면, 가급적 4바이트 단위로 align 되는 방향으로 작성하는 것이 좋다.
  • 64bit 프로그램이면, 8바이트 자료형을 신중하게 사용하도록...

 

특히, char 나 short 자료형 사용 시 주의하도록..

'분류대기' 카테고리의 다른 글