멀티 코어 세상에서 살아 남기 |
| 적을 알고 나를 알자 |
 |


김도형 dynaxis@alticast.com
양방향 TV 솔루션 개발을 해 왔고 현재는 DMB를 위한 자바 규격 표준화와 제품 개발을 맡고 있다. JSR 242, JSR 272 등 방송 관련 JSR에 전문가로 참여했으며, 자바 외에 프로그래밍 언어 전반, XML 등 다양한 분야에 걸쳐 관심을 가지고 있다.
2008년 8월 12일
|
|
 |
|
2007년 7월 인텔 최초의 코어2 듀오 프로세서인 콘로(Conroe)가 출시되고, 코어2 브랜드가 성공적으로 자리 잡으면서, 드디어 만인을 위한 멀티 코어 시대가 열렸다. 불과 1년이 지난 요즘 주변에는 널린 게 듀얼 코어, 쿼드 코어 CPU다.
쓰는 사람이야 성능이 향상됐다느니 별로 모르겠다느니 하면서 즐기는 분위기지만, 프로그래머 입장에서는 그저 새로운 장난감을 바라 보면서 그저 흐뭇하게 웃고 있을 상황이 아니다. 왜냐고? 신상품을 팔아 먹고 사는 CPU 제조사가 혼자 뛰다 안 되니까 이제 프로그래머도 같이 뛰어야 한다고 말하고 있기 때문이다. 인텔 펠로우(fellow)인 Shekhar Borkar는 지난해 중반 한 인터뷰에서 “소프트웨어도 무어의 법칙을 따르기 시작해야 합니다. 병렬 처리 능력을 활용할 수 있는 여지가 2년마다 대략 두 배씩 높아져야 합니다”라고 말했다. 얼핏 점잖은 이야기 같지만, 결국 CPU 작동 주파수가 높아지면 소프트웨어도 자동으로 빨라지던 무임 승차 시절은 갔으니, 이제는 멀티 코어에 맞게 프로그래머도 차비를 내라는 말이다(관련 기사).
사실 무임 승차론은 늘 인텔, AMD와 짜고 입을 맞춰온 MS에서 나온 이야기다. 2005년 10월 리서치 회사인 In-Stat/MDR이 개최한 가을 프로세서 포럼에서 MS의 소프트웨어 아키텍트인 Herb Sutter는 소프트웨어 업계가 멀티 코어에 대한 대비가 되어 있지 않다면서 “무임 승차는 끝났다(The free ride is over)”라고 말했다(관련 기사). 지난해에는 역시 MS의 Ty Carlson이 비스타는 코어 한두 개 혹은 기껏해야 네 개 정도까지만 고려가 됐다며 차기 윈도우는 멀티 코어를 활용하기 위해 “근본적으로 달라져야 할 것”이라고 언급했고(관련 기사), 빌 게이츠는 MVP(Most Valued Professionals) 모임 기조 연설에서 멀티 코어 지원에 대한 중요성을 언급하면서 5년 안에 데스크톱에서도 16이나 32코어를 탑재하게 될 것이라고 예상하였다(관련 기사).
이렇게 칩 메이커와 소프트웨어 회사가 함께 호들갑을 떨던 끝에 올해는 미국 대학 몇 군데에 2000만 달러 규모의 연구 자금을 제공하고(관련 기사), 급기야 “대학에서의 병렬 컴퓨팅 관련 교육의 중요성”까지 언급하기 시작했다(관련 기사). 학교부터 업계까지 전 방위적 체질 개선이 필요하다는 것이다.
그렇다면 한번 생각해 보자. 과연 우리가 팀 버튼의 영화 “화성 침공”처럼 호의적으로 보이는 멀티 코어라는 화성인을 맞이하러 나갔다가 광선총에 녹아 없어지는 우스꽝스러운 상황에 놓인 걸까? 결론부터 이야기하면 그렇지 않다. 멀티 코어가 x86으로 상징되는 대중화 단계로 접어든 건 분명 큰 사건이지만, 멀티 코어 자체는 더 오래된 개념이고 프로그래밍 모델 면에서 멀티 코어와 별반 차이가 없는 여러 CPU를 탑재한 공유 메모리 시스템은 한층 더 오래된 개념이다. 그간 아무런 준비를 안 했을 리가 없다는 말이다. 다만 애당초 멀티 코어 활용은 인텔이 생각하는 것보다 어려운 문제이고, 전통적으로 멀티 코어/프로세서와 거리가 먼 데스크톱 환경에서는 굳이 멀티 코어를 고려해 어렵게 프로그래밍할 필요가 없었을 뿐이다.
사실 대화식으로 사용되는 데스크톱 환경에서 본질적으로 32, 64코어의 이점을 누릴 수 있는 응용이 얼마나 많은지도 아직 분명치 않다. 그저 대중화를 계기로 자원이 투입되는 비중이 바뀔 것이고 모두가 관심을 가지고 전 방위적 노력을 같이 해 나갈 필요가 있을 뿐이다. 이 과정에서 개발자도 멀티 코어 시대에 맞는 프로그래밍 도구와 방법에 이전보다는 관심을 가져야 할 것이다.
이런 관점에서 이번 글에는 먼저 왜 칩 제작사가 멀티 코어로 넘어갈 수 밖에 없었는지 그 배경을 살펴 보고, 멀티 코어 구조의 미래를 예상해 보겠다. 그리고 소프트웨어 측면으로 넘어와서 운영체제나 개발 도구 측면에서 멀티 코어를 대비해 당장 해야 하는 일에 대해 살펴 보고, 프로그래머로서 우리가 어떤 부분에 관심을 가지고 어떻게 대응해야 할지에 대해 나름대로 해법을 제시하면서 글을 마무리하겠다.
프로그래머는 억울하다
본론으로 들어가기 전에 프로그래머로서 몇 마디 성토는 하고 지나가자. 멀티 코어로 가는 추세는 대세이고 적극적으로 대응은 해야 하겠다. 하지만 위기를 논하기 앞서 인텔이 충고하고 MS가 윈도우보다 응용 쪽이 더 준비되어 있지 않다고 성토를 하는 것은 그리 공평한 처사가 아니다. 우리는 무임 승차하지 않았다.
소프트웨어에는 자신만의 과제가 있다
우선 모든 소프트웨어 로직은 하드웨어로 구현할 수 있다는 점을 상기하자. 그럼 어디까지는 하드웨어로 만들고 나머지는 소프트웨어로 처리하는 걸까? 물론 비싼 컴퓨터를 제한된 용도에만 쓰는 것이 아까워 소프트웨어 개념이 도입되었다는 원론적인 이유도 있지만, 소프트웨어는 실용적인 관점에서 “소프트”해야만 풀 수 있는 문제를 해결하기 위한 것이다. 제한된 다이(die)에 구겨 넣기에는, 또 그 많은 비용을 들여 구현하기에는 지나치게 복잡한데다, 무엇보다도 프로젝트마다, 시간이 가며 지속적으로 변하는 요구 사항으로 인해 꾸준한 유지 보수가 필요한 것이 바로 소프트웨어다. 결국 성능 향상 외에도 소프트웨어가 다뤄야 할 문제는 많다는 것이다.
병렬 프로그래밍은 배워도 어렵다
학교에서 병렬 프로그래밍에 대한 체계적인 교육이 부족하다는 점은 인정한다. 국내 유명 공대에서도 운영체제 과목에서 아주 기본적인 내용을 언급할 뿐, 대학원 선택 과목에서나 제한적인 병렬 프로그래밍을 다룬다. 하지만 병렬 프로그래밍에 있어 기본적인 딜레마는 원래 어렵다는 점이다. 인간의 뇌는 대규모 병렬 컴퓨터라고 하지만 아이러니하게도 인간은 기호와 논리를 도구로 순차적으로 문제를 해결한다. 따라서 동시에 여러 작업을 고려해야 하는 병렬 소프트웨어를 설계하기는 본질적으로 어렵다. 개인적으로 병렬 프로그래밍에 대해 팀 운영에 적용하는 원칙은 일부 핵심 부분이나 프레임워크만 조심해서 개발하고 가능한 병렬 프로그래밍을 피하는 것이다.
쓸 데가 없으면 더 나은 CPU도 필요 없다
CPU, 아니 x86은 꾸준히 빨라져 왔다. 하지만 더 이상 빨라 봐야 할 일이 없다면 어떻게 할 것인가? 지금까지 속도 경쟁과 CPU 발전을 뒷받침해 온 것은 다름 아닌 소프트웨어의 발전이다. 웹이 등장해 높은 처리 능력을 가진 서버의 수요를 상상하지도 못한 정도로 올려놨으며, 친절하게도 누구나 살펴볼 수 있는 웹의 특성을 고집하면서 굳이 처리 효율이 떨어지는 텍스트 형태의 정보 표현 수단을 고집해 주고 있다(HTML, XML, 자바스크립트 따위를 이야기하는 것이다). 3D 게임, MMORPG는 또 어떤가? 넘쳐나는 이미지, 동영상은 또 어떤가? 하드웨어 발전이 소프트웨어로 할 수 있는 일의 수준을 높이기는 하지만 그만큼 CPU의 발전 이면에는 소프트웨어 혁신이 있었다. 빠르게 동작하는 소프트웨어를 만드는 것만이 혁신은 아니다.
근본적인 연구는 이미 진행 중
무어의 법칙에 따른 집적도 향상에 따라 멀티 코어가 등장했다. 몇몇 중요한 특성에 있어 기존의 멀티 프로세서, 즉 여러 CPU가 메모리를 공유하는 시스템과 다르기는 하지만 프로그래밍 모델에 있어서는 사실상 같다고 볼 수 있다. 앞서 MS 스스로도 비스타가 멀티 코어에 충분히 대응하지 못하고 있다고 했지만, 그건 비스타를 구성하는 다양한 서브시스템과 시스템 응용이 충분히 멀티 코어에 최적화되지 않았다는 뜻일 것이다. 동일한 커널을 사용하는 윈도우 서버는 이미 64CPU까지 지원하고, 유닉스 서버 중에는 128CPU를 가진 서버도 판매되고 있다.
x86 상의 멀티 코어 도입으로 기존의 과학 기술 계산, 그래픽, 다수 사용자를 동시에 처리하는 서버 응용 외에 더욱 다양한 문제를 해결해야 하게 됐을 뿐 본질적으로 공유 메모리 프로그래밍 모델에 대해서는 이미 많은 연구가 이뤄졌다. 다만 그런 연구에도 불구하고 병렬 프로그래밍은 여전히 어렵다. 이제는 데스크톱 환경에서 더욱 다양한 문제를 해결하기 위해 여러 개의 코어를 사용해야 하는 시대가 왔고 불행히도 문제는 더 어려워질 것이다.
하지만 애당초 병렬 처리의 여지가 없는 곳에는 어쩔 수 없이 멀티 코어가 소용이 없을 것이고, 그 외 분야에서는 기존 계산, 그래픽, 서버 부하 처리에서 얻은 노하우를 조합하여 문제를 해결해 나가게 될 것이다. 향후 10년간 “멀티 코어 대응(multicore ready)”이라는 인증 마크를 붙이고 나오는 해법이 있다면, 대중화하고 상업화됐을 뿐 결국 이전의 연구 결과를 밝은 세상으로 끌어낸 것일 뿐일 거라고 장담한다. 자바가 혁신이라지만 가비지 컬렉션, 가상 기계 개념 등 이미 중요한 개념과 그 개념을 잘 조합한 선구자적 연구는 이미 존재했던 것과 마찬가지다.
결론은 거듭 말하지만 프로그래머는 무임 승차한 적이 없다는 것이다. 데스크톱 환경에 멀티 코어/프로세서가 일반적이지 않은데 굳이 어렵고 비용이 더 드는 병렬 프로그래밍을 할 이유는 없지 않은가? 이제 겨우 상황이 바뀌었을 뿐이다.
왜 멀티 코어의 시대가 와야만 했나?
그렇다면 어떻게 멀티 코어 CPU를 만드는 것이 경제적이 되었고, 단일 코어의 성능을 끌어올리는 것보다 더 많은 코어를 넣는 데 집중하게 되었을까? 우리가 흔히 아는 이야기는 주파수를 끌어 올려 성능을 높이다가 발열 한계에 달했기 때문이라는 것인데 실체는 그렇게 단순하지만은 않다. 거기에는 순차 프로그램에서 찾아낼 수 있는 병렬성의 한계와 전력 소모 문제가 얽혀 있다.
다양한 멀티 코어 프로세서
위키백과를 참고하면 2007년 코어2 제품이 나오기 전에도 많은 멀티 코어 CPU가 있었음을 알 수 있다. 세계 최초의 멀티 코어 CPU는 2001년 출시된 IBM POWER4다. 재미있는 것은 MS조차도 2005년 출시된 Xbox 360에 3개의 PowerPC 코어를 넣은 Xenon이라는 프로세서를 사용한다는 점이다. 또 아주 특이한 예로 Azul Systems는 자바만 실행하는 특화 컴퓨터를 만드는데(주로 WAS를 돌린다) 극단적으로 Vega 3라는 54코어 프로세서를 16개나 탑재해 사용한다. 그 외에 멀티 코어는 ARM 11 MPCore, ARM Cortex MPCore 등 임베디드 시스템 쪽으로 번진 지도 오래다.
|
순차 프로그램 병렬성의 한계
CPU 성능에 영향을 주는 요소로 당장 머리에 떠오르는 것은 동작 주파수다. 하지만 지금까지 보아온 CPU의 성능 향상은 반드시 동작 주파수에 비례하지는 않았는데, 이를 테면 AMD 제품이 한동안 더 높은 주파수로 동작하는 인텔 제품보다 나은 성능을 보여준 사례나 이전 펜티엄 4보다 낮은 주파수로 동작하는 코어2 듀오 제품의 코어 하나가 더 빠른 것만 봐도 알 수 있다. 이런 주파수 외 성능 향상은 CPU 아키텍처 개선에 기인한다.
- 명령어 수준의 병렬 실행: 순차 프로그램의 의미는 그대로 보존하면서 가능한 동시에 여러 개의 명령을 동시에 실행하면 성능을 높일 수 있다. 이를 위해 최근 CPU는 명령어를 처리하는 파이프라인을 여러 개 두어 동시에 하나 이상의 명령을 실행한다. 또, 문제가 없는 경우 명령어의 실행 순서를 바꾸기도 하는데, 예를 들면 느린 메모리 연산을 수행하는 동안 그와 무관한 다음 명령을 미리 실행하기도 한다. 이를 위해서는 파이프라인 간을 조율해 순차 프로그램의 의미를 훼손하지 않도록 점검하는 복잡한 로직이 필요하다. 또 동시에 여러 개의 명령어를 해석하는 것을 포함, 실제 명령을 수행하는 로직 수도 늘어야 한다.
- 수행을 방해하는 요소 제거: 현대 CPU 아키텍처는 동일한 조건에서 동작 주파수를 올리기 위해 거의 예외 없이 파이프라인 구조를 가진다. 뒤에 설명하겠지만 파이프라인 구조에서는 명령어를 실행하는 전 과정을 몇 단계로 나눠 컨베이어 벨트 위에서 동시에 여러 대의 자동차를 조립하듯 동시에 여러 명령어를 줄 세워 처리한다. 문제는 파이프라인 단계마다 명령어가 꽉 차야 최선의 성능을 발휘한다는 점인데, 당장 써야 하는 데이터가 캐시에 없어서 느린 외부 메모리에서 읽어와야 하거나 조건 분기 때문에 당장 다음 채워야 할 명령을 한동안 알 수 없는 등의 상황이 발생하면 파이프라인의 일부를 비워둬야 한다. 프로그램 동작은 무작위가 아니므로 이 경우 복잡한 로직을 도입해 프로그램을 실행을 관찰하면 미리 필요할 것 같은 데이터를 읽어 두거나 더 높은 확률로 분기할 방향을 예측할 수 있다.
- 레지스터나 캐시 등 메모리 병목을 해소할 수 있는 수단 보강: 느린 메모리 내용 중 앞으로 사용될 내용을 CPU 내 레지스터나 빠른 캐시 메모리에 저장할 수 있으면 느린 메모리로 인한 성능 저하를 어느 정도 막을 수 있다. 이를 위해서는 레지스터나 캐시 양을 늘리고, x86이나 알파 아키텍처 등에서 사용하는 레지스터 재명명(renaming)이나 고수준 캐시 관리 로직 등 때문에 복잡도가 증가하기도 한다.
문제는 아무리 병렬 수행을 방해하는 요소를 배제하려고 노력해도 궁극적으로 순차 프로그램을 구성하는 명령어를 병렬 수행하는 데는 한계가 있다는 점이다. 순차 프로그램에서는 필연적으로 한 부분에서 계산된 결과를 다음 부분에서 활용하게 되는데 이런 의존성이 바로 병렬 수행의 한계로 작용한다. CPU가 동시 수행할 여지를 찾으려면 많은 명령어를 동시에 살펴볼 수 있어야 하지만 살펴보는 명령어 수가 늘어날수록 기하급수적으로 로직이 복잡해지므로 순차 프로그램에서 끌어낼 수 있는 병렬성은 현실적으로 더욱 제약 받게 된다.
이러한 제약은 인텔 펠로우인 Fred Pollack이 주창한 폴락의 법칙(Pollack’s Rule)을 살펴보면 잘 알 수 있다. Pollack은 i386부터 현재까지 인텔 CPU를 살펴 보고 사용한 미세 공정과 주파수 등 다른 요인을 배제한 상태에서 아키텍처 개선만으로 “성능을 두 배 올리기 위해서는 대략 복잡도를 네 배 올려야 한다”는 사실을 발견했다. 여기서 복잡도란 로직을 구현하는 데 쓰인 다이(die) 면적이다. 결국 정교한 아키텍처 도입을 통해 단일 코어의 성능을 향상시키는 것은 어려울 뿐 아니라 밑지는 장사다. 반면 활용만 충분히 할 수 있다면 코어 두 개를 넣을 때 복잡도는 두 배 증가하지만 성능도 같이 두 배로 증가한다.
사실 단일 코어의 발전에 있어서 그저 지금까지 설명한 명령어 수준 병렬성(Instruction Level Parallelism 혹은 줄여서 ILP)만이 문제라면 특별한 경우를 빼곤 멀티 코어에 집착할 필요가 없을지 모른다. 같은 아키텍처라면 동작 주파수만 높으면 더 빨리 동작할 것이기 때문이다. 바로 인텔이 펜티엄 4 프레스캇(Prescott) 코어에 이르기까지 지향했던 바가 여기서 출발한다. 문제는 더 복잡한 아키텍처와 그에 따른 트랜지스터 수•주파수 증가는 어차피 복병을 만나게 될 것이었고, AMD나 같은 인텔 내의 다른 아키텍처와 달리 펜티엄 4에 사용된 넷버스트 아키텍처가 그 복병을 더 빨리 만나게 됐다는 점이다.
전력 소모와 열 발산
공정 발전과 함께 10GHz까지 동작 주파수를 높일 목적으로 설계된 펜티엄 4는 90nm 공정을 사용한 프레스캇 코어에서 결국 발열 문제로 4GHz의 벽을 넘지 못하고 좌초하고 만다. 또한 전력 소모 증가는 자체 소비 전력 및 쿨링(cooling) 등의 문제로 컴퓨터 시스템을 운영하는 비용에도 막대한 영향을 끼치게 되었다. 바야흐로 성능 지상주의 시대는 가고 전력이 중요한 요인으로 부각된 것이다.
CPU의 전력 소모에는 사용된 미세 공정, 트랜지스터 수, 주파수, 동작 전압, 다이 크기 등 다양한 요인이 서로 얽혀 있다. 앞서 언급한 인텔 펠로우인 Shekhar Borkar는 IEEE Micro에 실린 “Design Challenges of Technology Scaling”이라는 논문에서 현재 공정 기술 발전과 그에 따른 동작 주파수•다이 크기 증가 등의 추세를 고려할 때 미세 공정으로 얻는 소자 동작 속도 향상, 에너지 소모 감소를 감안해도 2010년에는 2000W의 전력을 소모할 거라고 예측했다. 참고로 어지간한 다리미가 보통 1000W 수준의 전력을 소모한다. 결국 공정 발전보다 오히려 소모 전력이 CPU 성능 향상에 있어 더 치명적인 문제가 된다는 것이다. 이를 완화하려면 결국 다이 크기를 줄이거나 전압, 주파수를 떨어 뜨려야 한다. 모두 성능을 떨어뜨리는 요인이다. 따라서 지금까지처럼 더 복잡한 로직을 사용하고, 더 높은 주파수를 추구하는 추세는 한계에 봉착할 수 밖에 없다.
앞선 폴락의 법칙에서 한 가지 더 특기할 점은 전력 소모가 복잡도에 정비례한다는 것이다. 즉, “성능을 두 배 올리기 위해서는 네 배의 전력이 필요하다”라는 것이고, 거꾸로 생각하면 “성능을 반으로 낮추면 전력은 1/4이면 된다”. 이론상으로 1개 코어의 50% 성능의 코어를 4개 붙여도 전력은 이전과 동일하되, 코어 당 기존의 40%만 성능을 발휘해도 160%의 성능을 얻을 수 있다. 따라서 멀티 코어는 다음과 같은 다양한 방향으로 적용될 수 있다.
- 전력 소모 등 제약 조건을 맞추는 범위 내에서 단일 코어의 성능을 최대한 끌어내고 남는 트랜지스터 자원을 활용해 더 높은 성능을 끌어낼 가능성을 창출
- 단순한 코어를 여러 개 사용함으로써 전체적인 성능은 유지하면서 훨씬 낮은 전력 소모를 달성
결국 차이는 성능이 중요하냐 전력 소모가 더 중요하냐에 있다. 현재 대부분의 멀티 코어는 x86이나 멀티 코어 GPU, 고성능 임베디드 시스템을 목표로 하는 ARM MPCore를 포함해 대부분 전자에 가깝다고 볼 수 있다. 멀티 코어라고 하더라도 포함된 단일 코어의 성능을 올리는 것이 중요하지 않은 것은 아니기 때문이다. 멀티 코어를 활용하기에 충분한 병렬성을 찾아내기는 본질적으로 어렵다. 앞으로도 전력 및 공정상의 물리적 한계 내에서 가능하면 동작 주파수를 높일 것이고 충분한 효과만 있다면 아낌없이 더 많은 트랜지스터를 투입할 것이다. 하지만 전력에 더욱 민감하고 다양한 응용보다는 정해진 형태의 응용을 실행하는 임베디드 시스템을 시작으로 점차 후자의 형태도 일반화될 것이다.
인텔 넷버스트(NetBurst) 아키텍처의 종말
인텔의 프레스캇 코어는 주파수 경쟁을 고집한 넷버스트 아키텍처의 최종판이었다. 하지만 x86 단일 코어의 한계에 접근한 시도였고 단일 코어로서 궁극의 성능에 다가갔다고 생각하는 것은 잘못이다. 그간 AMD의 아키텍처나 그 후 인텔의 코어 아키텍처와 다른 방향을 택했고 그게 잘못된 시도였을 뿐이다.
간단히 말해 CPU 내에는 트랜지스터로 구현되는 단순한 로직이 꼬리에 꼬리를 물어 복잡한 로직을 구성한다. 꼬리를 문 경로를 통해 신호가 전달되는 데는 시간이 걸리는데 이 시간은 미세 공정일수록 짧아진다. 결국 동일한 공정에서 주파수를 올리려면 너무 긴 경로가 없어야 하는데 파이프라인 구조에서는 이를 위해 명령어를 수행하는 전체 과정을 잘라 짧은 경로를 가진 여러 단위로 나눈다. 이런 파이프라인의 각 요소는 자동차를 생산하는 컨베이어 벨트 시스템처럼 모두 동시에 동작하면서 각 단계 사이로 순서대로 명령어를 이송한다.
프레스캇은 주파수를 올리기 위해 파이프라인의 단계를 늘리고 앞서 언급했던 것처럼 긴 파이프라인을 빈틈없이 채우기 위해 복잡한 로직을 채용했다. 처음 20단계로 시작한 넷버스트 아키텍처의 파이프라인은 프레스캇에 이르러는 31단계로 늘어난다. 하지만 AMD나 코어 아키텍처에서는 수십 단계를 고수하는 대신 한번에 실행될 수 있는 명령 수를 늘이고 다른 최적화에 초점을 맞추었다. 양쪽 방향 모두 실행하는 프로그램의 특성에 따라 장단점이 있는데 프레스콧의 접근법은 애당초 실제 프로그램에 적용했을 때 효과적이지 못했다. 한 리뷰에서는 게임을 실행할 때 프레스캇이 2.6GHz로 동작하는 Athlon 64 FX와 맞먹으려면 동작 주파수가 5.2GHz는 되어야 한다고 평했듯 애당초 잘못된 방법을 택했던 셈이다.
결국 동작 주파수도 앞서 언급한 발열 문제로 너무 빨리 한계에 도달해, 10GHz를 내다보고 설계한 넷버스트 아키텍처는 코어 아키텍처에 자리를 내어 주게 되었다.
|
멀티 코어의 미래
흔히 프로그래밍 모델에 있어서 차이가 없는 멀티 프로세서와 멀티 코어를 구분하지 않고 이야기하기도 한다. 하지만 무어의 법칙에 의해 급격히 늘어난 집적도로 인해 한 칩에 여러 개의 코어를 넣게 된 것은 몇 가지 의미를 가진다.
- 코어끼리는 고속 스위치나 L2 캐시를 매개로 연결됨으로써 코어 간 고속 데이터 공유가 가능하다.
- 필요에 따라 각 코어를 켜고 끄거나 동작 전압, 주파수를 조정함으로써 전력 효율을 한층 높일 수 있다.
물론 이런 장점 외에도 코어가 늘어나면서 필요한 메모리 대역폭도 대폭 늘어나는 단점도 있지만, 하지만 이런 부분은 점차 개선될 것으로 보인다(참고 자료).
그렇다면 앞으로의 멀티 코어는 어떤 형태가 될까? 먼저 올 2월 열린 International Solid State Circuits Conference에서 AMD, IBM, 인텔, 르네사스(Renesas), 썬 등이 패널로 참여한 토의를 살펴보는 게 도움이 될 것 같다. 특기할 만한 것만 간단히 추려보면 다음과 같다.
- AMD: ATI를 인수해 CPU와 GPU의 통합을 꾀하는 입장에서 각종 작업에 최적화된 상이한 코어들을 활용하는 방향으로 나갈 것으로 예상했다. 또한 이런 코어는 API 호출에 의해 동작하게 될 것이고 따라서 아키텍처 자체의 호환성보다는(x86을 고집하는 인텔과 달리) DirectX 같은 API 수준의 호환성으로 방향이 선회할 것이라고 봤다.
- 르네사스: 휴대전화 응용 프로세서를 만드는 회사답게 AMD와 비슷한 관점을 공유하되 오늘날 휴대전화에서 다양한 프로세서를 혼용하는 것이 좋은 모델이라고 언급했다.
- IBM: 궁극적으로는 많은 범용 코어를 주로 활용하되 일부 필요한 특수 목적 코어를 섞어 사용할 것으로 예상했다.
- Tilera: 프로그래밍 모델을 단순하게 하기 위해 동일한 코어를 탑재하는 방향으로 가야 한다는 의견을 피력했다.
결론적으로 동일한 범용 코어에 절대적인 지지를 보내는 쪽에서 반대 극단으로 특수 목적의 서로 다른 코어를 섞는 방안까지 다양한 의견이 나온 셈이다. 이처럼 미래를 예측하기는 어렵다.
적용 분야마다 최적의 코어 구성은 다르겠지만 개인적으로는 AMD와 비슷한 생각이다. 서버를 제외한 데스크톱 및 임베디드 환경에서는 고속의 범용 코어는 2~4개 선으로 제한되고 특수 목적의 코어를 다수 탑재하는 구조가 되지 않을까 예상한다. 이 특수 목적 코어는 아마도 소수의 프로그래머가 사용하고 주로 라이브러리 형태로 제공되어 다수의 프로그래머는 API 수준에서 활용하게 될 것이다. 물론 이러한 특수 목적 코어가 고속의 범용 코어와는 아키텍처가 다를지라도 단순한 범용 코어가 될 수는 있다고 생각한다. 그 차이는 일반적인 프로그래머가 이런 특수 목적 코어를 직접 활용하는 정도에서 나타날 것이다.
이는 명령어 수준 병렬성과 달리 전제한 환경에서 멀티 코어에서 필요한 스레드 수준 병렬화(thread level parallelism)를 끌어 내기가 어려울 것이라는 개인적인 비관론에서 출발한다. 통상 CPU 부하를 급격히 높이는 멀티미디어 편집이나 비디오 인코딩 등은 특화된 코어에 더 적합하다. 최근의 도시바 랩톱은 셀 프로세서를 탑재해 SD 비디오를 HD급으로 업스케일링하거나 웹 캠 영상에서 동작을 인식하는 등의 작업을 할 수 있게 했다(참고 자료). 이런 작업은 상시 일어나는 것은 아니지만 셀 프로세서 덕에 필요할 때는 아주 빨리 실행할 수 있다. 이런 연장선 상에 개인적으로 생각하는 멀티 코어 CPU의 미래가 있다. 물론 틀릴 수도 있겠지만 말이다.
멀티코어 대응: 운영체제에서 개발 도구까지
멀티 코어의 대중화로 큰 일이 난 듯 떠들어도 궁극적으로 멀티 코어를 충분히 활용해야 하는 쪽은 우선은 운영체제이고, 개발 도구 측면에서도 우선적인 개선이 있어야 한다. 현재 운영체제와 개발 도구 측면에서 멀티 코어 대응은 어떤 정도이고 앞으로 어떤 일이 필요한지를 살펴보자.
운영체제 지원
앞서 말했든 이미 많은 운영체제가 64개 이상의 CPU를 지원하는 마당에 무엇을 더 준비해야 할까? 크게 “기존의 멀티 프로세서 지원을 확장해 멀티 코어에 더욱 최적화”하는 것과 “운영체제 자체와 서비스의 병렬화”, “응용을 위한 프레임워크 지원”이다. 운영체제에서 무슨 대응이 가능한 지 구체적인 사례를 알기 위해서 애플 맥 OS X의 경우를 살펴 보자.
MS는 비스타의 멀티 코어 대응에 대해 함구하고 있지만, 애플 맥 OS X의 경우는 약간 다르다. 맥 OS X은 충분하지 않다고는 하지만 최근 레퍼드(Leopard) 버전에서는 다음과 같은 멀티 코어 지원이 추가되었다.
- 스케줄러를 개선
- 네트워크 스택을 멀티스레드화
- 기본 응용(Mail, Address Book 등)과 스포트라이트 검색, 사전 검색을 병렬화
- NSOperation이라는 API를 통해 스레드가 아닌 단위 작업을 등록, 각 코어에 할당: 코어 수에 영향을 받는 멀티스레드 프로그래밍에 비해 효율적이고 쉽다.
- 메시지 전달 방식의 과학 계산을 지원하기 위해 OpenMPI 탑재
아울러 차기 버전인 스노우 레퍼드(Snow Leopard)에서는 충분한 정보는 공개되지 않았지만 “Grand Central”이라는 새로운 기술을 통해 운영체제 자체를 더욱 더 멀티 코어에 최적화하고 응용이 멀티 코어를 쉽게 활용할 수 있게 할 것이라고 한다. “Grand Central Dispatch”는 커널에 통합되어 응용이 제출한 작업을 블록 단위로 나눠 여유 있는 코어에 효율적으로 전달한다. 또한, OpenCL(Open Computing Language)이라는 NVidia의 CUDA처럼 GPU와 멀티 코어 CPU를 범용 계산에 사용할 수 있도록 배려할 예정으로 “Grand Central”과도 통합될 예정이다.
개발 도구 지원
운영체제보다 사실 더 준비가 되어 있지 않은 쪽은 개발 도구 쪽이다. 그나마 제공되는 도구는 다음과 같다.
- OpenMP: OpenMP는 순차 프로그램에 몇 가지 #pragma를 붙여 흔히 과학 기술 계산에 활용되는 루프 병렬화와 재귀 함수 병렬화 등을 쉽게 할 수 있는 일종의 API이자 언어 확장이다. 다행히 GCC와 비주얼 C++를 비롯해 대부분의 컴파일러 경우 OpenMP를 지원한다.
- 인텔 Threading Building Blocks(이하 TBB): 병렬 프로그래밍을 위한 고수준 API를 제공하는 C++ 템플릿 라이브러리로 오픈 소스로 공개되어 있다. 자바의 java.util.concurrent API처럼 다양한 데이터 구조와 알고리즘을 제공하여 명시적으로 스레드를 생성하거나 동기화를 할 필요 없이 멀티 코어를 활용할 수 있게 해 준다. OpenMP와 비슷한 병렬 루프 수행을 위한 수단도 제공한다.
- 자바의 java.util.concurrent: 현재 데스크톱 응용에서 자바의 활용도는 다소 의문이지만 자바는 일찌감치 다양한 고수준 및 저수준 API를 제공하고 있다. 앞으로 나올 자바 7에는 Fork-Join 프레임워크가 추가될 예정이다. 이를 통해 맥 OS X이나 인텔 TBB처럼 작업 단위의 병렬 처리를 구현하기 쉽게 될 것이다.
- 각종 멀티 코어에 최적화된 라이브러리: 인텔의 경우 멀티 코어에 최적화된 수학 라이브러리(Math Kernel Library)와 멀티미디어를 위한 IPP(Integrated Performance Primitives) 같은 라이브러리를 제공한다. 이런 라이브러리를 통해 응용은 저절로 멀티 코어를 활용하는 부수 효과를 얻게 된다.
여기까지를 살펴보면 OpenMP는 API이면서도 #pragma를 인식하고 그에 따라 루프를 자동으로 인식한다는 면에서는 일종의 언어 확장이다. 하지만 TBB는 순수 라이브러리이고 자바의 경우 최적화된 라이브러리이되 어느 정도는 VM의 지원을 받는다. 위 도구들은 주로 C/C++로 대표되는 기존 프로그래밍 환경에 쉽게 녹아 들어갈 수 있도록 언어 확장은 가능한 자제하고 라이브러리 형태를 띠고 있다.
이미 몇몇 특화된 소규모 회사가 병렬 프로그래밍을 돕는 도구를 만들어 시판하고 있지만, 앞으로는 기존 연구를 바탕으로 다음과 같은 다양한 형태의 개발 도구가 나오리라 예상한다.
- 병렬 언어: 전체 프로그램을 새로운 언어로 작성하는 것은 어려운 일이지만 예를 들어 일부 핵심 알고리즘을 함수 언어로 작성하면 컴파일러가 자동으로 병렬화하는 데 유리하다. 이렇게 작성된 핵심 모듈은 다른 언어로 작성된 GUI 코드나 기타 코드와 연결되어 실행될 수 있다.
- 병렬성 분석 도구: 코드를 분석해 프로그램 내에서 자동으로 병렬 수행이 가능한 부분을 찾아내거나 더 나아가 코드를 변경하는 도구도 완전하지는 않지만 점차 개선되어 나가리라 기대한다.
- 더 많은 라이브러리: 앞으로 더 많은 라이브러리가 개발되어야 할 것이고 이는 특화된 코어를 함께 사용하면서 더욱 중요해질 것이다. 특화된 코어는 프로그래밍하기가 어렵고 범용 코어를 포함한 전체 맥락에서 최적화하기도 어렵기 때문이다.
- 새로운 병렬 알고리즘: 도구라고 하기는 어색하지만 현존하는 알고리즘의 병렬 버전을 개발하거나 새로운 병렬 알고리즘을 개발하는 노력이 필요하다. 지금까지는 비디오 인코딩, 3D 등 일부 분야에서만 병렬 수행을 중요시해 왔지만 앞으로는 의식적인 노력이 더욱 중요하다.
프로그래머를 위한 생존 전략
그럼 마지막으로 프로그래머로서 멀티 코어 시대를 살아 나가기 위해 어떤 전략을 가져야 할까? 모든 프로그래머가 관련 하드웨어 아키텍처와 병렬 프로그래밍 이론에 정통할 필요는 없다고 생각한다. 여력이 닫는다면 물론 더 많이 읽고 더 많이 고민하는 것이 좋겠지만 프로그래머에게는 배워야 할 것이 쌓이고 쌓였다.
우선은 소프트웨어 전공자라면 컴퓨터 아키텍처에 대한 기본 개념을 잊지 않도록 해야 할 것이다. 쏟아지는 각종 정보를 흡수하는 데 많은 도움을 준다. 문제는 더 깊이 들어가는 것인데 통상 소프트웨어 관점에서 병렬 프로그램을 쉽게 다루는 책은 상당히 드물고 오히려 자바 관련 서적에 그런 책이 몇몇 있다. 하지만 자바의 메모리 모델까지 다루는 “Concurrent Programming in Java”: Design principles and patterns”만 해도 일반적인 프로그래머가 평이하게 읽기에는 벅찬 감이 있다.
따라서 개인적으로는 우선 자신이 종사하는 분야를 중심으로 가장 적은 노력으로 멀티 코어의 이점을 누릴 수 있는 멀티 코어에 최적화된 라이브러리의 동향을 잘 파악해 두는 것이 좋다고 생각한다. 또한 관련 분야 알고리즘을 살필 때 병렬 알고리즘을 더 눈 여겨 보는 것은 당연할 것이다.
그 다음으로 자바나 인텔 TBB처럼 고수준 API를 제공하는 라이브러리를 살펴볼 것을 권한다. TBB에 대해서는 “Intel Threading Building Blocks: Outfitting C++ for Multi-core Processor Parallelism”이라는 책이 나와 있고, 자바 쪽으로는 “Java Concurrency in Practice”라는 책이 나와 있다. 이런 책의 장점은 앞선 “Concurrent Programming in Java”보다는 가벼우면서 병렬 프로그래밍에 대한 기본 개념을 익힐 수 있다는 점이다.
다음으로는 기획자가 늘 사물의 새로운 면을 보듯 이제 소프트웨어 개발에 있어 스레드 수준 병렬성을 찾는 날카로운 눈을 항상 번득일 필요가 있을 것이다. 하지만 병렬 프로그래밍에 있어 철칙은 “KISS(Keep It Simple, Stupid)”라는 점을 잊지 말아야 한다. 병렬성을 위한 지나친 추구와 복잡한 구조는 멸망으로 가는 지름길이다. 저수준 병렬 프로그래밍은 많이 재사용되는 작은 모듈을 작업하는 소수의 프로그래머에게 제한되어야 한다. 멀티 코어 대중화 시대에서는 절대 다수의 코드는 고수준 라이브러리, 도구를 활용하여 읽기 쉽고 유지 보수가 쉬워야 한다.
마치면서
지금까지 멀티 코어의 탄생과 미래, 그리고 소프트웨어 업계가 어떤 일을 해야 하는지를 살펴 보았다. 살펴보면 알겠지만 큰 변화이기는 해도 어떤 소프트웨어든 마찬가지겠지만 궁극적으로 프로그래머가 해야 하는 일은 복잡하고 이해하기 힘든 초병렬 코드를 토해내는 것은 아니라는 점이다. 이는 멀티 코어 시대를 맞이하는 대다수의 프로그래머에 있어 궁극적으로 재앙에 가까운 변화가 일어나지는 않는다는 것이다. 상황 변화를 인지하고 차근히 준비해 가자.
이 문서 북마킹 하기
[지난 developerWorks Column 보기]
|