메모리와 캐시

 

메모리(RAM)은 느리다.

통상적으로 CPU가 덧셈을 1사이클만에 끝내는데, 메모리에서 CPU가지 데이터를 가져오는데 걸리는 시간은 42사이클 이상이 걸린다.

 

그래서 이를 보완하기 위해 캐시를 사용한다.

캐시는 CPU 칩 안에 있는 작은 사이즈의 메모리인데 RAM에 비해 기능도 빠른데다가 물리적으로도 CPU와 가까워서 매우 빠르다.

또한 코어마다 각각의 캐시를 가지고 있다.

하지만 캐시는 성능이 빠른만큼 크기가 작다.

내 노트북 기준으로 캐시의 크기는 아래와 같다.

즉 가장 빠른 L1캐시는 크기는 가장 작지만 읽기/쓰기가 4사이클정도면 되고, L2캐시 L3캐시는 이보다 점점 느려진다.

 

CPU의 경우 필요한 데이터가 있다면 먼저 캐시에 해당 데이터가 있는지 확인한다.

만약 캐시에 데이터가 있다면 이를 Cache hit라고 부르며 캐시에서 바로 가져온다.

반면 캐시에 데이터가 없다면 이를 Cache miss라고 부르며 이땐 RAM까지 가서 데이터를 가져와야 한다.

 

RAM에서 가져온 데이터를 캐시에 보관하고 사용한다.

근데 만약 이미 캐시가 가득 차있다면 캐시에서 내용을 제거하고 가져온 데이터를 넣어야 한다.

이때 사용되는게 페이징 교체 알고리즘인데 유명한게 LRU(Least Recently Used)로 가장 사용한지 오래된 캐시를 제거하고 새로운 데이터를 기록하는 방식이다.

 

이때문에 재밌는 현상이 발생한다.

for (int i = 0; i < 10000; i++) {
    for (int j = 0; j < 10000; j++) {
        s += data[j];
    }
}
for (int j = 0; j < 10000; j++) {
    for (int i = 0; i < 10000; i++) {
    s += data[j];
    }
}

아래쪽의 코드는 data[j]라는 같은 값에 10000번 연속으로 접근하므로 계속해서 cache hit이 발생한다.

반면 위쪽의 코드는 data[j]가 접근할때마다 다른 값이므로 계속해서 cache miss가 발생한다.

 

그러므로 위의 2개의 코드 중 아래쪽의 코드가 속도가 더 빠르게 된다.