본문 바로가기

Lecture & Column/cpu_lec_col

멀티스레딩 기술의 이해

Author : Daeguen Lee

(Any action violating either copyright laws or CCL policy of the original source is strictly prohibited)

 

 

오늘은 현대 CPU의 성능향상 기법 중 하나인 SMT에 대해 간단히 알아 보겠습니다.
SMT는 Simutaneous Multi-threading의 약자로, 동시에 여러 스레드를 처리하는 기법을 통칭합니다.

CPU의 성능을 올릴 때엔 '병렬성'(parallelism)이란 것을 추구하는 경우가 일반적인데
병렬성은 다시 명령어 수준(instruction level) 병렬성과 스레드 수준(thread level) 병렬성으로 나뉩니다.
전자의 경우 프로세서 내부의 각 명령어 처리장치를 늘림으로써 간단히 구현할 수 있고,
후자는 조금 더 복잡한데 보통 프로세서의 갯수 자체를 늘림으로써 구현할 수 있습니다.
그런데 물리적인 프로세서의 갯수를 늘리지 않고 단지 여러 스레드를 동시에 집어 넣기만 함으로써도
하나의 CPU 코어에서 여러 스레드를 병렬로 처리할 수 있기도 한데, 이를 SMT라고 합니다.

SMT 기술 중 가장 잘 알려진 것은 인텔의 하이퍼스레딩입니다.
하이퍼스레딩의 핵심은 CPU에 추가적인 트랜지스터를 투입하지 않고 스레드 수준 병렬성을 확보하는 것인데
즉 CPU가 처리하는 스레드가 늘어남에도 불구하고 추가적인 연산장치를 구현하지 않은 것이 특징입니다.
기존의 CPU 설계에 아주 사소한 수정만을 가해 최대 두 배의 성능향상을 꾀한다는 컨셉은 굉장히 매력적인데,
실제로 이 기술의 효용은 두배는커녕 1.5배에도 미치지 못할 때가 많습니다.


이 포스팅에서는 현행 하이퍼스레딩의 작동 원리와 그 효율이 그리 좋지 않은 이유 & 좋아지기 위한 조건,
그리고 하이퍼스레딩 외에도 현존하는 다른 SMT 기법에 대해 간단히 알아보도록 하겠습니다.

목차는 아래와 같습니다. (바로 보시려면 챕터 이름을 복사한 뒤 Ctrl+F로 검색해 주세요)

Chapter 1: 1 Core / 1 Thread 모델
Chapter 2: 2 Cores / 2 Threads 모델
Chapter 3: 1 Wide Core / 1 Thread 모델
Chapter 4: 1 Core / 2 Threads 모델 (인텔 하이퍼스레딩)
Chapter 5: 1 '2-Core-like' Module / 2 Threads 모델 (AMD 클러스터 멀티스레딩)
Chapter 6: 모델 비교 및 분석


Chapter 1: 1 Core / 1 Thread 모델

우선 보통의 1코어/1스레드 구조를 상상해 봅시다.



▲ 회색 상자가 CPU를 나타냅니다.
이 가상의 CPU는 네 개의 연산 유닛을 갖고 있고 각각 A, B, C, D라 이름붙여져 있습니다.
현실에서는 CPU의 연산 유닛은 굉장히 다양한 종류를 갖는데, 간단히 열거하자면

- 정수 스칼라 유닛 (ALU)
- 부동소수점 스칼라 유닛 (FPU)
- 정수 벡터 유닛 (MMX)
- 부동소수점 벡터 유닛 (SSE, AVX)

등으로 나뉩니다. 첨언하자면, 그림의 A, B, C, D유닛의 기능이 위에 열거한 대로 매치되는 건 아닙니다.
아무튼... 그림을 설명하자면 이 가상의 CPU에 두 개의 서로 다른 스레드가 투되는데
각각의 스레드는 프로세서 내부의 연산 유닛을 서로 다른 비율로 점유하고 있습니다.
일반적인 1코어/1스레드 CPU에서 한 스레드가 완전히 끝나기 전에는 다른 스레드를 처리할 수 없으므로
전체 처리시간은 각 스레드의 처리시간을 산술적으로 더한 값과 같습니다.

스레드 1의 경우, 연산유닛 B에서 가장 많은 시간을 잡아먹혀 총 3 사이클이 걸리고
스레드 2의 경우는 연산유닛 C에서 가장 많은 시간을 할애해 총 4 사이클만에 작업이 완료되었습니다.
즉 이 경우, 이 CPU가 스레드 1과 2를 모두 완료하는 데 걸린 시간은 7 사이클입니다.
총 4개의 유닛이 7 사이클동안 (4 x 7 = 28) 16개의 명령어를 처리했으니 가용률은 16/28 = 57%가 됩니다.

위 그림에서 나타낸 스레드는 비교적 일반적인 상황을 가정한 것인데
만약 두 스레드가 극단적으로 하나의 자원(연산유닛)에서 경합하는 경우라면 어떻게 될까요?


▲ 위 그림은 스레드 1 / 2 모두 연산유닛 B에 극단적으로 매달리고 있는 상황입니다.
이 경우 두 스레드가 처리되는 동안 다른 유닛은 그저 놀고만 있고, B만 엄청나게 구르고 있죠.
이 경우의 가용률은 매우 나빠집니다. (25%)

반대로, 두 스레드의 자원이 서로 배타적이어서 스레드 간의 경합이 없는 경우를 보겠습니다.


▲ 이 때에는 두 스레드가 경합하는 자원은 없지만
어차피 한 스레드가 완료되기 전에는 다음 스레드를 처리할 수 없으니 총 8 사이클이 걸렸습니다.
이 때의 가용률은 50% 되겠습니다.
이러한 경우라면, 한번에 두 스레드를 처리하는 것이 큰 도움이 되겠죠?

일단 지금까지 살펴본 1코어/1스레드 CPU의 경우 일반적인 작업/경합 작업/배타적 작업에 걸린 총 시간은
일반적인 작업 7 사이클 + 경합 작업 16 사이클 + 배타적 작업 8사이클 = 총 31 사이클입니다.
또한 총 가용률은 (16 + 16 + 16) / (4 x 31) = 39%가 됩니다.
이제 각기 다른 CPU 모델을 통해 이 작업시간 & 가용률이 어떻게 변화하는지 살펴봅시다.
앞으로 등장할 모델마다 위에서 살펴봤던, 아래의 세 가지 예제를 기준으로 살펴보도록 하겠습니다.

- 일상적인 작업 (각 연산유닛을 랜덤하게 사용하는 스레드)
- 특정 연산유닛에서 극단적으로 경합하는 스레드
- 사용하는 연산유닛이 서로 겹치지 않는, 배타적인 스레드


Chapter 2: 2 Cores / 2 Threads 모델

가장 간단한 멀티스레딩 기법은 CPU 갯수(코어 갯수) 자체를 늘리는 것입니다.
개별 CPU 코어의 처리방식엔 변화가 없다고 가정하고 단순히 갯수만 늘려 두 스레드를 동시에 처리해 봅시다.
'일상적인 작업'의 예제부터 보시겠습니다.


▲ 두 스레드를 처리하는 데 (처리시간이 오래 걸린 쪽에 맞춰) 총 4 사이클이 걸렸습니다.
이때의 가용률은 16 / (8 연산유닛 x 4사이클) = 50%입니다.
연산시간은 단축되었지만 가용률은 오히려 1코어/1스레드 CPU일 때보다 떨어졌습니다.
즉 자원이 남아돈다는 얘기이고, 자원(연산유닛)의 배분이 그만큼 효율적이지 못하다는 얘기도 됩니다.

이번엔 두 스레드가 극단적으로 같은 자원을 두고 경쟁하는 경우입니다.


▲ 이번에도 역시 가용률은 나아진 게 없고, 작업 시간은 두 스레드를 동시에 처리해 1/2로 단축되었습니다.

이번에는 반대로 두 스레드가 전혀 경합하지 않는 경우입니다.


▲ 이 경우도 역시 가용률은 나아지 않았고, 작업 시간만 1/2로 단축되었습니다.

위에서 살펴본 세 작업에 걸린 총 소요시간은 4 + 8 + 4 = 16 사이클로, 1코어/1스레드 모델의 1/2 수준입니다.
즉 연산유닛을 두 배로 늘린 효율이 거의 산출량에 비례해 나온 셈이니 괜찮은 전략이었다고 볼 수 있지만
가용률을 기준으로 보면 (16 + 16 + 16) / (8 연산유닛 x 16 사이클) = 37.5%로 오히려 떨어졌습니다.
즉 코어 갯수가 두 배 늘었지만 이 늘어난 자원이 더 비효율적으로 쓰이고 있다는 것이죠.
사실 이것은 프로세서의 소비전력 (쓸데없이 작동되는 유닛의 비율) 과 제조단가 (면적 = 연산유닛 갯수) 에 직결되기 때문에 가벼이 넘길 수 없는 문제이기도 합니다.


Chapter 3: 1 Wide Core / 1 Thread 모델

CPU 제조사의 입장에선 성능과 소비전력, 제조단가의 삼중점을 찾기 위해 끊임없이 머리를 굴려 왔는데

성능(작업시간)과 효율(가용률) 사이에서 절충점을 찾기 위해 최근까지 가장 널리 사용된 방법은
"자주 사용되는 유닛을 늘리는 것" 이었습니다.
우리가 가정한 예제에선 연산유닛 B와 C가 자주 쓰이고 있으므로 B / C를 각각 두개씩으로 늘려 보겠습니다.

"일상적인 작업" 예제부터 보시죠.


▲ 작업시간은 4 사이클로 위에서 살펴본 2코어 모델과 동등한 수준이고,
코어 자체를 둘로 늘리는 것보다 자주 쓰이는 연산유닛만 추가한 것이 가용률이 더 좋아졌습니다.
6개의 연산유닛을 4사이클동안 가동해 (6 x 4 = 24) 16개의 명령어를 처리했으니 67%가 됩니다.
그렇다면 특정 연산유닛에서 각 스레드가 경합하는 상황은 어떨까요?


▲ 여전히, 각 스레드별 가용률이 좋은 편은 아닙니다만 어쨌든 33%로 앞의 두 모델보다 늘었습니다.
특히 "일반적인 작업" 예제와 이 예제의 경우 작업 시간은 2코어 모델과 동등한 수준입니다.
(실제로는 코어 자원의 50%만 늘렸을 뿐인데 2코어와 같은 성능을 낸다는 뜻입니다)

마지막으로 서로 배타적인 스레드의 경우를 보시겠습니다.


▲ 사실 1코어가 1스레드를 처리하는 구조 하에서는 '서로 배타적인 스레드'가 주는 잇점이 전혀 없습니다.
여기에서도 단지 각 스레드마다 처리되는 시간의 산술적인 합이 전체 작업 시간이 되고 있습니다.

1개의 "넓은"코어 모델의 총 작업시간은 4 + 8 + 8 = 20 사이클로 앞의 두 모델의 중간 수준입니다.
반면 가용률은 매우 좋아졌는데, 48 / 120 = 40%에 이르렀습니다.
이렇듯 투입한 자원 대비 성능향상 & 가용률 향상폭이 크기 때문에 CPU 제조사들에게 채택되었던 것이죠.
그렇다면, 인텔의 하이퍼스레딩은 어떻게 등장하게 된 것일까요?


Chapter 4: 1 Core / 2 Threads 모델
(인텔 하이퍼스레딩)

위의 세 모델을 살펴보면서 공통적으로 불필요한 가용률 저하가 일어났던 곳은 '배타적인 스레드'였습니다.

각 스레드가 프로세서 상에서 중첩되지 않는 자원만을 사용하고 있음에도 불구하고
한 번에 한 스레드씩만 처리할 수 있어 불필요하게 노는 자원을 만들어냈던 것이죠.
하이퍼스레딩 기술은, 별도의 자원(연산유닛) 추가 없이도 이런 유휴자원을 남김없이 사용함으로써
추가 생산성을 만들어내는 것이 목적입니다.



▲ 일상적인 작업의 예제입니다.
두 스레드가 동시에 프로세서에 들어가 처리되고 있는데, 작업에 소요된 시간은 6사이클로
기존의 1코어/1스레드 모델보다 약 14% 가량 향상된 성능을 보여주고 있습니다.
또한 가용률도 그만큼 증가해 67%가 되었죠.

하지만 두 스레드가 한 연산유닛을 두고 경합하는 경우엔 1코어/1스레드에 비해 성능향상이 전혀 없습니다.


▲ 보시다시피 16사이클이 소요되어, 1코어/1스레드 모델과 똑같은 속도 & 가용률을 보여줍니다.
이러한 예는 특히 게임 등 한정된 연산을 많은 데이터에 대해 단순 반복하는 케이스에서 찾아볼 수 있는데
이 점이 바로 하이퍼스레딩이 게임 성능 향상에 큰 영향을 주지 못하는 원인입니다.

반면에 두 스레드가 완벽히 배타적인 세 번째 경우라면 이야기가 전혀 달라집니다.


▲ !!!!!
프로세서의 모든 자원이 남김없이 사용되고 있습니다. 즉 가용률이 100%가 되었단 뜻이죠.
이렇듯 각 스레드가 프로세서의 자원을 두고 경합하지 않을 때 하이퍼스레딩의 진가가 발휘됩니다.
사실 연산유닛이 확충되지 않았음에도 하이퍼스레딩이 어느 정도 성능향상을 보여주는 것은
일상 생활에서 사용하는 스레드는 모두 서로 배타적이진 않지만, 전적으로 경합하지만도 않기 때문입니다.
인텔의 CPU 설계자들의 의도했던 핵심이 바로 여기 있는 것이죠.

전체적으로, 1코어/2스레드 모델의 총 작업 시간은 6 + 16 + 4 = 26 사이클입니다.
1코어/1스레드 모델(31사이클)보다는 조금 좋고 2코어 모델(16사이클)보다는 많이 뒤떨어집니다.
반면에 가용률은 46%로 크게 향상되어 그동안보다 각 연산유닛이 더 효율적으로 일하게 되었습니다.
이렇듯 매우 적은 추가 자원만으로 약간의 성능향상과 큰 효율향상을 꾀하는 게 하이퍼스레딩의 취지입니다.

그런데, AMD의 차기 CPU 아키텍처인 Bulldozer에서는 조금 다른 SMT 패러다임을 선보이게 됩니다.


Chapter 5: 1 '2-Core-like' Module / 2 Threads 모델
(AMD 클러스터 멀티스레딩)

AMD의 차기 CPU 아키텍처인 Bulldozer에서는 클러스터 멀티스레딩(CMT)이란 SMT 기법이 도입됩니다.
어찌 보면 앞에서 살펴본 "넓은 코어" 모델과 하이퍼스레딩의 혼합형이라고도 할 수 있는 이 모델은

기본적으로 1코어/2스레드 모델에 뿌리를 두고, 자주 쓰이는 유닛을 2코어처럼 분화시키는 것이 핵심입니다.
공유되는 유닛과 분화된 '자주 쓰이는 유닛'이 입력되는 스레드에 대응해 '클러스터'라는 처리 단위를 이루죠.
(즉 이 두 개의 클러스터가 융합된 구조를 불도저 아키텍처에서는 '불도저 모듈'이라고 합니다)

불도저 모듈의 백엔드 구조는 자칫 '부동소수점 유닛을 공유하는 두 개의 코어'처럼 받아들여질 여지가 있는데
마케팅적인 수사를 뒤로 하고, 실제오히려 '백엔드가 확장된 1개의 코어'라고 보는 것이 타당합니다.
샴쌍둥이가 다리를 공유하고 상반신이 나뉘어 있다면 어떻게든 두 사람이라고 간주할 수 있겠지만
머리와 다리가 하나이고, 팔만 네 개인 경우라면 두 사람이라기보단 팔이 더 달린 한 사람이라고 봐야겠죠?

CPU를 구성하는 부분을 명령어를 인출/해독하는 프론트엔드와 해독된 명령어를 처리하는 백엔드로 나누는데
1개의 불도저 모듈의 경우 AMD 측에서는 이를 2코어라고 홍보하고 있지만 프론트엔드는 아예 하나밖에 없는 데다가 백엔드 부분에서도 부동소수점 유닛을 공유하고, 오직 정수 유닛만 분화되어 있기 때문입니다.
이 구조가 실제 스레드를 처리할 때 어떻게 동작하는지 간단히 알아보겠습니다.



▲ 편의상 "많이 쓰이는 유닛"인 B, C 유닛을 불도저 모듈에서 스레별로 분화한 유닛으로 가정했습니다.
분화된 B, C 유닛은 사실상 각각 독립된 코어처럼 작동하고 (즉 2코어 모델과 동일한 효율)
분화되지 않은 A, D 유닛은 대신 1코어/2스레드 모델에서처럼 유휴 자원을 최소화하는 방향으로 작동합니다.
그 결과 작동 속도는 4 사이클로 매우 높아졌고, 가용률도 67%로 매우 높은 편입니다.

그렇다면 각 스레드가 사용하는 자원이 중첩되는 경우는 어떨까요?


스레드별로 분화되어 있는 연산유닛 B가 2코어 모델에서와 동일하게 작동하는 것을 보실 수 있습니다.
가용률은 6개의 연산 유닛이 8 사이클동안 작동해 (6 x 8 = 48) 16개의 명령어를 처리해 33%가 됩니다.

마지막으로 두 스레드가 서로 배타적으로 연산유닛을 점유하는 경우를 보겠습니다.


▲ 이 경우는 2코어와 1코어/2스레드 모델의 중간쯤 되는 가용률을 보여주고 있습니다.
(2코어 모델 50%, 1코어/2스레드 모델 100%, 지금 모델은 67%)


작업 예제에 걸린 총 소요시간은 4 + 8 + 4 = 16 사이클로 2코어 모델과 동일한 수준이고
총 가용률은 50%로 지금까지 살펴본 모델 중 최고치를 기록하고 있습니다.
즉 성능/가용률 양쪽에서 2코어 모델과 1코어/2스레드 모델의 장점이 나타나고 있는 것입니다.


Chapter 6: 모델 비교 및 분석

지금까지 살펴본 다섯가지 모델의 스레드 성질별 성능을 그래프로 나타내 보았습니다.



▲ 하나의 연산유닛을 두고 서로 경합하는 스레드(위 그래프에서 Exhaustive Thread 항목)의 경우 실제로 그 연산유닛이 늘어나지 않는 한 성능향상이 없습니다.
하지만, 그다지 경합하지 않는 스레드(위 그래프에서 Exclusive Thread 항목)를 처리할 때엔 가용률을 높일 수 있는 SMT 기술이 적용된 경우가 단연 뛰어난 성능을 보입니다.

각 모델별, 스레드 성질별 연산유닛의 가용률은 아래와 같습니다.


▲ 가용률은 SMT 기술이 적용된 경우에 더 좋단 것이 다시 한번 확인되고 있습니다.
좀 더 보기 편하게, 위의 두 그래프에서 살펴본 각 스레드 항목을 한데 모아 평균성능 및 가용률을 알아봅시다.



▲ 각 모델의 강점과 약점이 명확히 드러나는 부분입니다.
연산유닛 추가 없이 SMT 기술만 적용한 경우(네번째 모델)는 성능 향상은 크지 않지만 가용률이 높은 편이고
연산유닛을 실질적으로 2배로 늘린 2코어 모델(두번째 모델)의 경우 성능 향상은 크지만 가용률이 낮습니다.
이 둘을 절충한 불도저식 모델(다섯번째 모델)은 성능/가용률 모두 가장 뛰어난 모습을 보이고 있습니다.

한편, 연산유닛의 갯수는 트랜지스터 갯수에 비례하고 이는 곧 CPU 다이의 면적과 비례한다고 볼 수 있습니다.
다이의 면적이 CPU의 생산 단가를 결정한다고 보면 가격 vs. 성능의 그래프는 아래와 같습니다.


▲ 단순히 성능만을 알아 봤던 그래프와는 다른 양상을 보입니다.

제조사의 입장에서는 CPU의 절대 성능만큼이나 그 CPU를 생산하는데 드는 단가를 중요히 고려해야 하는데
하이퍼스레딩(네번째 모델)을 도입한 경우 생산단가는 그대로 두면서 20% 정도의 성능향상을 기대할 수 있죠.
또한 다섯번째 모델인 CMT는 전세대와 동일한 트랜지스터 수로 40%에 가까운 성능향상을 기대할 수 있기에
전세대 대비 1.5배 정도만 트랜지스터를 집적하더라도 두 배 이상의 성능을 이끌어낼 수 있게 됩니다.


지금까지 간단히 현존하는 SMT 기술의 특징과 그 한계에 대해 알아 보았습니다.
다음 강좌에선 구체적으로 어떤 자원을 통해 SMT가 구현되는지 알아보도록 하겠습니다. ^^

 

//

 

아래 위젯은 일종의 크라우드펀딩 플랫폼인 티스토리 '밀어주기' 서비스 위젯입니다. 100원부터 3000원까지의 범위 내에서 소액기부가 가능하며, 이런 형태의 펀딩이 성공적일 경우 '이해관계자로부터 독립된 벤치마크' 의 지속 가능한 원동력이 되리라 생각합니다. 물론 후원 없이 제 글을 읽어 주시는 것만으로도 저는 독자 여러분께 감사합니다 :)

 

 

 

'Lecture & Column > cpu_lec_col' 카테고리의 다른 글

A short essay on "Kaveri"  (13) 2013.11.27
파이프라이닝의 이해  (22) 2011.03.02
현대 CPU의 구조 : 프론트엔드 편  (36) 2011.01.22
현대 CPU의 구조 : 백엔드 편  (54) 2011.01.22
오버클럭의 공학적 배경  (26) 2011.01.14