새소식

반응형
CS 지식/컴퓨터구조

[컴퓨터구조] 16-3. Cache Coherence

2023.10.10
  • -
반응형
  • 캐시에는 어떤 데이터를 가지는가? 
    • Recently used data (temporal locality)
    • Nearby data (spatial locality)
  • 데이터를 어떻게 찾는가? 
    • Set은 데이터의 주소에 의해 결정된다.
    • block내 word도 주소에 의해 결정된다.
    • associative cache에서 데이터는 여러 way 중 하나로 저장될 수 있다.
  • 데이터는 어떻게 교체되는가? 
    • Least-recently used(LRU) way in the set

 

  • Compulsory는 처음에 일어났다가 그 이후는 거의 안생긴다.
  • conflict도 cache size에 따라 줄어든다.

 

  • 더 큰 block은 compulsory miss를 감소시킨다.
  • 더 큰 block은 conflict miss를 증가시킨다.
  • block을 무작정 키우면 set의 갯수가 줄어들 것이다.
    • 그래서 결국 conflict miss가 늘어날 수 밖에 없음.

 

 

각각의 miss rate을 줄이기 위한 design change

  • capacity miss
    • cache size의 증가
    • 단점: access time가 증가될 수 있다
  • conflict miss
    • associativity 증가
    • 단점: access time가 증가될 수 있다.
  • compulsory miss
    • block size의 증가
    • 단점: miss penalty가 증가한다. 매우 큰 block size의 경우, pollution 때문에 miss rate이 증가할 수 있다.

 

  • 여러 개의 cache를 두는데 CPU와 가까운 cache는 최대한 빨라야 함.
  • 그치만 miss rate도 줄여야 하기 때문에 L2 cache를 두어서 memory까지 안 가도록 함.
  • 더 큰 caches는 더 낮은 miss rate과 더 긴 access time을 갖는다.
  • 메모리 계층을 multiple level of caches로 확장한다.
  • Level 1 (Primary): CPU에 붙어있는 캐시
    • small and fast (e.g. 16 KB, 1 cycle)
    • CPU와 가까이 있기 때문에 빨라야 함
  • Level 2: L1 cache로부터 misses를 service한다
    • 더 크고 더 느리지만, 여전히 main memory보다는 빠르다.(e.g. 256 KB, 2-6 cycles)
    • memory까지 안가기 위해서 용량이 어느 정도 커야함
  • 대부분 현대 PC는 L1, L2, and L3 cache를 갖는다.
  • (see Google.com for example CPUs)

 

  • 다음과 같이 주어졌을 때,
    • CPU base CPI = 1, clock rate = 4GHz
    • Miss rate/instruction = 2%
    • Main memory access time = 100ns
  • With just primary cache
    • Miss penalty = 100ns/0.25ns = 400 cycles
    • Effective CPI = 1 + 0.02 × 400 = 9
      • hit이면 CPI=1, miss이면 miss rate에 해당하는 CPI( 0.02 × 400)
    • 9 clock에 하나씩 instruction이 들어오는 것이므로 굉장히 성능이 안 좋다.
  • Now add L-2 cache
    • Access time = 5ns
    • Global miss rate to main memory = 0.5%
    • Primary miss with L-2 hit: Miss Penalty = 5ns/0.25ns = 20 cycles
    • Primary miss with L-2 miss: Extra penalty = 400 cycles
    • CPI = 1 + 0.02 × 20 + 0.005 × 400 = 3.4
  • Performance ratio = 9/3.4 = 2.6

 

 

  • Primary cache
    • 최소의 hit time에 집중한다.
  • L-2 cache
    • main memory 접근을 피하기 위해 낮은 miss rate에 집중한다.
    • hit time은 더 낮은 전체 impact을 갖는다.
  • Results
    • L-1 cache는 대개 single cache보다 더 작다
    • L-1 block 크기는 L-2 block 크기보다 작다.

 

Memory Controller <-> DRAM

 

  • array가 cache에 딱 들어맞지 않는다고 가정하면 loop의 중첩을 교환하는 것이 spatial locality를 향상시킬 수 있다.
  • Data stored in row-major ordering (in C-language):
/* before */ for(j = 0; j < 100; j++) for(i = 0; i < 5000; i++) x[i][j] = 2 * x[i][j];
/* after */ for(i = 0; i < 5000; i++) for(j = 0; j < 100; j++) x[i][j] = 2 * x[i][j];

 

after와 같이 되어있어야 데이터가 저장되어 있는 순서대로 access하기 때문에 cache를 잘 사용하는 것이다.

 

before 방식처럼 사용하게 되면 access를 세로로 하기 때문에 데이터 access를 계속 jump를 해 가면서 하기 때문에 locality를 완전히 무시하게 된다.

  • 목표: 데이터가 교체되기 전에 데이터에 대한 접근을 최대화시키는 것
    • Consider the loops of DGEMM (double precision general matrix multiplication): X = YZ
for ( i = 0; i < n, ++i) for ( j = 0; j < n; ++j) { r = 0; for( k = 0; k < n; k++ ) r += Y[i][k] * Z[k][j]; X[i][j] = r; }
  • n by n이 size가 작으면 상관 없는데 크면 문제가 된다.

두 개의 내부 루프는 모든 N을 Z의 N개 원소로 읽고, 같은 N개 원소를 Y의 행으로 반복해서 읽고, X의 N개 원소 중 한 행을 write한다.

 

  • X, Y and Z arrays

column을 가져와야 하는 j는 부담이 너무 크다.

 

/* after */ for (jj=0, jj < n, jj = jj+B) for (kk=0, kk < n, kk = kk+B) for ( i = 0; i < n, ++i) for ( j = jj; j < min(jj+B, n); ++j) { r = 0; for( k = kk; k < min(kk+B, n); k++ ) r += Y[i][k] * Z[k][j]; X[i][j] = X[i][j] + r; }

그래서 한 번에 쭉 다 계산하는 것이 아니라 적당한 block을 나누면 충분히 cache에 올라올 수 있기 때문에 그것에 대해서 계산을 진행하고 Block pointer를 옮겨가면서 Block 마다 matrix 계산을 쭉 이어 나간다.

 

이제 두 내부 루프는 X와 Z의 전체 길이가 아니라 크기 B의 단계로 계산됩니다. (X는 0으로 초기화된다고 가정합니다.)

 

두번째 퍼즐 그림 잘못됨

 

  • GFLOPS - 초당 몇 기가의 floating point 계산을 할 수 있는지
  • blocked는 새로 block이 올라올 때만 바꾸면 되기 때문에 초당 연산량이 거의 감소하지 않는다.

 

  • Shared memory
  • 두 개의 CPU cores가 물리적인 주소 공간을 공유한다고 가정 
    • Write-through caches

  • 처음에는 memory에 0이 들어있었다.
  • cache A가 이를 read 하면 A cache에는 0이 써질 것이다.
  • cache B가 또 read하면 B에도 0이 써질 것이다.
  • CPU A가 데이터 0을 1로 바꾸면 그 즉시 메모리 X에도 반영되어 write한다.
  • 그러면 cache와 memory에 값들이 전부 다 다른 문제가 발생한다. -> cache coherence

두 CPU의 각 cache(즉, 클라이언트) 에서 접근한다고 했을 때 캐시의 갱신으로 인한 데이터 불일치가 생기는데 예를 들어, 쉽게 말해서 변수 x가 있는데 이 값은 0이다.

그런데 A에서 이 값을 1로 바꾸었고 B에서 x값을 읽는 상황을 가정해 보면, CPU B는 A가 수정한 값 1을 읽는 것이 아니라 현재 자신의 로컬 캐시인 0을 읽을 수 밖에 없다. 따라서 캐시 1, 2는 같은 X라는 변수에 대해 다른 값을 가지게 되므로 데이터 불일치 문제가 발생한다.

 

  • 일관성( coherence )을 보장하기 위해 multiprocessors에서 캐시가 수행하는 연산  
    • local caches로 데이터의 migration
      • shared memory에 대한 bandwidth를 감소시킨다.
    • read-shared 데이터의 복제(replication)
      • Reduces contention for access
      • 접근에 대한 경쟁을 감소시킨다.
  • Snooping protocols (snoop: 돌아다니면서 몰래 살펴보는)
    • 각 cache는 bus reads/writes을 모니터한다.
    • bus를 통해 누가 쓰고 있는지 아닌지를 모니터한다.
  • Directory-based protocols
    • 어느 cache가 사용했는지 전부 기록하는 방식
    • caches와 메모리가 directory에 block의 상태를 공유하는 것을 기록한다.

 

  • Cache는 block을 기록할 때 block에 대한 독점적인 접근 권한을 갖는다  
    • bus에서 유효하지 않은 메시지를 broadcast 한다
    • Subsequent read in another cache misses
    • 또 다른 cache에서 후속 읽기가 miss된다.
      • cache 소유를 통해 업데이트된 가치 제공

A가 memory X에 1을 write하는 경우 다른 CPU는 bus를 사용하지 못하도록 bus activity에 대해서 Invalidate for X를 알려주는 것이다.

그렇기 때문에 이 때 B의 cache에는 데이터가 없을 것이고 그 후에 memory를 read하게 되면 memory 대신 A가 write한 값을 B에게 주게 된다.

Q) 어떻게 A가 B한테 주는 거지? monitor를 통해 x는 invalidate하다고 되었기 때문에 A가 주는 건가?

반응형

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.