지난 글들에서 우리는 AI에서 인공 뉴런의 기본 연산인 MAC(Multiply-Accumulate)이 하드웨어에서 얼마나 비싼 cost을 치르는지, 그리고 Activation function(ReLU)이 어떻게 그 비용을 절감해 주는지 살펴보았습니다.
이번 글에서는 시야를 조금 더 넓혀, 이 수많은 뉴런이 모여서 만드는 거대한 연산, 즉 행렬곱(Matrix Multiplication, MatMul)에 대해 알아보겠습니다.
딥러닝의 역사는 곧 “더 거대한 행렬을 더 빠르게 곱하기 위한 투쟁의 역사”와 같습니다. 그리고 이 투쟁 과정에서 수십 년간 컴퓨터의 두뇌 역할을 해왔던 CPU(Central Processing Unit)는 왕좌에서 내려오고, GPU(Graphics Processing Unit)와 NPU(Neural Processing Unit)가 새로운 주인으로 등극했습니다.
도대체 딥러닝 모델 내부에서는 무슨 일이 일어나기에 범용적인 CPU가 힘을 쓰지 못하는 것일까요? 그 비밀은 ‘병렬성(Parallelism)’과 ‘컴퓨터 아키텍처의 설계 철학’에 숨어 있습니다.
1. 딥러닝의 본질: 거대한 행렬 연산
우리가 다루는 딥러닝 모델은 거대한 행렬 연산기입니다.
- Fully Connected Layer (MLP): 입력 벡터와 가중치 행렬의 곱셈입니다 (Y = WX + B).
- Convolution Layer (CNN): 이미지의 픽셀들과 필터 커널의 곱셈이지만, 이 역시 수학적으로는 거대한 행렬곱(Toeplitz Matrix 변환)으로 환원됩니다.
- Attention Mechanism (Transformer): Query, Key, Value 행렬들 간의 연속적인 곱셈(Q * KT * V)입니다.
통계적으로 딥러닝 추론(Inference) 및 학습(Training) 시간의 90% 이상이 바로 이 행렬곱(MatMul)과 그에 수반되는 데이터 이동에 소모됩니다. 따라서 AI 하드웨어의 성능은 “얼마나 많은 행렬곱을 동시에 처리할 수 있는가?”로 귀결됩니다.
문제는 기존의 CPU가 이런 형태의 작업을 처리하도록 설계되지 않았다는 점입니다.
2. CPU 아키텍처의 한계: SISD
CPU는 컴퓨터의 만능 일꾼입니다. 운영체제를 돌리고, 웹 서핑을 하고, 엑셀 계산도 해야 합니다. 즉, 예측 불가능하고 복잡한 작업을 빠르게 처리하는 것이 목표입니다. 이를 위해 CPU는 소수의 아주 강력한 코어(Core)를 가집니다. 각 코어는 복잡한 분기 예측(Branch Prediction), 비순차 실행(Out-of-Order Execution), 그리고 거대한 캐시 메모리(Large Cache)로 무장하고 있습니다.
이 구조는 SISD (Single Instruction, Single Data) 방식에 최적화되어 있습니다. 한 번에 하나의 명령어로 하나의 데이터를 처리하는 방식이죠. CPU 코어의 면적 중 실제로 덧셈/곱셈을 하는 ALU(Arithmetic Logic Unit)가 차지하는 비율은 의외로 작습니다. 대부분의 면적은 다음에 무슨 명령을 실행할지 고민하고 데이터를 퍼 나르는 제어 로직(Control Logic)이 차지합니다.
Architect’s Insight:
딥러닝의 행렬곱은 매우 단순하고 반복적인 작업입니다. (A1 * B1, A2 * B2, …). 분기(if문)도 거의 없고, 다음에 수행할 작업이 너무나 뻔합니다. CPU의 똑똑한 제어 유닛들은 행렬곱을 하는 동안 대부분 할 일이 없어 놀게 됩니다. 엄청난 실리콘 면적과 전력의 낭비죠.
3. GPU의 혁명: SIMD
반면, GPU는 태생부터 다릅니다. GPU는 원래 3D 그래픽을 렌더링하기 위해 탄생했습니다. 화면의 수백만 개 픽셀 각각에 대해 좌표를 계산하고 색상을 입히는 작업은, 서로 독립적이면서 동일한 연산을 반복하는 과정입니다. 이런 작업을 위해 GPU는 SIMD (Single Instruction, Multiple Data) 아키텍처를 채택했습니다.
SIMD는 하나의 명령어로 여러 개의 데이터를 동시에 처리하는 방식입니다. CPU처럼 코어마다 복잡한 제어 유닛(Control Unit)을 두는 대신, GPU는 하나의 명령 유닛이 수십, 수백 개의 단순한 ALU(이를 CUDA 코어 등으로 부름)들에게 동시에 명령을 내립니다.
- CPU: “1번 코어는 A를 하고, 2번 코어는 B를 하고…” (개별 지시)
- GPU (SIMD): “자, 여기 있는 1000개의 코어들아, 지금부터 동시에 ‘곱셈’을 시작해!” (단체 지시)
명령어를 해석하고 스케줄링하는 오버헤드(Instruction Fetch/Decode Cost)를 수많은 ALU가 나누어 부담하기 때문에 효율성이 극대화됩니다. 이 구조가 우연히도 딥러닝의 행렬곱 연산과 완벽하게 맞아떨어진 것입니다.
4. GPU: SIMT로 확장(참고)
GPU는 SIMD(Single Instruction, Multiple Data) 방식으로 동일 명령을 여러 데이터에 동시에 적용해 벡터 연산을 가속합니다. 그러나 GPU는 단순 SIMD를 넘어 SIMT(Single Instruction, Multiple Threads) 모델도 사용합니다.
SIMT는 하나의 명령어를 여러 스레드에 동시에 실행하지만, 각 스레드는 자신만의 레지스터와 프로그램 카운터를 갖기 때문에 조건문이나 분기(divergence) 처리가 가능합니다. GPU는 스레드를 워프(warp) 단위로 묶어 실행하며, 동일 명령을 병렬로 수행하되 분기 발생 시 마스크 기반 실행으로 제어합니다. 이 방식은 대규모 행렬 연산(MatMul)에서 데이터 병렬성을 극대화하면서도 유연한 제어 흐름을 제공하는 것이 특징입니다.
5. NPU: Systolic Array로 진화
GPU가 딥러닝 시대를 열었지만, GPU조차도 완벽한 AI 전용 하드웨어는 아닙니다. GPU에는 여전히 그래픽 처리를 위한 텍스처 유닛, 레스터라이저 같은 딥러닝에 불필요한 기능들이 포함되어 있습니다.
AI에 100% 올인하기 위해 탄생한 NPU(Neural Processing Unit)는 GPU의 SIMD 개념을 넘어, 행렬곱에 극한으로 최적화된 구조를 도입합니다. 대표적인 것이 구글 TPU의 Systolic Array입니다. Systolic Array는 데이터가 심장 박동(Systolic)처럼 규칙적으로 배열 형태의 ALU들을 통과하며 연산되는 구조입니다.
- GPU: 데이터를 메모리(레지스터)에서 가져와 연산하고 다시 넣습니다. 수많은 ALU가 동시에 메모리에 접근하려 하므로 메모리 병목이 발생하기 쉽습니다.
- NPU (Systolic Array): 입력 데이터(행렬의 행)가 ALU 배열의 왼쪽에서 들어가고, 가중치 데이터가 위쪽에서 흘러들어옵니다. 데이터가 배열 내부를 통과하면서 옆 칸, 아래 칸의 ALU로 전달되며 연속적으로 곱셈과 덧셈이 일어납니다.
이 구조는 데이터의 재사용(Data Reuse)을 극대화하여, 딥러닝 하드웨어의 최대 적인 메모리 접근을 최소화합니다. 이것이 NPU가 GPU보다 전력 효율(Performance/Watt)이 뛰어난 결정적인 이유입니다.
6. 결론: Latency에서 Throughput
CPU는 지연 시간(Latency)을 최소화하도록 설계되었습니다. 복잡한 작업 하나를 얼마나 빨리 끝내느냐가 중요했습니다.
하지만 딥러닝의 시대는 처리량(Throughput)이 지배합니다. 개별 연산 하나하나는 조금 느리더라도, 수백만 개의 연산을 동시에 처리하여 단위 시간당 전체 처리량을 높이는 것이 핵심입니다.
이 거대한 병렬 처리의 요구사항이 SIMD라는 날개를 달고 GPU를 AI의 중심으로 이끌었으며, 이제는 행렬곱에 더 특화된 아키텍처로 무장한 NPU들이 그 자리를 이어받고 있습니다.
다음 글에서는 이러한 하드웨어 발전에도 불구하고 여전히 해결되지 않는 난제, 학습(Training)과 추론(Inference)이 NPU 설계에 미치는 결정적인 차이에 대해 알아보겠습니다.
참고: In-Datacenter Performance Analysis of a Tensor Processing Unit