본문 바로가기
책/DB

서버 구조의 이해 메모리

by 파이어볼러 2015. 3. 3.



내 컴퓨터의 메모리 계층 구조를 나타낸 사진이다


2. SISD vs. SIMD


우선 컴퓨터의 작업 형태는 다음의 두 가지로 분류될 수 있습니다.

- SISD (Single-Instruction, Single-Data)

- SIMD (Single-Instruction, Multiple-Data)


전자는 하나의 명령어가 하나의 데이터 스트림을 처리하는 것이고 후자는 하나의 명령어가 여러 데이터 스트림을 병렬적으로 처리합니다. 수학적으로 전자는 스칼라 연산, 후자는 벡터 연산이라고도 하는데 이해를 돕기 위해 간단한 예를 들어 보겠습니다.

- SISD의 예: A + B <- 하나의 명령어 (덧셈) 가 데이터 스트림을 한개씩 상대함

- SIMD의 예: {A, B, C} + {D, E, F} <- 하나의 명령어가 여러 데이터 스트림에 대해 동시에 적용됨


 



▲ SISD와 SIMD의 예

그림 출처: Ars Technica, [SIMD architectures], Jon Stokes, 3/21/2000


보통 하나의 연산을 여러 데이터에 적용하는 것은 같은 성질을 가진 데이터의 집합체에서, 그리고 특정 작업을 반복적으로 시행해야 할 때 큰 성능 향상을 이끌어낼 수 있는데 이러한 성질은 대개 멀티미디어 어플리케이션에서 잘 관찰됩니다. (예를 들어 이미지 편집 프로그램에서 이미지에 필터를 적용할 경우, '필터링'에 해당하는 특정 명령어 스트림이 이미지 데이터 전체에 대해 매 픽셀마다 같은 연산을 반복적으로 수행하게 됨. 스트리밍, 렌더링, 인코딩 등의 작업도 마찬가지)


반면에 동질한 성질의 데이터를 다루지 않는 경우나 여러 종류의 작업이 불규칙하게 수행되는 경우엔 SIMD 방식을 적용할 수 없는데 이러한 패턴을 보이는 것은 주로 오피스 프로그램입니다. (워드프로세서, 데이터베이스 관리, 스프레드시트 등) 사실, SISD와 비교했을 때 SIMD 방식의 약점은 범용성이 떨어진다는 것인데 따라서 오늘날의 CPU는 SISD 구조를 바탕으로 별도의 SIMD 처리 유닛을 내장하는 식의 확장을 꾀하고 있습니다.


또한 흔히 SISD를 정수 연산과, SIMD를 부동소수점 연산과 동일시하는 시각이 있는데, 이론적으로는 부동소수점 데이터를 SISD 방식으로 처리해야 할 경우도 있고 정수 데이터에 대해 SIMD를 적용할 수도 있으니 엄밀히 말해서 맞는 말은 아닙니다. 하지만 정수 데이터가 많이 쓰이는 오피스 어플리케이션, 부동소수점 데이터가 많이 쓰이는 멀티미디어 어플리케이션의 특성상 위에서 살펴본 분류를 따르자면 결국 오늘날의 컴퓨팅 환경에서는 SISD = 정수, SIMD = 부동소수점으로 수렴하는 경향이 있단 것도 틀린 말은 아닙니다.


따라서 이 글의 나머지 부분에서는 필요에 따라 SISD/정수 연산, SIMD/부동소수점 연산이란 용어를 같은 것으로 간주해 혼용해 사용하도록 하겠습니다.



3. Locality of Reference(분산 데이터 베이스를 설계함에 있어서 하나의 트랜잭션을 처리하기 위하여 참조해야 할 일련의 데이터가 가급적 한 곳에 모여 있어야 한다는 것)



대개의 경우 캐시메모리는 메인 메모리에 비해 매우 적은 용량을 갖고 있습니다. 그럼에도 불구하고 적은 용량의 캐시메모리가 CPU와 메모리 사이에서 원활히 가교 역할을 할 수 있는 것은 명령어/데이터 스트림의 '참조 집약성'이라는 성질 때문입니다. 다시 말해 이 성질을 추적함으로써, 캐시메모리는 단지 넓은 메모리 용량 중 일부분을 임의로 저장해 두는 것보다 더 효율적인 방식으로 '필요할 것으로 예측되는' 스트림을 콕 집어 저장해 둘 수 있는 것이죠. 이번 장에서는 이 참조 집약성이란 성질에 대해 알아 보겠습니다.


참조 집약성에는 아래의 두 가지가 있습니다.

- 공간 집약성 (Spatial Locality)

- 시간 집약성 (Temporal Locality)



 


▲ 공간 집약성의 예

그림 출처: Ars Technica, [Understanding CPU caching and performance], Jon Stokes, 7/7/2002


공간 집약성은 CPU가 메모리상의 어느 한 위치에서 명령어/데이터를 참조했다면 머지 않아 그 근처의 연속된 주소에서 다음 명령어/데이터를 찾는다는 것이고, 시간 집약성은 CPU가 특정 명령어/데이터를 과거에 사용했다면 머지 않은 시간 내에 그 명령어/데이터를 다시 사용한다는 것입니다. 이 참조 집약성은 프로그램에 따라, 그리고 한 프로그램 내에서도 명령어와 데이터에 따라서 달라질 수 있는데, 그 예를 보도록 하죠.


(1) 첫번째로 워드프로세서 작업을 상상해 봅시다. 문서 파일을 열어 특정 단어를 다른 단어로 치환한 후 글꼴을 변경할 때, 단어를 검색하는 명령어 / 치환하는 명령어 / 글꼴 변경 명령어가 메모리상에서 연속적인 위치에 있을 가능성은 크지 않습니다. 즉 공간 집약성이 작다는 것이죠. 하지만 그 작업들의 대상이 되는 '문서 파일' 데이터는 메모리상의 연속적인 공간에 집약되어 있으므로 공간 집약성이 매우 큽니다.


(2) 또다른 예로 음악 재생 프로그램으로 여러 개의 음악파일을 재생할 때를 상상해 봅시다. '음악 재생' 명령어는 전체 음악파일의 재생시간 내내 반복적으로 사용되지만 (즉 시간 집약성이 높지만), 음악파일 데이터에서 한번 '사용'된 부분이 머잖아 다시 반복 재생될 가능성은 크지 않습니다. 다시 말해 이 경우엔 데이터의 시간 집약성은 매우 작다고 봐야 합니다 (특정 구간만 반복해서 듣지 않는 한). 대신 하나의 파일은 메모리상에 연속적으로 저장되어 있으니 데이터의 공간 집약성은 큰 편이죠.


따라서 명령어와 데이터를 별도의 캐시메모리에 나누어 담아 각각 다른 참조 집약성 관리정책을 쓰는 것이 성능상 유리하고, 이렇게 명령어-캐시와 데이터-캐시를 따로 두는 구조를 '하버드 아키텍처'라고 합니다. 오늘날의 CPU에서 L1캐시는 명령어/데이터를 나눠 담는 것이 일반적입니다. (L1-D 캐시, L1-I 캐시)





특정 명령어/데이터 스트림이 시간 집약성이 뛰어나다면 과거에 사용했던 스트림을 항상 저장해 두는 것이 유리하므로 캐시의 용량이 클수록 좋습니다. (반면 캐시의 속도는 그리 중요하지 않게 됩니다) 이와 달리 특정 스트림의 공간 집약성이 뛰어날 때에는, 과거에 사용했던 스트림을 하염없이 저장해 두는 것보다는 메모리상의 연속적인 위치에서 스트림을 '연속적으로' 끊임없이 읽어들이는 것이 중요하므로 캐시의 속도가 빠른 것이 중요합니다. 이 경우엔 과거에 사용했던 스트림이 다시 사용될 가능성이 낮으니 한번 사용된 스트림을 바로 비우더라도 성능에 악영향을 미치지는 않습니다. 따라서 캐시의 용량이 일정 수준 이상 커질 필요는 없죠.


(1) 게임을 예로 들자면 하나의 명령어 스트림이 다량의 데이터를 반복 처리하는 SIMD 작업이 대부분인데 (예: 렌더링, 필터링, 안티알리아싱), 하나의 맵 (= 화면) 을 플레이할 때 해당 화면을 구성하는 데이터는 메모리상의 인접한 경로에 저장되어 있을 가능성이 크므로 공간 집약성이 크다고 볼 수 있습니다. 따라서 게임의 흐름에 따라 다음 화면을 구성할 데이터의 위치를 예상해 미리 캐시에 담아 두는 것이 바람직하죠. 따라서 캐시의 속도가 빠를수록 좋습니다. 반면 과거에 플레이했던 구간의 화면이 언젠가 다시 사용되리란 기대를 가지고 이를 모두 저장하기 위해 큰 용량의 캐시를 사용하는 것은 효율적인 캐시 관리 정책이 아닙니다.


(2) 반면 오피스 프로그램은 앞서 말했듯 SISD 작업이 주가 되는데, 이 경우엔 전체 스트림에서 데이터가 차지하는 비중은 SIMD 작업보다 크게 적은 편이고 (∵ SISD는 명령어:데이터가 1:1, SIMD는 명령어:데이터가 1:多) 다시 말해 명령어 스트림의 참조 집약성에 의거해 캐시를 관리하는 것이 현명합니다. (물론 SIMD 작업에서도 명령어는 공간 집약성을 따르지 않을 수 있지만, 명령어:데이터 비율이 1:多인 SIMD 작업의 특성상 데이터의 비중이 압도적으로 크기 때문에 데이터의 참조 집약성만을 고려해도 성능이 크게 떨어지지는 않습니다)


스프레드시트나 데이터베이스 관리 프로그램의 경우, 과거에 수행한 몇 가지의 작업을 계속해서 수행하는 빈도가 높기 때문에 시간 집약성이 큰 편이고, 따라서 한번 사용했던 명령어 스트림을 저장해 두었다가 언젠가 다시 사용될 경우를 대비하는 것이 합리적입니다. 다시 말해 저용량/고속의 캐시보다는 속도가 조금 느리더라도 용량이 큰 캐시가 성능상 유리합니다.

4. Latency vs. Bandwidth of Hierarchies

기본적으로, 캐시메모리의 존재 이유는 CPU와 메모리의 속도 차이를 극복하여 CPU에게 '끊김 없이' 스트림을 전달해주기 위해서입니다. ('끊김 없이'가 중요한 이유는 오늘날의 CPU는 상당히 깊은 파이프라인 구조를 사용해, 파이프라인 버블이 성능에 미치는 악영향이 크기 때문입니다)

이론상 고속 & 고용량의 캐시메모리를 장착하면 성능상 가장 좋은 결과를 얻겠지만 비용 문제를 고려했을 때 이는 현명한 해결책이 못 됩니다. 따라서 대부분의 CPU는 초고속/미세용량 - 고속/저용량 - 저속/고용량으로 이어지는 '메모리 계층 구조'를 채택해 각 단계별 속도 차를 세분화함으로써 CPU가 최대한 끊김 없이 스트림을 공급받을 수 있도록 대책을 강구했습니다.

대개 오늘날의 CPU는 CPU - L1 캐시 (명령어/데이터) - L2 캐시 - (L3 캐시) - 메모리로 이어지는 메모리 계층 구조를 갖고 있는데, 첫 장에서 보았던 스크린샷을 다시 한번 보도록 합시다.




▲ 에버레스트 캐시 & 메모리 벤치마크 결과를 보면 각 계층별로 대역폭과 레이턴시가 나와 있습니다.
아시겠지만 L1 캐시의 레이턴시가 가장 짧고 대역폭도 가장 높습니다 (= 속도가 가장 빠릅니다).
그리고 그 뒤를 이어 L2 캐시, L3 캐시, 메모리의 순으로 속도가 떨어지는 것을 알 수 있죠.

가능한 한 속도가 빠른 L1 캐시가 높은 적중률을 갖춰 CPU에게 스트림을 공급해 주는 것이 성능상 가장 유리하기 때문에 용량 대비 L1 캐시의 적중률은 다른 계층보다 훨씬 높게끔 설계됩니다. 하지만 아무리 좋은 정책에 의거해 캐시를 관리하더라도 캐시 미스가 발생하는 경우가 생기는데, L1 캐시에서 캐시 미스가 발생한 경우 CPU는 L2 캐시에게 데이터를 요청하고 L2 캐시가 데이터를 찾게 됩니다. (L2 캐시에서도 미스 발생시 L3 캐시로 넘어가고, 그 다음 순위는 메모리입니다)

이 때 각 계층이 CPU에 응답하는 시간이 레이턴시, 필요한 스트림을 CPU에 전송해 주는 통로의 폭이 대역폭인데 CPU로 넘겨줄 스트림의 용량에 따라 레이턴시와 대역폭이 각각 성능에 미치는 영향력이 달라지게 됩니다. 간단한 예를 들어 보겠습니다.

모든 캐시에서 미스가 발생해 CPU가 메모리에 직접 스트림을 요청했다고 가정해 봅시다.

 



▲ 위의 스크린샷을 기준으로, 레이턴시 31.9ns / 읽기 대역폭 12614MB/s을 이용해 계산한 자료입니다.

보시다시피 스트림의 용량이 작을 경우엔 레이턴시가 성능에 큰 영향을 미치지만, 용량이 커질수록 레이턴시가 전체 소요시간에 미치는 영향은 점점 작아지는 것을 알 수 있습니다. 다시 말해, 작은 파일을 빈번하게 읽고 쓰는 경우에는 레이턴시가 성능에 더 큰 영향을 미치지만 큰 파일을 가끔 읽고 쓰는 경우라면 레이턴시보다도 대역폭이 성능에 더 중요하다는 뜻이 됩니다.

그렇다면, 레이턴시가 느리지만 대역폭이 더 높은 경우에 성능이 어떻게 변할지도 예상이 가능하겠죠?
레이턴시 45ns / 읽기 대역폭 16000MB/s인 경우를 가정해 보겠습니다.


 

▲ 100MB 스트림을 읽는 경우엔 레이턴시가 빠른 쪽이 성능이 더 좋지만, 500MB 스트림에서 성능차가 좁혀지더니 1GB 스트림에서는 대역폭이 높은 쪽이 역전했습니다. 즉 주고받은 스트림의 용량이 클수록 높은 대역폭이 더 중요해진다는 것을 보여주고 있죠.


5. Hierarchy and Performance

한편, 같은 계층의 레이턴시/대역폭뿐만 아니라 계층간의 레이턴시/대역폭 차이도 성능에 영향을 주게 됩니다.
예를 들어 L3 캐시가 없는 Athlon II 시리즈의 경우, L2 캐시에서 미스가 발생하면 공은 바로 메모리에게 넘어가게 되는데 이 경우 메모리 전 단계에 L3 캐시라는 완충장치가 한 단계 더 있는 Phenom 시리즈에 비해 필연적으로 성능저하가 생기게 됩니다. 이를 앞의 스크린샷을 바탕으로 유추해 보겠습니다.
(편의상 Athlon II의 메모리 레이턴시/대역폭은 위 스크린샷과 같다고 보겠습니다)

 



▲ 아래 그래프는 위의 그래프를 토대로 L3 캐시가 있을 때와 비교해 L3 캐시가 없을 때의 상대성능을 나타낸 것입니다. 작은 스트림을 읽고 쓰는 일상적인 작업에서 L3 캐시가 없는 Athlon II 시리즈는 성능 저하가 발생할 가능성이 크다는 것을 그래프가 보여주고 있습니다. 오히려 스트림의 용량이 늘어나면 캐시 미스로 인한 성능차가 줄어드는 편인데, 이때는 캐시의 적중률이 문제가 되겠죠.
(※ 위 그래프는 L1 캐시 미스 후 L2 캐시까지 미스가 발생했을 때에 한정된 결과입니다. 실제로는 L2 캐시까지에서의 적중률도 상당히 높은 편이라 저렇게까지 성능이 벌어지지는 않습니다)

그렇다면, L3 캐시가 있는 경우 L2 캐시가 적중한 때와 L2 캐시 미스가 발생한 때의 성능 차를 계산해 봅시다.
스크린샷에 따르면 L3 캐시의 레이턴시/대역폭은 5.5ns/12569MB/s, L2 캐시는 2.3ns/31709MB/s 입니다.

 




 

▲ L2 캐시 미스가 발생한 경우엔 L2 캐시가 적중한 때보다 거의 절반 가까이 떨어진 성능을 내게 됩니다.
즉 L3 캐시의 유무보다도 L2 캐시의 적중률을 끌어올리는 것이 성능에 더 유리하다는 뜻이죠.

즉 4장과 5장의 내용을 종합하면 아래와 같습니다.
- 작은 스트림을 다루는 작업의 경우 레이턴시가 성능에 중요하다.
- 큰 스트림을 다루는 작업의 경우 대역폭이 성능에 중요하다.
- 아래 계층의 메모리보다는 위 계층의 메모리가 성능에 더 중요하다.


6. Inclusive vs. Exclusive Among Hierarchies

인텔과 AMD는 자사의 CPU를 설계함에 있어 서로 다른 캐시 구조를 채택하고 있는데, 그 중 주요한 차이점은 인텔은 하위 계층 캐시가 상위 계층 캐시의 내용을 반드시 포함하도록 하는(inclusive)구조인 데 비해 AMD는 상/하위 계층 캐시의 내용이 서로 배타적인(exclusive)구조란 점입니다. 이는 단순히 우연에 의해 상위 레벨 캐시의 내용물이 하위 레벨 캐시에 존재하고 없고의 차이가 발생한 것이 아니라, 매우 의도적으로 그렇게 설계된 것인데 이 장에서는 이 두 설계 방식의 특징과 장단점에 대해 알아보도록 하겠습니다.


<Inclusive 방식>

Inclusive 방식은 앞에서 설명했듯 상위 계층의 내용물이 하위 계층에 포함되어야 하는데, 이 조건이 '상위 계층의 내용이 전부 하위 계층에 포함되어야 하는지' 혹은 '상위 계층의 내용 중 일부라도 하위 계층에 포함되어야 하는지'에 따라 다시 Stricly-Inclusive 방식과 Mainly-Inclusive 방식으로 나뉩니다. 어느 쪽이든 Inclusive 방식을 취할 때 장점이 되는 것은, CPU 이외의 장치(또는 멀티프로세서 시스템에서 다른 CPU)가 캐시에 담긴 자료를 참조하고 싶을 때 모든 계층의 캐시를 둘러볼 필요 없이 가장 낮은 계층의 캐시만 둘러보면 된다는 점입니다. 그 외에도 Inclusive 방식의 주된 장점이 하나 더 있는데, 이것은 뒤에서 Exclusive 방식의 단점을 설명할 때 함께 설명하도록 하죠.

Inclusive 방식의 단점은 각 계층 캐시 용량의 총 합보다 적은 양의 자료만을 담을 수 있다는 것입니다. 정확히 말해 Inclusive 방식에서는 '가장 낮은 계층의 캐시의 용량'만큼의 자료밖에 담을 수 없게 되는데, 상위 계층의 모든 캐시에 담긴 자료가 최하위 계층의 캐시에 반드시 존재해야 한다는 것을 생각하면 당연한 결과입니다. 즉 256KB L1 캐시와 4MB L2 캐시를 장착한 인텔 Core 2 Quad Q8000 시리즈의 경우, 총 캐시 용량은 4.25MB이지만 실제로 담을 수 있는 자료의 양은 4MB를 초과할 수 없게 됩니다. (L2 캐시에 담긴 4MB의 자료 중에서 L1 캐시에 256KB를 선별적으로 저장해야 함)


<Exclusive 방식>

Exclusive 방식은 상위 계층과 하위 계층 캐시가 저장한 내용이 상호 배타적이어야 합니다. (상호 독립적인 것과는 다른 개념입니다. 예컨대 L2 캐시에 특정 데이터가 있든 없든 L1 캐시에 그 데이터를 담을 수 있다면 이는 상호 독립적인 것이지만, L1/L2 캐시에 데이터를 담을 지 여부를 판단할 때 L2/L1 캐시에 해당 데이터가 있는지 여부를 조사해 없을 경우에만 담는다면 이는 독립적이지 않은 것이죠) 이 방식의 가장 큰 장점은 각 계층 캐시가 하나의 거대한 캐시처럼 기능함으로써 캐시의 총 용량을 효율적으로 활용할 수 있게 된다는 점입니다.

예를 들어 인텔 펜티엄 III 카퍼마인 CPU는 32KB L1 캐시와 256KB L2 캐시를 탑재했지만 실질적으로 256KB의 자료밖에 저장해둘 수 없는 반면, 경쟁자였던 AMD 애슬론 썬더버드 CPU는 128KB L1 캐시와 256KB L2 캐시를 탑재해 L2 캐시의 용량이 같음에도 경쟁 상대보다 1.5배 더 많은 384KB의 자료를 저장할 수 있었습니다. (반면 이 특징은 양날의 검처럼 작용해, L2 캐시가 L1 캐시에 비해 매우 큰 경우에는 Inclusive 방식에 비해 그리 효율이 앞선다고 볼 수 없게 됩니다. 예컨대 인텔 Core 2 Quad Q9050 시리즈는 256KB L1 캐시와 12MB L2 캐시를 내장했는데, Inclusive 방식을 채택함으로써 못 쓰게 된 256KB의 용량은 전체 캐시 용량에 비하면 매우 미미한 수준일 뿐입니다)

Exclusive 방식의 또다른 장점은 L2 캐시의 감소가 성능에 큰 영향을 미치지 않는다는 것인데, 이는 성능상의 잇점이라기보다는 CPU 제조단가를 절감하는 데 도움이 되어 (= 성능을 크게 희생하지 않으면서 L2 캐시가 차지하는 면적을 과감히 줄일 수 있기 때문에) 제조사 차원에서 하나의 CPU 아키텍처에 기반한 여러 하위 모델을 손쉽게 파생시킬 수 있게 해 줍니다. 그 예로 AMD 애슬론의 하위 모델인 듀론은 L2 캐시를 256KB에서 64KB로 무려 1/4이나 줄인 모델이지만, L1 캐시는 애슬론과 같이 128KB를 탑재해 L2 캐시 절감에 따른 성능저하를 최소화하면서 다이사이즈를 성공적으로 줄일 수 있었습니다.

반면 Exclusive 방식의 단점은 그 특성상 모든 계층의 캐시가 거대한 단일 캐시처럼 작동하기 때문에, 모든 계층의 캐시에 걸쳐 같은 크기의 자료 단위를 사용해야 한다는 점입니다. 사실 캐시의 최소 단위는 CPU가 어떤 자료를 찾기 위해 캐시를 '둘러 보는' 시간에 직결되기 때문에 성능에 큰 영향을 미치는데, 일반적으로 캐시의 용량이 커지면 최소 단위도 적정 수준까지 커지는 것이 검색 시간 단축에 유리합니다. 하지만 Exclusive 방식에서는 용량이 상대적으로 큰 L2 / L3 캐시도 용량이 적은 L1 캐시와 균등하게 같은 크기의 최소 단위를 가져야 하므로 검색 시간에서 Inclusive 방식에 비해 불이익이 발생할 가능성이 높습니다. (이것을 뒤집으면 Inclusive 방식의 장점이 되는데, 각 계층 캐시의 용량에 따라 개별적으로 적당한 크기의 최소 단위를 설정할 수 있어 각 계층 캐시마다 최적화된 검색 시간을 가질 수 있게 됩니다)


보통 인텔 CPU는 L1 캐시에 비해 L2 캐시가 매우 큰 편이고, AMD CPU는 반대로 하위 계층 캐시에 비해 상위 계층 캐시의 용량이 큰 편인데 이러한 차이는 우연의 산물이 아닌 각 제조사의 캐시 구조 차이에 따른 고도로 계산적인 결과물이란 것이죠.


' > DB' 카테고리의 다른 글

OS의 종류 서버 장비에 대해 이해  (0) 2015.03.03
OSI 7Layer의 계층  (0) 2015.03.03
Directory의 구조와 용도에 대한 이해  (0) 2015.03.03
서버 구조의 이해 NIC  (0) 2015.03.03
서버 구조의 이해 CPU  (0) 2015.03.03