메모리 정렬(memory alignment)은 특정 바이트 수의 배수인 주소에 데이터를 메모리에 배치하는 것입니다. 이는 아키텍처나 데이터 유형에 따라 달라집니다. 올바른 정렬은 성능과 하드웨어와의 올바른 작동, 일부 프로세서 지침에 매우 중요합니다.
이 문제의 역사.
초기 컴퓨터에서 정렬 위반은 하드웨어 오류(bus error)로 이어졌으며, 프로세서의 계산 능력은 비배수 데이터 주소에 민감했습니다. 현대 아키텍처에서도 올바른 정렬은 패널티 없이 1 사이클 내에 데이터에 접근할 수 있도록 보장합니다.
문제.
구조체가 정렬을 고려하지 않고 배치되면 읽기/쓰기가 느려지거나 불가능할 수 있습니다(예: ARM 또는 MIPS에서의 크래시). 또한 저수준 API, 장치, 네트워크 전송을 위한 데이터 직렬화 작업이 중단될 수 있습니다.
해결책.
C++에서는 정렬 제어를 위해 alignas 키워드(C++11)와 std::align, 그리고 비표준 컴파일러 속성(__attribute__((aligned(N))) in GCC/Clang, __declspec(align(N)) in MSVC)을 제공합니다.
코드 예:
struct alignas(16) MyStruct { int a; double b; char c; }; #include <iostream> #include <type_traits> int main() { std::cout << alignof(MyStruct) << std::endl; // 16 std::cout << sizeof(MyStruct) << std::endl; }
주요 기능:
구조체 멤버의 선언 순서가 구조체의 크기와 정렬에 영향을 줍니까?
예, 멤버의 순서는 서로 사이의 패딩을 직접적으로 결정하여 구조체 크기에 추가적인 바이트를 추가할 수 있습니다.
struct S1 { char a; int b; }; // 일반적으로 sizeof==8 (4바이트 정렬에서) struct S2 { int b; char a; }; // 일반적으로 sizeof==8, 하지만 padding이 더 적어질 수 있음
정렬을 위반하지 않고 구조체 크기를 줄일 수 있습니까?
예, 큰 멤버를 먼저 그룹화하고 작은 멤버를 나중에 배치하면 됩니다. 비트 수를 고려하여 데이터를 효과적으로 정렬합니다:
struct S { double d; int i; char c; }; // 분리된 것보다 나음
정렬되지 않은 주소를 포인터로 수동으로 작업하면 무슨 일이 발생하나요?
C++ 표준은 그러한 동작을 정의되지 않은 행동(undefined behavior)이라고 선언하며, 일부 CPU는 SIGBUS와 유사한 오류를 발생시킬 수 있습니다.
프로그래머가 SIMD 작업을 위해 자체 구조체를 구현했으나 정렬을 지정하지 않았습니다. 그 결과 프로그램이 일부 ARM 장치에서 정렬되지 않은 데이터 작업을 시도하는 동안 크래시 발생.
장점:
단점:
비디오 데이터 작업 프로젝트(SSE/AVX)에서 버퍼 구조체에 대해 alignas를 적용하여 항상 SIMD 지침을 효과적으로 사용할 수 있었습니다.
장점:
단점: