Memory Alignment(메모리 정렬)는 왜 필요할까?
자, 32비트 CPU에서 4-byte 데이터가 4의 배수가 아닌 주소에 존재한다고 생각해보자.
아래와 같이 데이터가 4-byte의 경계에 걸쳐있다.
이 데이터를 읽으려면 어떻게 해야할까?
0 1 2 3 4 5 6 7 8 = address
|-----------|-----------|
| | |00|F4|14|1F| | | = data
|-----------|-----------|
CPU는 워드 사이즈로 워드 사이즈의 배수인 주소만 읽기가 가능하다.
그래서 이렇게 되면 CPU는 0-3 범위의 데이터를 한번 읽고,
4-7 범위를 한번 더 읽은 뒤 합쳐야 한다.
즉 한번에 읽을 수 없어서 추가적인 과정이 필요하다.
캐시라인이나 페이지경계에 걸쳐 있다면 문제가 생기기도 한다.
x86 같은 아키텍쳐는 이런 정렬되진 않은 데이터도 읽은 뒤 합쳐주지만,
GPU나 RISC계열 CPU는 대부분 이를 지원하지 않아 bus-error를 낸다.
여기까지가 일반적인 설명인데,
“그래서 왜 CPU는 꼭 워드 사이즈 경계로만 데이터를 읽는가?”
32비트 CPU가 하나 있다고 생각해보자.
CPU가 메모리에서 load하기 위해선 IR를 다음과 같이 세팅한다.
1000 0000 0000 0000 0000 0000 0000 0000
||
|└-> 0 : R0
| 1 : R1
|
└--> 0 : STORE
1 : LOAD
- 0번 비트는 메모리에 store를 하는지, load를 하는지 지정한다.
- 1번 비트는 어떤 레지스터의 내용을 store 할지,
어떤 레지스터에 내용을 load 해올지 지정한다. - 나머지 30개의 비트는 가져올 메모리의 주소를 표현한다.
그런데 어라, 주소를 표현할 수 있는 비트가 30개 밖에 안된다.
30비트로 32비트를 표현해야 하는데 어떻게 하면 될까.
30비트가 나타내는 주소의 단위가 4바이트가 되도록 하면 된다.
그러면 표현할 수 있는 주소가 4배 늘어나므로 30비트로 32비트를 표현할 수 있다.
┌-------------30 bit----------------┐
???? ???? ???? ???? ???? ???? ???? ??00
최하위 비트 2개가 무조건 0이라고 지정하면
4의 배수만 표현이 가능하지만
위에서 얘기한대로 30비트로 32비트의 범위를 표현가능해진다.
(오해할 수 있는데, CPU에서 포인터 주소연산 할 때에는 여전히 1바이트 단위이다.
CPU에서 메모리에 요청할 때 4바이트 단위로 요청해 가져와서 따로 후처리를 한다는 것이다.)
위와 같은 이유들로 메모리 정렬이 필요한 것이다.
(이유 중 하나 일 뿐이지, 많은 CPU가 이 방식을 사용하는 것은 아니다.)