[memory] 캐시 라인은 어떻게 작동합니까?

프로세서는 캐시 라인을 통해 데이터를 캐시에 가져옵니다. 예를 들어, Atom 프로세서에서 실제 데이터의 크기에 관계없이 한 번에 약 64 바이트를 가져옵니다.

내 질문은 :

캐시에서 가져올 64 바이트는 메모리에서 1 바이트를 읽어야한다고 상상해보십시오.

내가 볼 수있는 두 가지 가능성은 64 바이트가 관심있는 바이트 아래의 가장 가까운 64 바이트 경계에서 시작하거나 64 바이트가 미리 결정된 방식으로 바이트 주위에 퍼져 있다는 것입니다 (예 : 절반 이하, 절반 위 또는 모든 것 위에).

무엇 이니?



답변

로드중인 바이트 또는 단어를 포함하는 캐시 라인이 캐시에 아직없는 경우 CPU는 캐시 라인 경계에서 시작하는 64 바이트 (필요한 주소 아래의 가장 큰 주소 인 64의 배수)를 요청합니다. .

최신 PC 메모리 모듈은 한 번에 64 비트 (8 바이트)를 8 번의 전송 버스트로 전송 하므로 한 명령으로 메모리에서 전체 캐시 라인의 읽기 또는 쓰기를 트리거합니다. (DDR1 / 2 / 3 / 4 SDRAM 버스트 전송 크기는 최대 64B로 구성 가능합니다. CPU는 캐시 라인 크기와 일치하도록 버스트 전송 크기를 선택하지만 64B는 일반적입니다)

일반적으로 프로세서가 메모리 액세스를 예측할 수없고 (프리 페치 할 수없는 경우) 검색 프로세스는 ~ 90 나노초 또는 ~ 250 클럭 사이클 (주소를 알고있는 CPU에서 데이터를 수신하는 CPU까지)이 걸릴 수 있습니다.

대조적으로 L1 캐시의 적중은 3 또는 4주기의로드 사용 대기 시간을 가지며, 상점 재로드는 최신 x86 CPU에서 4 또는 5주기의 상점 전달 대기 시간을 갖습니다. 다른 아키텍처에서도 상황이 비슷합니다.

추가 자료 : Ulrich Drepper의 모든 프로그래머가 기억해야 할 사항 . 소프트웨어 프리 페치 조언은 약간 구식입니다. 최신 HW 프리 페처는 더 똑똑하고 하이퍼 스레딩은 P4 일보다 훨씬 좋습니다 (따라서 프리 페치 스레드는 일반적으로 낭비입니다). 또한 태그 위키에는 해당 아키텍처에 대한 많은 성능 링크가 있습니다.


답변

캐시 라인의 폭이 64 바이트 인 경우, 이는 64로 나눌 수있는 주소에서 시작하는 메모리 블록에 해당합니다. 주소의 최하위 6 비트는 캐시 라인에 대한 오프셋입니다.

따라서 임의의 주어진 바이트에 대해, 페치되어야하는 캐시 라인은 주소의 가장 작은 6 비트를 클리어함으로써 찾을 수 있는데, 이는 64로 나눌 수있는 가장 가까운 어드레스로 반올림하는 것에 해당한다.

이 작업은 하드웨어에서 수행되지만 일부 참조 C 매크로 정의를 사용하여 계산을 표시 할 수 있습니다.

#define CACHE_BLOCK_BITS 6
#define CACHE_BLOCK_SIZE (1U << CACHE_BLOCK_BITS)  /* 64 */
#define CACHE_BLOCK_MASK (CACHE_BLOCK_SIZE - 1)    /* 63, 0x3F */

/* Which byte offset in its cache block does this address reference? */
#define CACHE_BLOCK_OFFSET(ADDR) ((ADDR) & CACHE_BLOCK_MASK)

/* Address of 64 byte block brought into the cache when ADDR accessed */
#define CACHE_BLOCK_ALIGNED_ADDR(ADDR) ((ADDR) & ~CACHE_BLOCK_MASK)


답변

우선 주 메모리 액세스는 매우 비쌉니다. 현재 2GHz CPU (가장 느린 속도)에는 초당 2G 틱 (사이클)이 있습니다. CPU (현재 가상 코어)는 틱당 한 번 레지스터에서 값을 가져올 수 있습니다. 가상 코어는 여러 처리 장치 (ALU-산술 논리 장치, FPU 등)로 구성되므로 가능한 경우 특정 명령을 실제로 병렬로 처리 할 수 ​​있습니다.

메인 메모리 액세스 비용은 약 70ns ~ 100ns입니다 (DDR4가 약간 빠름). 이번에는 기본적으로 L1, L2 및 L3 캐시를 찾고 메모리 (메모리 컨트롤러에 명령을 보내 메모리 뱅크로 전송)를 누르고 응답을 기다렸다가 완료합니다.

100ns는 약 200 틱을 의미합니다. 따라서 기본적으로 프로그램이 항상 각 메모리가 액세스하는 캐시를 놓치면 CPU는 메모리를 기다리는 동안 유휴 상태 (메모리 만 읽는 경우)의 약 99,5 %를 소비합니다.

속도를 높이기 위해 L1, L2, L3 캐시가 있습니다. 그들은 칩에 직접 놓인 메모리를 사용하고 다른 종류의 트랜지스터 회로를 사용하여 주어진 비트를 저장합니다. CPU는 일반적으로 고급 기술을 사용하여 생성되고 L1, L2, L3 메모리에서 생산 오류가 발생하여 CPU를 쓸모 없게 만들 수 있기 때문에 더 많은 공간과 에너지가 필요하며 주 메모리보다 비용이 많이 듭니다. 큰 L1, L2, L3 캐시는 오류율을 증가시켜 ROI를 직접 감소시키는 수율을 감소시킵니다. 따라서 사용 가능한 캐시 크기와 관련하여 큰 절충안이 있습니다.

(현재 실제 생산 결함이 캐시 메모리 영역이 CPU 결함을 전체적으로 렌더링 할 가능성을 줄이기 위해 특정 부분을 비활성화 할 수 있도록 L1, L2, L3 캐시를 더 생성합니다).

타이밍 아이디어를 제공하려면 (출처 : 캐시 및 메모리 액세스 비용 )

  • L1 캐시 : 1ns ~ 2ns (2-4주기)
  • L2 캐시 : 3ns ~ 5ns (6-10 사이클)
  • L3 캐시 : 12ns ~ 20ns (24-40주기)
  • RAM : 60ns (120 회)

우리는 서로 다른 CPU 유형을 혼합하기 때문에 추정치 일 뿐이지 만 메모리 값을 가져올 때 실제로 어떤 일이 발생하는지 알고 특정 캐시 계층에서 적중이나 누락이 발생할 수 있습니다.

따라서 캐시는 기본적으로 메모리 액세스 속도를 크게 향상시킵니다 (60ns 대 1ns).

값을 가져 와서 다시 읽을 수 있도록 캐시에 저장하면 자주 액세스하는 변수에는 적합하지만 메모리 복사 작업의 경우 값을 읽고 어딘가에 값을 쓰고 절대 읽지 않기 때문에 여전히 느려집니다. 다시 … 캐시 적중이없고, 죽은 느린 (이것은 우리가 순서가 잘못되어 병렬로 발생할 수 있습니다).

이 메모리 사본은 매우 중요하므로 속도를 높일 수있는 다른 방법이 있습니다. 초기에는 메모리가 종종 CPU 외부의 메모리를 복사 할 수있었습니다. 메모리 컨트롤러에서 직접 처리했기 때문에 메모리 복사 작업으로 인해 캐시가 오염되지 않았습니다.

그러나 일반 메모리 사본 외에도 메모리의 다른 직렬 액세스가 매우 일반적이었습니다. 일련의 정보를 분석하는 것이 한 예입니다. 정수 배열을 가지며 합계, 평균, 평균 또는 더 간단한 특정 값 찾기 (필터 / 검색)는 범용 CPU에서 매번 실행되는 또 다른 매우 중요한 알고리즘 클래스였습니다.

따라서 메모리 액세스 패턴을 분석함으로써 데이터를 매우 자주 읽는다는 것이 명백해졌습니다. 프로그램이 인덱스 i에서 값을 읽는 경우 프로그램도 값 i + 1을 읽을 가능성이 높습니다. 이 확률은 같은 프로그램이 i + 2 등을 읽는 확률보다 약간 높습니다.

따라서 메모리 주소가 주어지면 미리 읽고 추가 값을 가져 오는 것이 좋습니다. 이것이 부스트 모드가있는 이유입니다.

부스트 모드에서의 메모리 액세스는 주소가 전송되고 여러 값이 순차적으로 전송됨을 의미합니다. 각각의 추가 값 전송에는 추가로 약 10ns (또는 그 이하)가 소요됩니다.

또 다른 문제는 주소였습니다. 주소를 보내는 데 시간이 걸립니다. 메모리의 많은 부분을 처리하려면 큰 주소를 보내야합니다. 초기에는 주소 버스가 단일주기 (틱)로 주소를 전송하기에 충분히 크지 않았으며 더 많은 지연을 추가하여 주소를 전송하기 위해 하나 이상의주기가 필요했습니다.

예를 들어 64 바이트의 캐시 라인은 메모리가 64 바이트 크기의 별개의 (겹치지 않는) 메모리 블록으로 분할됨을 의미합니다. 64 바이트는 각 블록의 시작 주소가 가장 낮은 6 개의 주소 비트를 가지며 항상 0임을 의미합니다. 따라서 매번이 6 개의 0 비트를 전송할 필요가 없으며, 주소 버스 폭에 관계없이 주소 공간을 64 배 늘릴 필요가 없습니다 (환영).

캐시 라인이 해결하는 또 다른 문제 (주소 버스에서 6 비트를 저장 / 해제 / 저장하는 것 외에)는 캐시가 구성되는 방식에 있습니다. 예를 들어, 캐시가 8 바이트 (64 비트) 블록 (셀)으로 분할 될 경우, 메모리 셀의 주소를 저장해야하는 경우이 캐시 셀은 그와 함께 값을 보유합니다. 주소가 64 비트 일 경우 이는 캐시 크기의 절반이 주소에서 소비되어 100 %의 오버 헤드가 발생 함을 의미합니다.

캐시 라인이 64 바이트이고 CPU가 64 비트-6 비트 = 58 비트 (0 비트를 너무 정확하게 저장할 필요가 없음)를 사용할 수 있으므로 오버 헤드 58 비트 (11 % 오버 헤드)로 64 바이트 또는 512 비트를 캐시 할 수 있습니다. 실제로 저장된 주소는 이보다 훨씬 작지만 상태 정보가 있습니다 (캐시 라인이 유효하고 정확하고 더럽고 램으로 다시 써야하는 등).

또 다른 측면은 세트 연관 캐시가 있다는 것입니다. 모든 캐시 셀이 특정 주소를 저장할 수있는 것은 아니며 일부 주소 만 저장할 수 있습니다. 이것은 필요한 저장된 주소 비트를 더 작게 만들고, 캐시의 병렬 액세스를 허용합니다 (각 서브 세트는 한 번 액세스 할 수 있지만 다른 서브 세트와는 독립적 임).

다른 가상 코어, 코어 당 독립적 인 다중 처리 장치 및 하나의 메인 보드 (최대 48 개 이상의 프로세서를 수용하는 보드가 있음)에 여러 개의 프로세서간에 캐시 / 메모리 액세스를 동기화 할 때 더욱 특히 문제가 있습니다.

이것이 기본적으로 현재 캐시 라인을 가진 이유입니다. 미리 읽는 것의 이점은 매우 높으며 캐시 라인에서 단일 바이트를 읽고 다시 읽지 않는 최악의 경우는 확률이 매우 얇기 때문에 매우 얇습니다.

캐시 라인 (64)의 크기는 더 큰 캐시 라인들 사이에서 현명하게 선택된 트레이드 오프 (trade-off)로서, 가까운 미래에도 전체 캐시 라인을 페치하는 데 걸리는 시간 동안 마지막 바이트가 읽히지 않을 수있다 메모리에서 (및 다시 쓰기) 캐시 구성의 오버 헤드와 캐시 및 메모리 액세스의 병렬화.


답변

프로세서에는 다중 레벨 캐시 (L1, L2, L3)가있을 수 있으며 크기와 속도가 다릅니다.

그러나 각 캐시에 정확히 들어가는 내용과 해당 특정 프로세서에서 사용하는 분기 예측 변수 및 프로그램의 명령 / 데이터가 어떻게 작동하는지 이해해야합니다.

분기 예측기 , CPU 캐시교체 정책 에 대해 읽으십시오 .

이것은 쉬운 일이 아닙니다. 하루가 끝나고 성능 테스트를 원하는 경우 Cachegrind 와 같은 도구를 사용할 수 있습니다 . 그러나 이것은 시뮬레이션이므로 결과가 다소 다를 수 있습니다.


답변

모든 하드웨어가 다르기 때문에 확실하게 말할 수는 없지만 일반적으로 “64 바이트는 가장 가까운 64 바이트 경계에서 시작합니다”는 CPU에 대한 매우 빠르고 간단한 조작이기 때문입니다.


답변