병럴처리의 한계와 가능성 – Amdahl의 법칙

병렬 프로그래밍이 여러곳에서 말 나오면서 제일 많이 들어봤을 법칙이다. 병렬 처리를 이용하여 얻을 수 있는 속도 향상에 대한 한계를 제시한 법칙이다.

직렬 알고리즘 중에서 반드시 순차적으로 처리되어야 하는(= 병렬화 할 수 없는) 부분의 비율을 Amdahl의 비율이라 하는데, 여기서는 a라고 정하고 이야기를 진행한다. 실제 시스템에서 병렬화 할 수 없는 부분에 해당하는 예를 보면, 프로그램이나 데이터를 다운로드 하는 데 걸리는 시간, 운영체제의 커널 실행 시간 혹은 연산들 간에 선행 관계가 존재하는 경우 등등이 있다. a를 고려하면 Tp는 아래와 같은 식이 된다.

그를 통해 속도 향상은 아래 식으로 표현된다.

그런데, 만약 사용 가능한 프로세서의 수가 무한대라고 가정하면, 속도 향상의 비율값은 아래와 같아진다.

즉, 병렬처리를 이용하여 얻을 수 있는 최대 속도 향상은 a값의 역수가 된다. 예를 들어, 계산할 알고리즘 중에 반드시 순차적으로 처리해야 할 부분이 5%가 있다면, 프로세서가 무한히 많이 사용되더라도 속도 향상은 최대 1/0.05 = 20배 밖에 되지 못한다는 것이다. a값이 큰 경우에는 프로세서의 수가 증가하더라도 속도 향상은 거의 얻지 못하고 포화되며, 결과적으로 시스템 효율이 크게 떨어진다는 것을 알 수 있다.

그러나, Amdahl의 법칙도 처리할 응용의 특성에 따라 해석이 달라질 수 있다. 예를 들어, 대부분의 공학 및 과학 계산 문제들에서 Amdahl의 비율 a는 문제의 크기 n에 따라 값이 달라지게 된다. 여기서 문제의 크기란 선형방정식의 차수, 즉 변수의 개수에 해당한다. 그 크기가 증가하면 그만큼 계산량이 증가하게 되는 반면에, 순차적으로 처리되어야 하는 부분은 거의 일정하게 고정되기 때문에 a값은 상대적으로 감소한다. 이렇게 되면 즉.

이 될 수 있다. 따라서 그러한 응용들을 처리하는 경우에 문제의 크기가 무한대로 커진다고 가정하면, 속도 향상은 아래의 식과 같이 정의될 수 있다.

이러한 예는 실제로 미국의 Sandia 국립연구소에서 몇 가지 응용들을 1024개의 프로세서들로 구성된 하이퍼큐브시스템에서 처리한 결과, 거의 선행적인 성능 향상을 얻을 수 있다는 것을 입증하였다. (유명한 논문이다. 단, 작성날짜가….ㅠㅠ J.L.Gustafson, “Reevaluating Amdahl’s Law”, Communications of the ACM, Vol31, No. 5, Maay 1988, pp 532-533)

결론적으로, 큰 응용 문제가 효율적인 방법으로 처리되는 이상적인 경우에는 Amdahl의 법칙과는 달리, 프로세서의 수에 비례하는 선형적인 속도 향상을 얻을 수 있다는 것이다. 여기서 주의해야 할 사항은 문제의 크기가 증가하면 기억장치의 용량도 증가해야 하며, 그렇지 않은 경우에는 속도 향상이 프로세서의 수보다 기억장치 용량에 의해 더 큰 영향을 받을 수 있다는 점이다. 이 부분은 이후에도 지속적으로 설명하겠다.

병렬 컴퓨터의 성능 한계에 대한 초기의 비관적인 내용들을 확인해보았다. 이런 예측들은 응용 문제의 크기가 증가하고 효율적인 통신 매커니즘이 설계되면 충분히 개선될 수 있는 것이 입증되고 있었고, 그에 대해서도 현재 실제로 입증되고 있다. 그래서 최근의 개발된 거의 모든 고성능컴퓨터시스템들은 병렬처리 기술을 사용하고 있다.

병렬처리의 한계와 가능성 – Minsky의 모순성

P개의 프로세서들을 사용한 병렬컴퓨터에서 프로세서들 간의 정보 교환에 따른 통신 오버헤드 때문에 시스템 성능은 P배가 아닌 log2P배까지밖에 개선되지 못한다는 주장이다.

이런 주장은 1960년대에 적은 수의 프로세서들로 구성된 병렬처리시스템들을 이용하여 연구한 결과에 근거한 것이었다. 그러한 시스템들은 근대적인 운영체제를 사용하였는데, 프로세서의 수가 조금만 증가하여도 그에 따른 오버헤드가 매우 높아졌다. 통신 오버헤드가 커지면 프로세서의 이용률이 떨어지고 그것이 성능 저하의 주요 요인이 되었다.

그러나, 이후에 프로세서의 이용률을 높일 수 있는 여러 가지 방법들이 개발되었다. 우선, 더 효율적인 병렬 알고리즘들과 상호연결망들이 개발되었다. 또한 어떤 한 프로그램이 시스템 내의 모든 프로세서들을 이용하지 않는 경우에는, 남아있는 프로세서들이 다른 프로그램들을 처리하도록 해주는 스케쥴링 기술도 개발되었다. 이러한 기술들은 Minsky가 고려하지 못한 것들이었다.

물론, 통신 오버헤드 때문에 P개의 프로세서들을 가진 시스템의 성능이 단일프로세서 시스템 성능의 P배는 되지 못한다. 그러나 최근에 개발되고 있는 시스템들의 성능이 Minsky가 예측한 것보다는 훨씬 더 높아진 것에 대해서는 사실이다. 그로 인해 이 비관적인 예측 또한 어느 정도 빗나가게 되었다.

병렬처리의 한계와 가능성 – Grosch의 법칙과 VLSI

다단일 프로세서의 계산 속도는 물리적인 특성에 의하여 한계점을 가지고 있다. 실리콘 칩 상에 전자 프름의 최대 속도는 및의 속도의 1/10 정도(3*10^7m/sec)이다. 따러서 지름이 3cm인 칩에서 전기 신호의 최대 전송시간은 10^-9sec가 된다. 한 번의 신호 전송에 의해 한 번의 부동소수점 연산이 가능하다고 가정하더라도 그러한 칩으로 만들어진 컴퓨터의 계산 능력은 10^9FLOPS = 1GFLOPS가 된다,. 즉, 초당 10억번의 부동소수점 연산 처리까지만 가능하다. 다만, 최근에는 프로세서 칩 내부에 여러 개의 명령어 실행 유니트들을 두는 슈퍼스칼라 기술을 이용함으로써 프로세서의 처리 속도를 좀 더 높여뒀지만(이미 적용되어 나오는 프로세서들이 대부분임), 명령어-수준 병렬성에 의해 한계를 가진다. 

이러한 물리적인 한계를 극복할 수 있는 기술이 병렬처리이다. 병렬처리 개념이 문헌에 처음 소개된 것은 1920년대였으나, 상당 기간 동안 구체적인 연구와 개발이 이루어지지는 못한 데는 몇 가지 비관적인 예측들이 있었기 때문이다. 이것들을 하나 하나 살펴볼 것이다.

우선 살펴볼 것은 프로세서의 성능이 가격의 제곱에 비례하여 높아질 것으로 예측한 Grosch의 법칙이다. (p.s. 이 이론 자체가 엄청난 고전이지만, 병렬컴퓨터 구조론에 있어서 한번 지나가야 하는 법칙이다.) 이 법칙은 n개의 프로세서들을 사용하면 최대 n배의 성능 향상을 얻을 수 있지만, 루트n배의 비용만 투자해도 n배 더 빠른 프로세서를 개발할 수 있을 것이므로 단일 프로세서의 시스템으로도 같은 수준(n배)의 성능을 얻을 수 있다는 주장이다. 만약 이 법칙이 사실이라면, 굳이 많은 비용을 들여서 다수의 프로세서들을 이용한 병렬처리 시스템을 구성하는 의미가 없어지는 것이다. 그래서 당시의 이러한 논리 때문에, 오래동안 병렬처리 기술은 비용보다도 신뢰도를 더 중요시하는 군사용 또는 항공우주용 컴퓨터에서만 사용되어 왔는데, 병렬처리 구조가 결함 허용을 위한 하드웨어 중복성을 구현하기가 용이하기 때문이었다. (망가져도 다른 코어 쓰면 되니깐)

그러나, VLSI 칩들이 등장하면서 상황이 완전히 바뀌게 되었다. VLSI 기술을 이용하여 개발한 프로세서들은 대량 생산이 가능하기 때문에 가격이 저렴해지고, 따라서 약간의 비용만 추가하면 많은 수의 프로세서들을 포함하는 시스템을 구성할 수 있게 된 것이다. 결과적으로 Grosch의 법칙은 VLSI 기술의 출현과 함께 더 이상 성립되지 않게 된 것이다.

최근에는 가격보다는 처리 속도를 더 중요시하는 경향이 있기 때문에 많은 비용을 투자하면서 더 빠른 프로세서를 개발하고, 그러한 프로세서들을 많이 사용하여 더 높은 성능의 시스템을 개발하고 있다.

병렬 프로그래밍과 시스템 의존성

병렬 프로그래밍을 하면서 대부분 일단 배우는 것이 쓰레드를 잘 이용하는 방법부터 시작하여 하나하나 공부해 나갑니다. 저도 그런 수업을 들어왔고요. 그러다 여러 병렬 프로그래밍 환경을 살펴보면 은근 의존성을 겪게 됩니다.

실제로 프로그래밍을 병렬화 작업을 진행하려면 해당 라이브러리를 이용하여 작성하는 경우가 많은데, 이 라이브러리들이 시스템 의존적인 라이브러리가 있고 그렇지 않은 라이브러리가 있습니다. 대표적으로 엔비디아틔 쿠다(CUDA)와 같이 프로세서 제조사에서 지원하는 라이브러리를 이용해야 하는 경우도 있고, 라이브러리에서 좀 더 자율적으로 처리해주는 OpenCL, MPI와 같은 경우도 있습니다. 또한 인텔처럼 자사의 페러럴 스튜디오를 통한 병렬 프로그래밍을 좀 더 최적화하여 진행하도록 컴파일러도 제공합니다. 언어 레벨에서 지원하는 라이브러리도 늘고 있어서 병렬화 작업이 많이 친숙하게 다가오고 있는 것은 확실합니다.

일단 이런 식으로 살펴는 봤는데, 그럼 이것들의 경우에는 직접 제어하는 프로세서, 시스템에 얼마나 의존적일수록 좋은 것일까라는 생각을 해봅니다만…

가장 단순한 사고를 해본다면, 저수준의 병렬화를 쉽게 구현할 수 있다면 확실히 빠릅니다. 라이브러리 함수 콜(API 함수 사용)을 통해 진행되는 병렬화에 의존하지 말고 직접 pthread를 능숙하게 제어하는 쪽이 퍼포먼스는 확실히 빠르게 나옵니다. 단, 저수준의 병렬화를 진행한다는 것은 정말 어려운 작업입니다.

그러나, 그렇게 하더라도 병렬화 작업을 위한 멀티코어 혹은 매니코어 제조사에서 제공하는 최적화된 API 라이브러리를 이길 수는 없습니다. 엔비디아의 쿠다만 하더라도, 쿠다에 최적화된 코딩 스타일이 존재합니다. (쿠다 프로그램밍을 해보시면 이게 어떤 뜻인지 대충은 아실 겁니다.) 인텔의 경우에도 CPU의 병렬화만 진행하더라도 패러럴 스튜디오에서의 병렬 최적화 튜닝 기법을 이용하면 일반 프로그램에서 쓰레드 제어를 잘 하는 것보다 훨씬 빠른 병렬화가 가능합니다.

이런 식으로 본다면 시스템에 상당히 의존적인 부분이 크게 느껴질 겁니다. 하지만, 응용 프로그램을 개발하는 사람들의 입장에서 보면 자신이 혹은 사용자가 어떤 시스템을 이용할 지 모르는데 시스템 의존적인 병렬 프로그래밍을 할 수는 없습니다. 상각해 보죠. 자신이 프로그램을 개발해야 하는 입장에서라면..? 여러 곳에서 이용하고 싶기 때문에 의존성이 최대한 낮은 쪽을 이용해야 여러 플랫폼, 여러 시스템에 상관 없이 이용할 수 있을 것입니다. (JVM 상에서 멀티쓰레드 제어에 침흘리는 사람들이 이런 입장일 겁니다.)

하지만 실제로 의존성을 무시할 수는 없습니다. 의존성에 의한 퍼포먼스 증가는 무시할 수 없는 수준입니다. 과학 실험을 위한 프로그램들은 대다수가 특정한 HPC를 정해놓고 이용하기 때문에 의존적인 프로그램으로 실험을 하여도 아무 문제가 없습니다. 과거에 제가 개발했던 열역학 시뮴레이터도 그랬고요. 하지만 응용 프로그램을 개발하는 입장에서 병렬 프로그램을 이용해야 한다면 특정한 시스템에 의존성을 가져야 되는지 아닌지에 대해서는 여러모로 고민하게 될 거 같습니다만, OpenCL이나 MPI를 보다보면 어느 정도의 해답은 있을 것 같습니다.

빠른 컴퓨터에 대한 개발을 위하여…

빠른 컴퓨터에 대한 요구는 지금도, 앞으로도 지속적으로 있을 것으로 보여진다.

컴퓨터의 속도를 결정하는 첫 번째 요소는 다들 알듯 프로세서의 속도가 향상되는 것이다. 그러나, 그것이 한계에 부딧히게 되어 멀티코어를 만들고 병렬처리 기술들을 지속적으로 하고 있다.

병렬 처리란 다수의 프로세서들이 여러 개의 프로그램들 혹은 한 프로그램의 분할된 부분을 분담하여 동시에 처리하는 기술을 말한다. 병렬처리가 가능해지기 위해서는 다음과 같은 조건들을 만족해야 한다.

  1. 많은 수의 프로세서들을 이용하여 하나의 시스템을 구성할 수 있도록, 작고 저렴하며 고속인 프로세서들의 사용이 가능해야 함
  2. 한 프로그램을 여러 개의 작은 부분들로 분할하는 것이 간으해야 하며, 분할된 부분들을 병렬로 처리한 결과가 전체 프로그램을 순차적으로 처리한 경우와 동일한 결과를 얻을 수 있어야 함

첫 번째 조건은 최근 반도체 및 프로세서 기술의 발전으로 만족을 하고 있는 상황이다. 그러나 두 번째 조건은 다음과 같은 새로운 과제들을 야기시킨다.

  • 분할성
  • 복잡성
  • 프로세서간 통신

분할성이란 병렬 처리를 위하여 하나의 프로그램을 여러 개로 나눌 수 있어야 ㅎ나다는 것이다. 프로그램들 중에는 반드시 순차적으로 처리되어야 하는 것들도 있기 때문에 병렬 처리가 근본적으로 불가능할 수도 있다. 또한 많은 수의 프로세서들이 제공되더라도 프로그램을 그 수만큼 분할할 수 없는 경우에는 프로세서 이용률이 낮아져서 원하는 만큼의 성능 향상을 얻을 수 없기 때문에 분할성은 매우 중요하다. 이러한 경우에 병렬처리의 효과를 높이기 위해서는 순차적으로 처리해야 하는 부분을 최소화할 수 있는 새로운 알고리즘 개발이 필요하게 되고, 알고리즘 자체가 복잡해질 수도 있다. 이 부분이 복잡성에 해당한다.

하나의 프로그램이 여러 개의 작은 부분들로 나누어져 서로 다른 프로세서들에 의해 처리되는 경우에는 프로세서들 간에 데이터 교환을 위한 통신이 필요하게 된다. 프로세서의 수가 증가하면 통신 선로의 수도 그만큼 더 많아지고, 인터페이스를 위한 하드웨어도 복잡해진다. 그에 따라 프로세서간 통신을 지원하기 위한 소프트웨어 오버헤드와 하드웨어상 지연 시간 때문에 통신에 소모되는 시간이 길어져서 속도 향상에도 한계가 있게 된다.

이 밖에도 알고리즘의 병렬성을 표현해줄 수 있는 병렬 프로그램 언어와 컴파일러의 개발도 요구된다. 또한 프로세서들이 각종 하드웨어 자원들을 공유할 수 있도록 상호 베타 매너키즘이 설계되어야 하고, 공유자원들에 대한 경합을 줄이고, 이용률을 극대화시킬 수 있도록 운영체제에서도 특별한 기능이 추가되어야 한다.

이 내용들을 전부 정리해보면 병렬컴퓨터 시스템을 개발하기 위해서는 하드웨어의 구조, 운영체제, 알고리즘, 프로그래밍 언어, 컴파일러 등 거의 모든 분야의 컴퓨터 기술들이 통합되어야 한다.

p.s. 진짜 진짜 관심 있는 분야이긴 한데…./먼산