[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 자료형 사용 시 주의하도록..
'분류대기' 카테고리의 다른 글
[인코딩] UTF-8 (0) | 2025.01.23 |
---|---|
[리눅스] goto 사용 시 컴파일 (0) | 2025.01.14 |
swagger-editor 설치 (0) | 2024.06.12 |
CUDA 설치하기, [윈도우, 리눅스] (0) | 2024.05.14 |
[내용 채워야 됨] CUDA 사용해보기 (0) | 2024.02.22 |