홈으로 돌아가기

CPU 메모리 계층 구조: 지연 시간과 최적화

메모리 계층 구조 챕터는 cache misses가 성능을 지배하는 이유를 설명합니다. registers부터 DRAM까지의 레벨, 64 B cache lines, access locality, prefetcher 및 일관성 프로토콜 MESI/RVWMO를 설명합니다. 데스크톱 및 임베디드 RISC-V 시스템을 위한 예제.

메모리 지연 시간: 1 사이클 L1부터 200 DRAM까지
Advertisement 728x90

# 현대 프로세서의 메모리 계층 구조: 레지스터부터 DRAM까지

현대 시스템에서 캐시 미스는 100–200 사이클, 히트는 1–4 사이클이 소요됩니다. 이는 코드 성능을 좌우합니다. 실제 사례를 보죠: 1 GHz RISC-V에서 네트워크 인터페이스 드라이버를 최적화한 경우. 예상 패킷 처리 속도는 패킷당 500 지시어(500 ns)로 초당 200만 개였습니다. 실제 달성 속도는 초당 20만 개.

프로파일링 결과:

$ perf stat -e cycles,instructions,cache-misses ./driver_test
  Performance counter stats:
    5,000,000 cycles
      500,000 instructions
       45,000 cache-misses

50만 지시어는 IPC=1에서 50만 사이클이 필요하지만, 실제로는 500만 사이클이 소요되었습니다. 차이는 4만5천 미스 × 100 사이클 = 450만 사이클입니다. 계산에 10%, 메모리 대기에 90%가 걸렸습니다.

Google AdInline article slot

메모리 계층 구조

메모리는 속도가 점차 느려지고 크기가 커지는 계층 구조로 조직되어 있습니다:

| 계층 | 유형 | 지연 시간 | 크기 |

|---------|-----|----------|--------|

Google AdInline article slot

| 레지스터 | 32 레지스터 | 1 사이클 | ~128 B |

| L1 캐시 | 지시어/데이터 | 3–4 사이클 | 32–64 KB |

| L2 캐시 | 통합 | 12–15 사이클 | 256–512 KB |

Google AdInline article slot

| L3 캐시 | 공유 | 40–50 사이클 | 2–32 MB |

| DRAM | 메인 | 100–200 사이클 | GB–TB |

주요 특성:

  • 속도가 1에서 200 사이클로 급감.
  • 크기가 128 B에서 TB로 폭증.
  • DRAM은 L1보다 100–200배 느림.

마이크로컨트롤러(RISC-V RV32IMC, 100 MHz)에서는 계층이 단순화됩니다:

| 계층 | 유형 | 지연 시간 | 크기 |

|---------|-----|----------|--------|

| 레지스터 | 32 레지스터 | 1 사이클 | 128 B |

| I-캐시 L1 | 지시어 | 1 사이클 | 16 KB |

| D-캐시 L1/SRAM | 데이터 | 1–2 사이클 | 8–32 KB |

| 플래시 | 코드 | ~10 사이클 | 128 KB–1 MB |

| DRAM | 데이터 | 50–100 사이클 | 8–64 MB |

차이점: 캐시가 작고 L2/L3 없음, 코드에 DRAM 대신 플래시 사용, 크기 제한적.

캐시 라인과 데이터 액세스

캐시는 64바이트 블록 단위로 작동합니다. 1바이트를 액세스해도 전체 라인을 로드합니다.

int 액세스 예제:

int x = array[0];  // 4 bytes at 0x1000
// Loads 64 bytes: 0x1000–0x103F (16 ints)

순차 액세스는 효율적:

for (int i = 0; i < 16; i++) {
    sum += array[i];  // 1 miss, 15 hits
}

랜덤 액세스는 비효율적:

for (int i = 0; i < 16; i++) {
    sum += array[random_index[i]];  // Many misses
}

캐시 조직: 세트와 웨이

캐시는 세트와 웨이로 나뉩니다. 직접 매핑:

주소 비트: [Tag | Index | Offset]

32 KB, 64 B/라인 기준: 512 라인, 9비트 인덱스, 6비트 오프셋, 17비트 태그(32비트 주소).

충돌: 배열 a[1024](0x10000)과 b[1024](0x18000)이 동일 세트에 매핑.

세트 결합식(N-웨이): 세트에 N개 라인 포함, 충돌 감소.

일반적인 매개변수:

  • L1: 8-웨이, 32–64 KB.
  • L2: 8–16-웨이, 256–512 KB.
  • L3: 16-웨이, 2–32 MB.

임베디드: 직접 또는 2-웨이, 충돌 확률 높음.

액세스 지역성

공간적: 인접 주소.

좋음:

for (int i = 0; i < n; i++) sum += array[i];

나쁨:

for (int i = 0; i < n; i++) sum += array[random[i]];

시간적: 반복 액세스.

좋음:

int temp = array[0];
for (int i = 0; i < 1000; i++) result += temp * i;

나쁨:

for (int i = 0; i < 1000; i++) result += array[i % 10] * i;

최적 코드(행렬 곱셈):

for (int i = 0; i < N; i++) {
    for (int k = 0; k < N; k++) {
        int r = A[i][k];
        for (int j = 0; j < N; j++) {
            C[i][j] += r * B[k][j];
        }
    }
}

프리페처와 그 제한

프리페처는 패턴에 따라 데이터를 미리 로드합니다.

  • 순차: for(i) sum += array[i];
  • 스트라이드: sum += array[i*2];

제한:

  • 랜덤 액세스에서는 작동 안 함.
  • 10–20 라인으로 제한.
  • 배열 인터리빙에 속음.

마이크로컨트롤러에서는 단순하거나 없음.

대역폭과 멀티코어

DDR4-3200: 채널당 25.6 GB/s, 듀얼 채널 — 51.2 GB/s. L1: ~1000 GB/s.

임베디드: SRAM 1–4 GB/s, DRAM 100–500 MB/s. 작업 세트를 SRAM에 유지하세요.

MESI 프로토콜(x86/ARM):

  • M: 수정됨.
  • E: 배타적.
  • S: 공유.
  • I: 무효.

거짓 공유:

struct { int c0, c1; } shared;  // Ping-pong

해결:

struct { int c0; char pad[60]; int c1; } shared;

RISC-V: 약한 메모리 모델, fence 사용:

sw a0, 0(a1)
fence w, w
sw a2, 0(a3)

원자성 연산: lr.w/sc.w.

중요한 점

  • 캐시 미스가 지배적: 90% 시간이 대기.
  • 64 B 라인: 순차 액세스로 15 히트 무료 획득.
  • 지역성(공간/시간) — 최적화의 핵심.
  • 임베디드 시스템: 단순화된 계층, 프리페처 없음.
  • MESI와 패딩으로 멀티코어 핑퐁 방지.

— Editorial Team

Advertisement 728x90

다음 읽기