PyTorch 팀의 연구원은 딥러닝 모델의 성능 최적화를 위해 연산(Compute), 메모리 대역폭(Memory Bandwidth), 오버헤드(Overhead)라는 세 가지 핵심 구성 요소를 분석했다. 현대의 GPU는 행렬 곱셈(Matmul)을 처리하는 텐서 코어(Tensor Cores)를 통해 이론적으로 312테라플롭스(TFLOPS)라는 압도적인 연산 능력을 제공하지만, 실제 모델 실행 시 이 성능을 온전히 활용하는 경우는 드물다. 이는 연산 능력의 성장 속도가 데이터를 공급하는 메모리 대역폭의 성장 속도를 훨씬 앞질렀기 때문이다.
성능 최적화의 핵심은 현재 시스템이 어떤 '레짐(Regime, 상태)'에 놓여 있는지를 파악하는 것이다. 예를 들어, 데이터 전송 시간이 전체 실행 시간을 지배하는 '메모리 대역폭 제한(Memory-bandwidth bound)' 상태에서는 GPU의 연산 성능(FLOPS)을 높여도 아무런 효과가 없다. 반면, 거대한 행렬 곱셈이 주를 이루는 '연산 제한(Compute-bound)' 상태에서는 코드 최적화만으로는 성능을 올리기 어렵다. 결국 GPU가 쉬지 않고 일하게 만들려면 연산 자체를 줄이는 것이 아니라, 연산 장치로 데이터를 나르는 과정에서 발생하는 병목을 제거해야 한다.
핵심 변화
딥러닝 시스템의 효율성은 연산(compute), 메모리 대역폭(memory bandwidth), 오버헤드(overhead)라는 세 가지 구성 요소로 나뉜다. 쉽게 말하면 시스템이 현재 어떤 영역(regime)에 머물러 있는지 정확히 파악해야만 유효한 최적화가 가능하다는 뜻이다. 비유하자면 도로 정체의 원인이 차량의 절대적인 수 때문인지, 아니면 톨게이트의 처리 속도 때문인지를 먼저 구분하는 것과 같다. 메모리 대역폭이 제한된 상황에서는 GPU(그래픽 처리 장치)의 FLOPS(초당 부동 소수점 연산 횟수)를 높이는 것이 아무런 도움이 되지 않으며, 반대로 연산 자체가 병목인 상황에서는 C++(범용 프로그래밍 언어)로 로직을 재작성해 오버헤드를 줄이는 작업이 실질적인 효과를 거두기 어렵다.
현대 머신러닝 가속기는 Nvidia의 Tensor Cores(텐서 코어)와 같이 행렬 곱셈(matrix multiplication)에 특화된 하드웨어를 사용한다. 하드웨어가 명시한 최대 성능을 온전히 끌어내기 위해서는 반드시 행렬 곱셈 연산을 수행해야 한다. 예를 들어 312 teraflops(테라플롭스)의 성능을 가진 장치라 하더라도 행렬 곱셈이 아닌 연산을 수행하면 성능이 19.5 teraflops 수준으로 급격하게 떨어진다. 이러한 특성은 GPU뿐만 아니라 TPU(텐서 처리 장치)에서도 동일하게 나타나며, TPU는 GPU보다 훨씬 덜 범용적인 구조를 가지고 있다.
행렬 곱셈이 아닌 연산들이 실제 실행 시간에서 많은 비중을 차지하는 근본적인 이유는 메모리 대역폭 비용 때문이다. 비유하자면 데이터가 저장된 DRAM(동적 랜덤 액세스 메모리)은 땅값이 저렴해 넓은 공간을 확보할 수 있는 창고이고, 실제 연산이 이루어지는 SRAM(정적 랜덤 액세스 메모리)은 제품을 생산하는 공장이다. 창고에서 공장으로 재료를 운송하는 과정에서 발생하는 메모리 대역폭 비용이 실제 연산 자체에 드는 비용보다 훨씬 크기 때문에 병목이 발생한다. 결국 연산 장치의 속도보다 데이터를 옮기는 운송 속도가 전체 시스템의 효율을 결정짓는 핵심 요인이 된다.
기존과의 차이
하드웨어가 낼 수 있는 최대 성능을 온전히 쓰려면 연산 제한(compute-bound) 영역에서의 시간을 최대화해야 한다. 쉽게 말하면 GPU(그래픽 처리 장치)가 제공하는 최대 FLOPS(초당 부동 소수점 연산 횟수)를 모두 활용하기 위해 오버헤드나 메모리 비용 같은 부수적인 소요 시간을 줄이는 작업이다. 비유하자면 최고 속도로 달릴 수 있는 슈퍼카 엔진을 가지고 있지만, 정작 가속 페달을 밟기 전의 준비 과정이나 기어 변속 시간이 너무 길어 도로 위에서 제 속도를 내지 못하는 상황을 해결하는 것과 같다. 딥러닝에서 수행하는 작업의 성격상 연산량 자체는 연산 방식을 완전히 바꾸지 않는 한 줄일 수 없기에, 연산 외의 영역에서 낭비되는 시간을 걷어내는 것이 최적화의 핵심이다.
최근에는 연산 능력의 성장 속도가 메모리 대역폭(데이터 전송 통로의 넓이)의 성장 속도보다 훨씬 빠르게 증가하고 있어 상황이 더 까다롭다. 비유하자면 공장의 기계 설비 효율은 비약적으로 높아져 제품을 순식간에 찍어낼 수 있는데, 정작 원자재를 실어 나르는 트럭의 속도나 도로의 넓이는 그대로인 상황이다. 원자재 공급 속도가 생산 속도를 따라가지 못하면 아무리 좋은 기계라도 놀 수밖에 없으며, 이로 인해 하드웨어의 최대 효율을 달성하는 일은 점점 더 어려워지고 있다. ML(머신러닝) 시스템 엔지니어에게 데이터가 흐르는 길목 어디에서 병목 현상이 발생하는지 정확히 이해하고 관리하는 능력이 더욱 중요해진 이유다.
실제로 BERT(구글의 언어 모델)의 연산 구조를 살펴보면 행렬 곱셈인 텐서 수축(Tensor Contraction)을 제외한 나머지 연산이 차지하는 FLOPS 비중은 0.2%에 불과하다는 사실을 알 수 있다. 정규화나 포인트와이즈 연산 같은 작업들은 전체 연산량에서 매우 적은 비중을 차지하는 부수적인 단계들이다. 쉽게 말하면 GPU가 이러한 비-행렬 곱셈 연산을 처리할 때 행렬 곱셈보다 15배나 더 느리게 작동하더라도, 전체 FLOPS 관점에서 보면 그 영향이 미미해 큰 문제가 되지 않는다는 뜻이다. 결국 딥러닝 시스템 최적화의 성패는 압도적인 비중을 차지하는 행렬 곱셈 연산 시간을 얼마나 확보하고 효율적으로 운영하느냐에 달려 있다.



