굳이 전산학이나 컴퓨터과학· 공학의 전공자가 아니어도, 그리고 현업 프로그래머가 아니더라도 조금만 기계 다루는 것에 조예가 있는 컴덕후라면 요즘 컴퓨터의 발전 추세가 어떤지는 다들 알 것이다. 사실은 <날개셋> 한글 입력기의 개발자인 본인보다도 더 잘 알 것이다.

튜링 완전하며 메모리로부터 프로그램을 읽어 와서 구동하는(프로그램 내장 방식) 범용 컴퓨터가 발명된 지가 아직 100년은커녕 반세기 남짓밖에 지나지 않았다. 그러나 컴퓨터의 속도와 메모리 용량은 가히 넘사벽급으로 발달했다. 컴퓨터는 지난 수십 년 동안 자연이 아니라 인간의 발명한 물건의 내부에서, 시간에 따른 지수함수 스케일의 발전을 볼 수 있던 얼마 안 되는 엄청난 분야였다.

그러나 그것도 한계는 있어서 컴퓨터 성능의 대표적인 지표이던 클럭 속도가 2003년 즈음에 놀랍게도 지수함수적인 성장이 멈췄다. 10년까지는 아닌 거 같다만 8, 9년 전의 PC나 지금의 PC나 다 3GHz대이다. 오늘날과 같은 반도체 회로라는 근본적인 패러다임이 바뀌지 않는 한, 전력 소모와 발열 때문에 컴퓨터의 기본 동작 자체만의 속도가 더 빨라질 수는 없다.

우리가 다루는 컴퓨터는 생각보다 굉장히 정밀하고 빡세게 만들어져 있다. 예전부터 파이프라인, pre-fetching 등 온갖 있는 테크닉, 없는 꼼수를 다 동원해서, 어떻게든 낭비되는 자원이 없이 모든 자원이 계산에 동원되고, 1ms라도 속도를 더 올리려고 공돌이들의 온갖 공밀레 연구가 동원되었다. 이런 수직적인 성장이 한계에 달하니 요즘 컴퓨터는 코어 수를 늘려서 분업이라는 수평적인 성장을 추구하고 있다.

100이라는 일이 있어서 이게 2GHz짜리 컴으로 다 처리하는 데 10이라는 시간이 걸린다면, 1GHz짜리 컴으로는 20만큼의 시간이 걸릴 것이다. 그러나 1GHz 짜리 컴이 2개 있어서 그 일을 정확히 50씩 분담할 수 있다면, 각각의 컴퓨터의 최대 속도는 비록 1GHz밖에 안 되더라도 10에 가까운 시간 만에 일을 끝마칠 수 있어서 2GHz에 준하는 효과를 낼 수 있다. 이것이 기본적인 아이디어이다.

물론, 딱 10으로 줄이지는 못한다. 일을 분할하고 두 대의 컴퓨터를 세팅하고 굴린 후, 두 컴퓨터(코어)가 내놓은 결과를 취합하는 오버헤드가 역시 결코 작지 않기 때문이다. 그리고 세상에 상호 의존성이 없이 역할을 저렇게 딱 분담할 수 있는 작업이란 흔치 않다.

방대한 계산이 필요한 작업을 다수의 컴퓨터 기계들을 여러 대 동원해서 수행하는 건, 분산 컴퓨팅이라 하여 예전부터 그리 생소한 개념이 아니었다. 3D 그래픽으로 영화를 만드는 업계에서는 rendering farm이라 하여 수많은 컴퓨터들을 농장에다 비유할 정도로 열나게 굴려서 영상을 만들어 낸다. 빌드 속도가 느린 걸로 악명 높은 C++ 언어를 위해서 Incredibuild라는 이스라엘산 분산 빌드 시스템도 있다.

그런 것처럼 개개의 컴퓨터도 이제 코어가 여러 개 돌아가고 멀티스레딩 능력이 충분히 뛰어나니, 이제는 프로그램 차원에서 멀티코어-aware한 개발이 필요해졌다. 왜냐하면 분산 처리 시스템을 설계하는 건 정말 머리가 뽀개질 정도로 복잡하기 때문에 컴퓨터나 컴파일러가 자동화를 해 줄 수 없다. 예전 계산 결과에 의존적인 다음 계산을 병렬로 동시 수행시킬 수는 없는 노릇이니까 말이다.

그렇기 때문에 멀티코어-aware하지 않은 옛날 프로그램은 컴퓨터가 언제나 최악의 case로 가정하고 보수적으로 돌려서 코어를 하나만 할당하여 돌아가게 된다. 한 프로그램이 while(1); 만 하고 가만히 있다 해도 CPU 사용률이 100%로 치솟지 않는다.

특히 C/C++의 포인터는 그야말로 아무 메모리 주소나 가리킬 수 있고, *a, *b가 있으면 둘이 가리키는 주소가 같을지 아닐지 등 최적화나 병렬화를 할 여지가 없을 정도로 너무 generic하기 때문에 오히려 처리가 어렵다고 한다. 파스칼이나 포트란처럼 언어 표현력이 C/C++보다 부족한 대신에 컴파일러가 소스 코드만 보고서 그 복잡도를 어느 정도 파악할 수 있는 언어가 병렬화에는 오히려 더 유리하다고.

그래서 소스 코드의 특정 구간에 대해서 컴파일러나 CPU가 안심하고 병렬화를 할 수 있게 중간 중간에 부가정보를 인위적으로 넣어 줄 필요가 생겼는데, 이런 부가정보를 기술하는 표준 규격 중 하나가 그 이름도 유명한 OpenMP이다. #pragma omp 같은 컴파일러 지시자도 있고, OpenMP 라이브러리보부터 호출해서 쓰는 함수도 있다.

옛날에 C언어가 처음 발명되던 시절엔 최적화를 위해서 변수를 가능한 한 레지스터에 올려라고 지시하던 register라는 카워드가 있었는데, 앞으로 C/C++ 같은 네이티브 코드 언어는 멀티코어를 언어 차원에서 수용하는 규격이 덩달아 생겨야 할 것이다.

물론, CreateThread 같은 함수를 써서 코드의 로직 차원에서 대놓고 여러 작업 스레드를 만들었다면, 단일 프로세스가 여러 개의 코어를 자연스럽게 점유하는 게 응당 가능하다. 그러나 디자인의 성격상, 멀티스레딩은 보통 일하는 스레드, 사용자 UI 스레드, 입출력 신호를 기다리는 스레드처럼 용도별로 달리 배당하는 게 바람직하지, 동일한 일을 하는 코드 내부에서 굳이 다중 코어 활용을 위해 스레드를 분할하기에는 동기화를 비롯해 일이 너무 복잡해진다. 서버 같은 게 아니라면, 제각기 CPU를 full로 사용하는 여러 작업 스레드를 만들 일 자체가 매우 드물다.

이때 멀티코어 프로그래밍을 잘 하면 이런 단일 코드가 명시적인 스레드 생성을 하지 않고도 CPU의 자원을 골고루 전부 활용해서 고성능으로 돌아가게 된다. 다만 앞서 말했듯이 이렇게 작업을 분할하는 오버헤드부터가 큰 편이기 때문에, 소규모 작업보다는 동영상 인코딩이나 대용량 파일 압축이나 컴파일 같은 진짜 방대한 작업에서 멀티코어의 진가는 더욱 두드러질 것이다.
일례로, 비주얼 C++은 2005부터 빌드할 때 병렬 컴파일이 수행된 경우, 빌드 로그에 해당 빌드를 수행한 코어의 번호가 뜬다.

O(n^3)짜리 행렬 곱셈은 워낙 단순 무식하고 복잡한 의존도 따위도 없는 순수 노가다이다 보니, “나 병렬화 해 주세요”라고 온몸으로 외치는 문제라고 볼 수 있다. 퀵 정렬이나 병합 정렬처럼 구간이 딱딱 나뉘는 알고리즘이 병렬화에도 적합한 구조일 것 같긴 하다만, 요즘 컴퓨터의 성능이라면 겨우 internal sort 규모의 정렬 작업은 굳이 멀티코어를 쓸 필요도 없을 정도로 금방 끝날 듯.

요컨대 이제는 고성능 컴퓨터를 쓰는 것만으로 프로그램의 수행 속도가 저절로 올라가는 시대는 끝났다. 그 다음으로 레지스터 배당이라든가 캐시나 파이프라인 적중률 같은 건 CPU 설계에 맞춰 컴파일러가 더 똑똑해야 하는 영역인데, 이 역시 튜닝의 수준은 예술의 경지에 도달해 있다. 그리고 그 다음으로 필요해진 것이 코드에 등재되어 있는 병렬화 가능성 정보라고 생각하면 되겠다. 멀티코어는 프로그래머가 잘 숙지하고 있어야 하는 프로그래밍 패러다임이 되었음이 틀림없다.

Posted by 사무엘

2012/04/13 08:24 2012/04/13 08:24
Response
No Trackback , 7 Comments
RSS :
http://moogi.new21.org/tc/rss/response/668

Trackback URL : http://moogi.new21.org/tc/trackback/668

Comments List

  1. 주의사신 2012/04/13 10:11 # M/D Reply Permalink

    1. 사람은 한 번에 하나에만 집중하는 것을 잘 합니다(마 6:24). 그러다 보니 두 개 이상의 분석을 해야 하는 멀티코어, 멀티 스레드는 어려울 수밖에 없습니다.

    모든 사람을 다 집중해서 보시는 하나님은 얼마나 대단하신건지요...


    2. 그래서 요즘 C/C++을 잘 사용하려면 포인터의 사용을 웬만하면 자제하라는 이야기를 하더랍니다. 포인터를 쓰면 최적화시켜 줄 수 있는 것도 최적화시켜줄 수 없다고...

    1. 사무엘 2012/04/13 15:44 # M/D Permalink

      그래서 심지어는 xor 연산을 이용한 임시 변수 없는 꼼수 swap조차도, 병렬성이 안습하기 때문에 멀티코어 CPU에서는 전통적인 임시 변수 대입을 이용한 swap보다 느릴 수 있다고 하지요.
      Aero DWM하에서는 화면 전체의 DC를 얻어서 xor 래스터 연산으로 경계선을 긋는 게 오히려 실시간으로 개체를 옮기는 것보다 성능이 떨어지고..
      그런 프로그래밍 패러다임이 바뀌는 날이 올 거라고 옛날에 예측하기는 어려웠겠죠~!

  2. 김재주 2012/04/14 13:49 # M/D Reply Permalink

    이런 면에서 함수형 언어가 concurrency에서는 기존 언어들보다 확실히 편하지 않은가 싶습니다
    함수 단위로 이게 atomic한지 아닌지만 프로그래머가 지정해주면 나머지는 최대한 컴파일러에서 해결!

    물론 함수형 언어를 쓴다고 해서 문제를 쪼개는 게 간단해지거나 쉬워지거나 하지는 않겠죠

    1. 사무엘 2012/04/14 20:35 # M/D Permalink

      잘 지내시나요?
      C/C++ 사고방식으로 변태같은 최적화 덕질을 추구하는 게 아니라,
      한번 만든 값은 계속 불변으로 남아 있고 side effect가 있는 대입을 피하는 패러다임의 언어라면
      아무래도 병렬 처리 디자인이 용이하겠죠..!

      물론 그래도 한계는 말씀하신 것 그대로입니다.

  3. 사람이 2012/04/15 02:28 # M/D Reply Permalink

    어렵네요.



    멀티코어 지원,
    유니코드 지원,
    멀티dpi 지원(망할 네이트온 -_-;)

    이 완벽한 프로그램은 언제 나올까요?

    1. 사무엘 2012/04/15 19:16 # M/D Permalink

      DLL의 경우 64비트 지원도 추가로 있죠. ㅋ
      그런 것들이 요즘 프로그램들의 현대화를 나타내는 지표입니다.

    2. 사람이 2012/04/17 00:57 # M/D Permalink

      아맞다 64비트 네이티브 지원 -_-


      망할 HKLM|software, HKCR의 wow6432node -_-

      저 둘에 키값을 기록하는 32비트 프로그램 덕에(HKCU|software에 기록해도 별문제 없는 것들이 꼭 저기다 기록을 하는 바람에)

      64비트 처음 쓸 때 개삽질 한 기억이 나네요.

Leave a comment
« Previous : 1 : ... 1560 : 1561 : 1562 : 1563 : 1564 : 1565 : 1566 : 1567 : 1568 : ... 2139 : Next »

블로그 이미지

그런즉 이제 애호박, 단호박, 늙은호박 이 셋은 항상 있으나, 그 중에 제일은 늙은호박이니라.

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2024/04   »
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        

Site Stats

Total hits:
2665913
Today:
1151
Yesterday:
1937