바이트 정렬 요구사항
시스템 별로 메모리에 효율적으로 접근하기 위해서
n 바이트 배수인 시작 주소에서만 메모리 접근이 가능하게끔 한다
예를 들어, x86 시스템은 4바이트 단위로 읽어오고
4바이트마다 정렬(aligned)되어 있다
따라서 각 컴파일러가 알아서 각 멤버의 시작 위치를 그 경계에 맞춰준다
그렇다면, 안 쓰는 바이트를 덧붙이는 경우가 발생하고
이를 패딩(padding)이라고 한다
서로 다른 시스템에서 저장한 파일을 서로 읽으려 할 때,
바이트 정렬 요구사항이 다르면 전혀 다른 데이터가 읽어질 수도 있다
구조체 바이트 정렬 방식
구조체에서 바이트 정렬은 2가지 규칙을 따른다
1. 각각의 자료형 크기의 배수에 해당하는 위치에만 들어간다
2. 끝이나는 구간은 반드시 구조체에서 가장 큰 바이트 배수로 끝이 나야 한다는 것이다
첫 번째 규칙 예시
struct A
{
char c1;
int i4a;
int i4b;
double d8;
};
4바이트 i4a는 4의 배수 자리에만 들어가야 해서 2, 3 바이트 위치가 아닌 4바이트에 위치했다
8바이트 d8은 8의 배수 자리에만 들어가야해서 12 ~ 15 바이트 위치가 아닌 16바이트에 위치했다
두 번째 규칙 예시
struct B
{
double d8;
int i4a;
int i4b;
char c1;
};
c1의 끝은 17바이트 위치이지만
구조체의 크기는 항상 가장 큰 바이트인 d8(8바이트)의 배수로 끝이 나야 하니
24바이트 까지 패딩이 되며 총 크기가 구성된다
명시적 패딩
위에서 보았던 구조체 A, B를 명시적 패딩을 해준다면 아래와 같다
물론, 명시적 패딩을 하지 않아도 자동적으로 패딩을 해준다
struct A
{
char c1;
char unused1[3];
int i4a;
int i4b;
char unused2[4];
double d8;
};
struct B
{
double d8;
int i4a;
int i4b;
char c1;
char unused[7];
};
바이트 정렬 크기 설정
#include <stdio.h>
#include <iostream>
#include <vector>
struct Aligned
{
double d8;
int i4a;
int i4b;
char c1;
};
int main()
{
std::vector<Aligned> vec(10);
}
위의 코드는 아래와 같은 형태로 구성된다
이러한 형태에서는 큰 문제점이 있다
일반적으로 캐시 라인은 64바이트이기에
구조체가 중간에 잘리게 되는 문제가 발생한다
결국, 구조체를 어떻게 짜느냐에 따라서 Race Condition 문제 혹은 False Sharing 문제가 발생한다
따라서, 구조체의 크기를 캐시 라인 크기에 맞춰줘야 한다
alignas
구조체의 크기를 캐시 라인 크기에 맞추기 위해서
C++ 11부터 alignas specifier가 등장했다
#include <stdio.h>
#include <iostream>
#include <vector>
struct alignas(32) Aligned
{
double d8;
int i4a;
int i4b;
char c1;
};
int main()
{
std::vector<Aligned> vec(10);
}
이렇게 alignas를 이용하면
원하는 크기만큼 남는 부분들을 패딩을 해주기 때문에
구조체가 잘리는 문제를 해결할 수 있다
'C > [코드조선] C 핵심' 카테고리의 다른 글
[C] 구조체 vs. 클래스 (0) | 2024.02.14 |
---|---|
[C] 비트필드와 비트플래그 (0) | 2024.02.14 |
[C] 버퍼, 버퍼링 (0) | 2024.02.13 |
[C] 구조체 복사 (0) | 2024.02.12 |
[C] typedef (0) | 2024.02.12 |