메모리 정렬의 목적


197

분명히 나는 ​​그것을 얻지 못한다. 길이가 1 바이트 인 메모리 워드가있는 메모리가 있다고 가정하십시오. 정렬 된 주소의 경우와 같이 정렬되지 않은 주소 (즉 4로 나눌 수 없음)의 단일 메모리 액세스에서 4 바이트 길이 변수에 액세스 할 수없는 이유는 무엇입니까?


17
수행 한 후 몇 가지 추가로 인터넷 검색을 나는 발견 정말 잘 문제를 설명 큰 링크를.
ark

학습을 시작한 사람들을위한이 작은 기사를 확인하십시오. blog.virtualmethodstudio.com/2017/03/memory-alignment-run-fools
darkgaze

3
@ark 링크 끊어짐
John Jiang

2
@JohnJiang 나는 새로운 링크를 여기에서 찾았다 고 생각한다. developer.ibm.com/technologies/systems/articles/pa-dalign
ejohnso49

답변:


63

많은 기본 프로세서의 한계입니다. 일반적으로 하나의 효율적인 워드 페치가 아닌 4 개의 비효율적 인 단일 바이트 페치를 수행하면 문제를 해결할 수 있지만 많은 언어 지정자는이를 불법화하고 모든 것을 강제로 정렬하는 것이 더 쉬울 것이라고 판단했습니다.

이 링크 에는 OP가 발견 한 훨씬 더 많은 정보가 있습니다.


311

최신 프로세서의 메모리 하위 시스템은 단어 크기를 세분화하고 정렬하여 메모리에 액세스하도록 제한됩니다. 여러 가지 이유가 있습니다.

속도

최신 프로세서에는 데이터를 가져와야하는 여러 수준의 캐시 메모리가 있습니다. 단일 바이트 읽기를 지원하면 메모리 서브 시스템 처리량이 실행 단위 처리량 (일명 cpu-bound)에 밀접하게 바인딩됩니다. 이는 하드 드라이브에서 동일한 이유로 여러 가지 이유로 PIO 모드가 DMA능가한 방식을 연상시킵니다 .

CPU는 항상 단어 크기 (32 비트 프로세서의 경우 4 바이트)로 읽으므로 정렬되지 않은 주소 액세스 (지원하는 프로세서에서)를 수행하면 프로세서가 여러 단어를 읽게됩니다. CPU는 요청한 주소가 차지하는 각 메모리 워드를 읽습니다. 이로 인해 요청 된 데이터에 액세스하는 데 필요한 메모리 트랜잭션 수가 최대 2 배까지 증폭됩니다.

이 때문에 4 바이트보다 2 바이트를 읽는 것이 매우 느릴 수 있습니다. 예를 들어 메모리에 다음과 같은 구조체가 있다고 가정 해보십시오.

struct mystruct {
    char c;  // one byte
    int i;   // four bytes
    short s; // two bytes
}

32 비트 프로세서에서는 다음과 같이 정렬 될 가능성이 높습니다.

구조 레이아웃

프로세서는 이러한 각 멤버를 한 번의 트랜잭션으로 읽을 수 있습니다.

전송 효율을 위해 압축 된 구조의 압축 된 버전을 가지고 있다고 가정 해 봅시다. 다음과 같이 보일 수 있습니다.

포장 된 구조

첫 번째 바이트를 읽는 것은 동일합니다.

프로세서에 0x0005에서 16 비트를 제공하도록 요청하면 0x0004에서 워드를 읽고 1 바이트 왼쪽으로 이동하여 16 비트 레지스터에 배치해야합니다. 추가 작업이 필요하지만 대부분 한 번의 주기로 처리 할 수 ​​있습니다.

0x0001에서 32 비트를 요청하면 2 배 증폭됩니다. 프로세서는 0x0000에서 결과 레지스터로 읽고 왼쪽으로 1 바이트 이동 한 다음 0x0004에서 임시 레지스터로 다시 읽고 오른쪽으로 3 바이트 이동 한 다음 OR결과 레지스터로 이동합니다.

범위

주어진 주소 공간에 대해 아키텍처가 2 LSB가 항상 0이라고 가정 할 수있는 경우 (예 : 32 비트 시스템) 4 배 더 많은 메모리에 액세스 할 수 있습니다 (2 개의 저장된 비트는 4 개의 개별 상태를 나타낼 수 있음). 플래그와 같은 것을 위해 2 비트의 메모리. 주소에서 2 개의 LSB를 제거하면 4 바이트 단위로 정렬됩니다. 4 바이트 의 보폭 이라고도 합니다. 주소가 증가 할 때마다 비트 0이 아닌 비트 2가 효과적으로 증가합니다. 즉, 마지막 2 비트는 항상 계속 유지됩니다 00.

이것은 시스템의 물리적 설계에도 영향을 줄 수 있습니다. 주소 버스에 2 비트가 더 적은 비트가 필요한 경우 CPU에 2 개의 핀이 줄어들고 회로 보드에 2 개의 더 적은 트레이스가있을 수 있습니다.

원 자성

CPU는 정렬 된 메모리 워드에서 원자 적으로 작동 할 수 있습니다. 즉, 다른 명령으로 해당 작업을 중단 할 수 없습니다. 이는 많은 잠금없는 데이터 구조 및 기타 동시성 패러다임 의 올바른 작동에 중요합니다 .

결론

프로세서의 메모리 시스템은 여기에 설명 된 것보다 상당히 복잡하고 복잡합니다. x86 프로세서가 실제로 메모리 를 지원 하는 방법에 대한 설명 (많은 프로세서가 비슷하게 작동).

이 IBM 기사 에서 읽을 수있는 메모리 정렬을 준수하면 더 많은 이점이 있습니다 .

컴퓨터의 주요 용도는 데이터를 변환하는 것입니다. 최신 메모리 아키텍처와 기술은 수십 년에 걸쳐 최적화되어보다 안정적인 방식으로 더 많은 데이터를 인 / 아웃 / 더 빠르고 더 빠른 실행 유닛간에 가져올 수있게되었습니다.

보너스 : 캐시

이전에 언급 한 성능에 대한 또 다른 정렬은 64B 인 캐시 라인 (예 : 일부 CPU)에 대한 정렬입니다.

캐시를 활용하여 얼마나 많은 성능을 얻을 수 있는지에 대한 자세한 내용은 Gallery of Processor Cache Effects를 참조하십시오 . 캐시 라인 크기에 대한질문에서

캐시 라인에 대한 이해는 특정 유형의 프로그램 최적화에 중요 할 수 있습니다. 예를 들어, 데이터의 정렬은 동작이 하나 또는 두 개의 캐시 라인을 터치하는지 여부를 결정할 수있다. 위의 예에서 보았 듯이 이것은 잘못 정렬 된 경우 작업이 두 배 느리다는 것을 쉽게 의미 할 수 있습니다.


다음 구조체 xyz는 크기가 서로 다릅니다. 각 멤버의 규칙은 크기의 배수 인 주소로 시작해야하고 strcut은 구조체의 멤버의 최대 크기의 배수로 끝나야합니다. struct x {short s; // 2 바이트와 2 개의 패딩 타이트 int i; // 4 바이트 char c; // 1 바이트 및 3 패딩 바이트 long long l; }; struct y {int i; // 4 바이트 char c; // 1 바이트 및 1 패딩 바이트 short s; // 2 바이트}; struct z {int i; // 4 바이트 short s; // 2 바이트 char c; // 1 바이트 및 1 패딩 바이트};
Gavin

1
올바르게 이해하면 컴퓨터가 한 단계에서 정렬되지 않은 단어를 읽을 수없는 이유는 addesses가 32 비트가 아닌 30 비트를 사용하기 때문입니다.
GetFree

1
@chux 그렇습니다. 절대 절대 절대 보유하지 않습니다. 8088은 속도와 비용 간의 트레이드 오프에 대한 흥미로운 연구이며 기본적으로 16 비트 8086 (전체 16 비트 외부 버스가 있음)이지만 버스 라인의 절반만으로 생산 비용을 절약합니다. 이 때문에 8088은 전체 16 비트 워드를 얻기 위해 두 번의 읽기를 수행해야했기 때문에 8086보다 메모리에 액세스하기 위해 두 번의 클록 사이클이 필요했습니다. 흥미로운 부분 인 8086은 단일 사이클에서 16 비트 읽기로 정렬 된 워드를 수행 할 수 있으며 정렬 되지 않은 읽기에는 2가 걸린다는 것입니다. 8088에 하프 워드 버스가 있다는 사실은 이러한 속도 저하를 막았습니다.
joshperry

2
@joshperry : 약간의 수정 : 8086은 4 번의 주기로 워드 정렬 된 16 비트 읽기를 수행 할 수 있지만 정렬되지 않은 읽기에는 8이 걸립니다 . 메모리 인터페이스가 느리기 때문에 8088 기반 시스템의 실행 시간은 일반적으로 명령 페치에 의해 지배됩니다. "MOV AX, BX"와 같은 명령어는 "XCHG AX, BX"보다 명목상 1주기 빠르지 만, 실행되기 전에 코드 바이트 당 4 회 이상의주기를 수행하는 명령어가 선행되거나 이어지지 않는 한 4 배 더 길어집니다. 실행하십시오. 8086에서 코드 페칭은 때때로 실행을 유지할 수 있지만, 다음을 사용하지 않는 한 8088에서 ...
supercat

1
사실, 마틴. 토론 인트라 구조에 초점을 맞추기 위해 패딩 바이트를 생략했지만 포함시키는 것이 좋습니다.
joshperry

22

일부 프로세서 ( nehalem 이이 작업을 수행 할 수 있음 )를 사용할 수 있지만 이전에는 모든 메모리 액세스가 64 비트 (또는 32 비트) 라인에 정렬되었습니다. 버스의 너비는 64 비트이므로 한 번에 64 비트를 가져와야했습니다 64 비트의 정렬 된 '청크 (chunks)'에서 이들을 가져 오는 것이 훨씬 쉬워졌습니다.

따라서 단일 바이트를 얻으려면 64 비트 청크를 가져온 다음 원하지 않는 비트를 마스킹하십시오. 바이트가 오른쪽 끝에 있으면 쉽고 빠르지 만 64 비트 청크의 중간에 있으면 원하지 않는 비트를 가리고 데이터를 올바른 위치로 옮겨야합니다. 더 나쁜 것은 2 바이트 변수를 원했지만 2 청크로 분할 된 경우 필요한 메모리 액세스의 두 배가 필요하다는 것입니다.

따라서 모든 사람들이 메모리가 저렴하다고 생각할 때, 메모리를 낭비하면서 코드를 더 빠르고 효율적으로 실행할 수 있도록 컴파일러가 프로세서의 청크 크기에 맞게 데이터를 정렬하도록 만들었습니다.


5

근본적으로 그 이유는 메모리 버스가 메모리 크기보다 훨씬 작은 특정 길이를 갖기 때문입니다.

따라서 CPU는 요즘 32KB 인 온칩 L1 캐시를 읽습니다. 그러나 L1 캐시를 CPU에 연결하는 메모리 버스는 캐시 라인 크기의 너비가 훨씬 작습니다. 이것은 128 비트 정도 입니다.

그래서:

262,144 bits - size of memory
    128 bits - size of bus

잘못 정렬 된 액세스는 때때로 두 개의 캐시 라인과 겹치며, 데이터를 얻으려면 완전히 새로운 캐시 읽기가 필요합니다. 심지어 DRAM으로 빠져 나갈 수도 있습니다.

또한, CPU의 일부는 각각 데이터가있는 두 개의 서로 다른 캐시 라인에서 단일 오브젝트를 구성하기 위해 머리 위에 서 있어야합니다. 한 줄에서, 그것은 매우 높은 순서의 비트에, 다른 하나는 매우 낮은 순서의 비트에있을 것입니다.

파이프 라인에 완전히 통합 된 전용 하드웨어가있을 것입니다. 정렬 된 객체를 CPU 데이터 버스의 필요한 비트로 이동하는 것을 처리하지만 잘못 정렬 된 객체에는 이러한 하드웨어가 부족할 수 있습니다. 프로그램들.

어쨌든 때때로 필요한 두 번째 메모리 읽기는 정렬되지 않은 메모리 작업을 패치하기 위해 (가설 적으로 어리석게) 특수 목적의 하드웨어가 얼마나 많은지에 관계없이 파이프 라인을 느리게합니다.


5

@joshperry는이 질문에 훌륭한 답변을주었습니다. 그의 대답 외에도 설명 된 효과, 특히 2X 증폭을 그래픽으로 보여주는 숫자가 있습니다. 다음 은 다양한 단어 정렬의 효과를 보여주는 Google 스프레드 시트 링크 입니다. 또한 테스트 코드 가 포함 된 Github 요점에 대한 링크가 있습니다. 테스트 코드는 @joshperry가 참조한 Jonathan Rentzsch가 작성한 기사 에서 수정되었습니다 . 테스트는 쿼드 코어 2.8GHz Intel Core i7 64 비트 프로세서 및 16GB RAM이 장착 된 Macbook Pro에서 실행되었습니다.

여기에 이미지 설명을 입력하십시오


4
무엇 xy좌표는 뜻?
shuva

1
어떤 세대의 코어 i7? (코드 링크를 게시 해 주셔서 감사합니다!)
Nick Desaulniers

2

바이트 주소 지정 가능 메모리가있는 시스템에 32 비트 폭의 메모리 버스가있는 경우 동일한 주소를 읽거나 쓰도록 연결된 4 개의 바이트 폭 메모리 시스템이 사실상 존재한다는 의미입니다. 정렬 된 32 비트 읽기에는 4 개의 메모리 시스템 모두에서 동일한 주소에 저장된 정보가 필요하므로 모든 시스템이 동시에 데이터를 제공 할 수 있습니다. 정렬되지 않은 32 비트 읽기는 일부 메모리 시스템이 한 주소에서 데이터를 반환하고 일부는 다음 상위 주소에서 데이터를 반환해야합니다. 이러한 요청을 수행 할 수 있도록 최적화 된 일부 메모리 시스템이 있지만 (어드레스 외에, 지정된 것보다 하나 높은 주소를 사용하도록하는 "플러스 1"신호를 효과적으로 가짐) 이러한 기능은 상당한 비용을 추가합니다 및 메모리 시스템에 대한 복잡성;


2

32 비트 데이터 버스가있는 경우 메모리에 연결된 주소 버스 주소 라인은 A 2 에서 시작 하므로 단일 버스주기에서 32 비트 정렬 주소 만 액세스 할 수 있습니다.

따라서 워드가 주소 정렬 경계에 걸쳐있는 경우, 즉 16/32 비트 데이터의 경우 A 0 또는 32 비트 데이터의 A 1 이 0이 아닌 경우 데이터를 얻기 위해 두 개의 버스주기가 필요합니다.

일부 아키텍처 / 명령 집합은 정렬되지 않은 액세스를 지원하지 않으며 이러한 시도에서 예외를 생성하므로 컴파일러에서 생성 된 정렬되지 않은 액세스 코드는 추가 버스주기뿐만 아니라 추가 명령이 필요하므로 효율성이 떨어집니다.


0

PowerPC에서는 홀수 주소에서 문제없이 정수를로드 할 수 있습니다.

Sparc와 I86 그리고 Itatnium은 이것을 시도 할 때 하드웨어 예외를 발생시킵니다.

하나의 32 비트로드 대 4 개의 8 비트로드는 대부분의 최신 프로세서에서 큰 차이를 만들지 않습니다. 데이터가 이미 캐시에 있는지 여부는 훨씬 더 큰 영향을 미칩니다.


Sparc에서는 이것이 "버스 오류"였으므로 Peter Van der Linden의 "전문가 C 프로그래밍 : 깊은 C 비밀"의 "버스 오류, 기차를 타십시오"장
jjg
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.