분명히 나는 그것을 얻지 못한다. 길이가 1 바이트 인 메모리 워드가있는 메모리가 있다고 가정하십시오. 정렬 된 주소의 경우와 같이 정렬되지 않은 주소 (즉 4로 나눌 수 없음)의 단일 메모리 액세스에서 4 바이트 길이 변수에 액세스 할 수없는 이유는 무엇입니까?
분명히 나는 그것을 얻지 못한다. 길이가 1 바이트 인 메모리 워드가있는 메모리가 있다고 가정하십시오. 정렬 된 주소의 경우와 같이 정렬되지 않은 주소 (즉 4로 나눌 수 없음)의 단일 메모리 액세스에서 4 바이트 길이 변수에 액세스 할 수없는 이유는 무엇입니까?
답변:
최신 프로세서의 메모리 하위 시스템은 단어 크기를 세분화하고 정렬하여 메모리에 액세스하도록 제한됩니다. 여러 가지 이유가 있습니다.
최신 프로세서에는 데이터를 가져와야하는 여러 수준의 캐시 메모리가 있습니다. 단일 바이트 읽기를 지원하면 메모리 서브 시스템 처리량이 실행 단위 처리량 (일명 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를 참조하십시오 . 캐시 라인 크기에 대한 이 질문에서
캐시 라인에 대한 이해는 특정 유형의 프로그램 최적화에 중요 할 수 있습니다. 예를 들어, 데이터의 정렬은 동작이 하나 또는 두 개의 캐시 라인을 터치하는지 여부를 결정할 수있다. 위의 예에서 보았 듯이 이것은 잘못 정렬 된 경우 작업이 두 배 느리다는 것을 쉽게 의미 할 수 있습니다.
일부 프로세서 ( nehalem 이이 작업을 수행 할 수 있음 )를 사용할 수 있지만 이전에는 모든 메모리 액세스가 64 비트 (또는 32 비트) 라인에 정렬되었습니다. 버스의 너비는 64 비트이므로 한 번에 64 비트를 가져와야했습니다 64 비트의 정렬 된 '청크 (chunks)'에서 이들을 가져 오는 것이 훨씬 쉬워졌습니다.
따라서 단일 바이트를 얻으려면 64 비트 청크를 가져온 다음 원하지 않는 비트를 마스킹하십시오. 바이트가 오른쪽 끝에 있으면 쉽고 빠르지 만 64 비트 청크의 중간에 있으면 원하지 않는 비트를 가리고 데이터를 올바른 위치로 옮겨야합니다. 더 나쁜 것은 2 바이트 변수를 원했지만 2 청크로 분할 된 경우 필요한 메모리 액세스의 두 배가 필요하다는 것입니다.
따라서 모든 사람들이 메모리가 저렴하다고 생각할 때, 메모리를 낭비하면서 코드를 더 빠르고 효율적으로 실행할 수 있도록 컴파일러가 프로세서의 청크 크기에 맞게 데이터를 정렬하도록 만들었습니다.
근본적으로 그 이유는 메모리 버스가 메모리 크기보다 훨씬 작은 특정 길이를 갖기 때문입니다.
따라서 CPU는 요즘 32KB 인 온칩 L1 캐시를 읽습니다. 그러나 L1 캐시를 CPU에 연결하는 메모리 버스는 캐시 라인 크기의 너비가 훨씬 작습니다. 이것은 128 비트 정도 입니다.
그래서:
262,144 bits - size of memory
128 bits - size of bus
잘못 정렬 된 액세스는 때때로 두 개의 캐시 라인과 겹치며, 데이터를 얻으려면 완전히 새로운 캐시 읽기가 필요합니다. 심지어 DRAM으로 빠져 나갈 수도 있습니다.
또한, CPU의 일부는 각각 데이터가있는 두 개의 서로 다른 캐시 라인에서 단일 오브젝트를 구성하기 위해 머리 위에 서 있어야합니다. 한 줄에서, 그것은 매우 높은 순서의 비트에, 다른 하나는 매우 낮은 순서의 비트에있을 것입니다.
파이프 라인에 완전히 통합 된 전용 하드웨어가있을 것입니다. 정렬 된 객체를 CPU 데이터 버스의 필요한 비트로 이동하는 것을 처리하지만 잘못 정렬 된 객체에는 이러한 하드웨어가 부족할 수 있습니다. 프로그램들.
어쨌든 때때로 필요한 두 번째 메모리 읽기는 정렬되지 않은 메모리 작업을 패치하기 위해 (가설 적으로 어리석게) 특수 목적의 하드웨어가 얼마나 많은지에 관계없이 파이프 라인을 느리게합니다.
@joshperry는이 질문에 훌륭한 답변을주었습니다. 그의 대답 외에도 설명 된 효과, 특히 2X 증폭을 그래픽으로 보여주는 숫자가 있습니다. 다음 은 다양한 단어 정렬의 효과를 보여주는 Google 스프레드 시트 링크 입니다. 또한 테스트 코드 가 포함 된 Github 요점에 대한 링크가 있습니다. 테스트 코드는 @joshperry가 참조한 Jonathan Rentzsch가 작성한 기사 에서 수정되었습니다 . 테스트는 쿼드 코어 2.8GHz Intel Core i7 64 비트 프로세서 및 16GB RAM이 장착 된 Macbook Pro에서 실행되었습니다.
x
과 y
좌표는 뜻?
바이트 주소 지정 가능 메모리가있는 시스템에 32 비트 폭의 메모리 버스가있는 경우 동일한 주소를 읽거나 쓰도록 연결된 4 개의 바이트 폭 메모리 시스템이 사실상 존재한다는 의미입니다. 정렬 된 32 비트 읽기에는 4 개의 메모리 시스템 모두에서 동일한 주소에 저장된 정보가 필요하므로 모든 시스템이 동시에 데이터를 제공 할 수 있습니다. 정렬되지 않은 32 비트 읽기는 일부 메모리 시스템이 한 주소에서 데이터를 반환하고 일부는 다음 상위 주소에서 데이터를 반환해야합니다. 이러한 요청을 수행 할 수 있도록 최적화 된 일부 메모리 시스템이 있지만 (어드레스 외에, 지정된 것보다 하나 높은 주소를 사용하도록하는 "플러스 1"신호를 효과적으로 가짐) 이러한 기능은 상당한 비용을 추가합니다 및 메모리 시스템에 대한 복잡성;
32 비트 데이터 버스가있는 경우 메모리에 연결된 주소 버스 주소 라인은 A 2 에서 시작 하므로 단일 버스주기에서 32 비트 정렬 주소 만 액세스 할 수 있습니다.
따라서 워드가 주소 정렬 경계에 걸쳐있는 경우, 즉 16/32 비트 데이터의 경우 A 0 또는 32 비트 데이터의 A 1 이 0이 아닌 경우 데이터를 얻기 위해 두 개의 버스주기가 필요합니다.
일부 아키텍처 / 명령 집합은 정렬되지 않은 액세스를 지원하지 않으며 이러한 시도에서 예외를 생성하므로 컴파일러에서 생성 된 정렬되지 않은 액세스 코드는 추가 버스주기뿐만 아니라 추가 명령이 필요하므로 효율성이 떨어집니다.