[Mini-NPU RTL] TPU (Study Paper)
"In-Datacenter Performance Analysis of a Tensor Processing Unit"
1. Introduction to Neural Networks
NN은 입력 값들의 가중 합에 비선형 함수(ex. max(0, value))를 적용하는 단순한 인공 뉴런에 기초한다.
※ 가중합 : 입력 데이터에 중요도인 가중치를 곱해서 더한다.
※ 비선형 함수 : 계산된 합을 그대로 쓰지 않고 변형하는 함수(활성화 함수)
※ max(0, value) : ReLU(Rectified Linear Unit) 함수이며, 음수면 0, 양수면 해당 값을 그대로 내보낸다.
NN은 계층(Layer)으로 모이며, 한 계층의 출력은 다음 계층의 입력이 된다.
DNN(Deep Neural Networks)은 NN에서 계층의 수가 많은 것을 의미한다.
NN은 개발 단계인 Training(학습) 단계와 실전 단계인 Inference(추론) 단계가 있다.
개발자는 계층의 수와 NN의 유형을 선택하고, 학습 과정을 통해 가중치를 결정한다.
학습이 끝난 후, 추론 단계에서는 소수점 아래 긴 자리까지 필요 없는 경우가 많다.
∴ Quantization(양자화) 단계에서 단순한 정수(8-bit)로 바꿔 용량↓ & 속도↑
→ 8-bit 정수 곱셈 : 16-bit 부동소수점 곱셈보다 에너지 소모 6배↓ & 칩 면적 6배↓
→ 8-bit 정수 덧셈 : 16-bit 부동소수점 덧셈보다 에너지 소모 13배↓ & 칩 면적 38배↓
NN의 유형은 3가지로 볼 수 있다.
· MLP(Multi-Layer Perceptrons) : 각 계층은 이전 계층의 모든 출력 값(Fully Connected)들의 가중 합에 비선형 함수를 적용
· CNN(Convolutional NN) : 각 계층은 이전 계층 출력 중 공간적으로 인접한 일부 영역에 대해, 서로 다른 좌표에서 가중 합을 구하고 비선형 함수를 적용
→ 이미지의 전체를 한 번에 보는 것이 아니라 일부분씩 처리
→ 공간적으로 가중치 재사용 가능
· RNN(Recurrent NN) : 각 계층은 출력 값 뿐만 아니라 이전 상태의 가중 합에 비선형 함수를 적용
→ 시간적으로 가중치 재사용 가능
→ LSTM(Long Short Term Memory) : 시간이 지나면 과거의 기억을 잊어버리는 기본 RNN의 문제를 보완
다음은 Table 1을 바탕으로 구글 데이터 센터의 95%를 차지하는 핵심 모델을 테스트 하여 얻은 결과이다.
· 데이터 센터에서 사용하는 위 3가지 유형의 NN은 일반적으로 TensorFlow로 작성되어 코드가 100~1500줄 정도로 매우 짧다.
· 추론 앱들은 주로 사용자 대면 서비스이기 때문에 처리량보다는 응답 시간을 더 중요시한다.
· GPU는 큰 배치 사이즈로 인해 많은 데이터를 모아서 한 번에 처리할 때 최대의 성능을 뽑을 수 있다.
→ But, 사용자 대면 서비스는 응답 시간을 더 중요시하므로 데이터를 모을 시간이 없어 작은 단위로 처리한다.
∴ GPU는 CPU와 성능 차이가 크지 않다.
· TPU는 연산 속도에 비해 메모리에서 데이터를 가져오는 속도가 느려 성능이 억제되어 있다.
→ K80 GPU와 같은 수준의 고성능 메모리를 갖추도록 수정되면 성능이 매우 향상될 것이다.
· TPU는 더 작고 전력을 덜 소모하는 칩임에도 불구하고, K80 GPU보다 25배 많은 MAC과 3.5배 많은 온칩 메모리를 갖고 있다.
· TPU의 와트당 성능은 CPU 또는 GPU보다 30~80배 높다. (K80 GPU 수준의 메모리를 갖추면 70~200배 더 좋아질 것이다.)
2. TPU Origin, Architecture, Implementation, and Software
배경
2013년 이전에는 AI 작업이 많지 않아 남는 CPU로 작업을 처리
But, 2013년 이후에는 데이터 센터의 연산 수요가 늘어 연산량을 기존 CPU로 감당 불가
→ 추론을 위한 맞춤형 ASIC(=TPU) 개발에 집중 & 학습 단계는 기성품 GPU를 사용
※ FPGA : 프로그래밍 가능한 반도체로 수정이 가능하지만 ASIC보다 느리다.
※ ASIC(Application Specific Integrated Circuit) : 특정 목적만을 위해 설계된 주문형 반도체(as like TPU)
구현 방식 및 연결
TPU는 CPU와 통합되지 않고 PCIe I/O Bus 상의 보조 프로세서로 설계되었다.
→ 빈 슬롯에 끼우기만 하면 되므로 데이터 센터 업그레이드가 빠르고 쉽다.
※ PCIe(Peripheral Component Interconnect Express) : 표준 직렬 버스로 슬롯에 꽂는 방식
TPU는 스스로 메모리에서 명령어를 가져오는 CPU와 달리 host 서버로부터 실행할 명령어를 전달받는다.
→ 칩 내부 구조가 단순해진다.
Architecture
CPU와 데이터를 주고받는 통신(PCIe)은 속도가 느리다.
∴ 모델 전체를 TPU에 올려두고 한 번에 끝까지 계산하게 하여 통신 횟수를 최소화하는 것이 목표
→ 계산 많은 작업엔 TPU >>>>> GPU, CPU & 계산보다 데이터 이동이 많은 작업인 경우 TPU의 상대적 이점↓
① 명령어 처리 : Host CPU → PCIe Bus → Host Interface → Control Unit
TPU 명령어는 host로부터 PCIe Gen3 x16 Bus를 통해 명령어 Buffer로 전송된다.
내부 블록들은 일반적으로 256Bytes 너비의 경로로 연결되어 있다.
② Weight Flow : DDR3 DRAM → Weight FIFO → MMU
딥러닝 모델의 parameter인 가중치는 칩 외부인 DDR3 DRAM에 저장되어 있다.
연산이 필요할 때 DDR3 Interface를 통해 읽어와 칩 내부의 Weight FIFO에 임시 저장한다.
이후 MMU의 위쪽으로 전달되어 연산에 사용된다.
③ Activation(입력 데이터) Flow : Host/Unified Buffer → Systolic Data Setup → MMU
입력 데이터는 host에서 Unified Buffer로 먼저 이동한다.
연산 시, Buffer에 있던 데이터는 Systolic Data Setup 블록을 거쳐 MMU의 왼쪽으로 들어간다.
이때, Systolic Array 구조 특성에 맞춰 데이터가 정확한 타이밍에 들어가도록 정렬된다.
④ Computation : 위에서 내려오는 가중치와 왼쪽에서 들어오는 데이터가 256x256개의 MAC Unit에서 교차하며 연산된다.
⑤ Accumulation : 연산된 부분합은 MMU 아래의 누산기에서 수집된다.
8-bit끼리 곱하면 최대 16-bit, 16-bit의 연산 결과를 계속 더하면 숫자가 커지면서 Overflow 발생 가능
∴ Overflow를 방지하기 위해 16-bit의 연산 결과들을 MMU 아래에 있는 4MiB크기의 32-bit 누산기에 수집
※ 4MiB 용량은 4,096개의 행을 가지며, 각 행은 256개의 원소로 된 32-bit 누산기들로 구성된다.
이후 Activation Unit을 통과하며 ReLU 또는 Sigmoid와 같은 비선형 함수가 적용된다.
필요에 따라 Pooling 또는 정규화 과정을 거친다.
⑥ Loopback or Output : Normalize/Pool → Unified Buffer → Host Memory
최종 처리된 결과는 167GiB/s의 빠른 대역폭을 가진 Bus를 통해 다시 Unified Buffer로 돌아간다.
다음 Layer 연산의 입력으로 재사용되거나, 최종 결과라면 PCIe Bus를 통해 다시 Host CPU로 전송된다.
Matrix Multiply Unit은 256x256개의 MAC의 집합이며 부호가 있거나 없는 8-bit 정수에 대해 곱하고 더하는 연산을 수행한다.
※ MAC : a * b + c를 수행하는 연산기. 행렬 곱셈의 기초
※ 256x256 : 총 65,536개의 연산기가 동시에 작동(CPU는 기껏해야 수십 개 수준). 이것이 Systolic Array 구조
MMU은 clock cycle 당 하나의 256개 원소 부분 합을 생성한다.
→ clock cycle 당 256개의 값을 읽고 쓸 수 있으며, 행렬 곱셈이나 합성곱 연산을 수행할 수 있다.
Memory Architecture
8-bit 가중치와 16-bit 입력 값을 연산하거나 그 반대의 경우, MMU은 절반의 속도로, 모두 16-bit인 경우 1/4 속도로 연산한다.
→ 즉, TPU는 기본적으로 8-bit 정수 연산에 최적화되어 있다.
MMU은 64KiB 크기의 가중치 타일 하나와, Double Buffering을 위한 또 다른 타일 하나를 보유한다.
※ 64KiB = 65,536Bytes = 256 * 256 * 1Byte
※ Double Buffering : 현재 타일을 계산하는 동안, 다음 타일을 미리 로딩해 MMU가 멈추지 않고 계속 작동하도록 하는 기술
MMU은 개발 시간을 단축하기 위해 Dense 행렬만 지원하고 Sparse 행렬은 지원하지 않는다.
※ Dense : 행렬 안에 0이 아닌 숫자가 꽉 차 있는 경우
※ Sparse : 행렬 대부분이 0인 경우 (Sparse 행렬을 지원하면 0인 계산을 건너뛰어 효율적이지만, 하드웨어 설계가 매우 복잡)
MMU을 위한 가중치는 Weight Memory라고 불리는 칩 외부의 8GiB DRAM에서 읽어와 칩 내부의 Weight FIFO를 통해 단계적으로 전달된다. (추론용이므로 가중치는 읽기 전용이며, 8GiB 용량은 동시에 여러 활성 모델을 지원한다.)
Weight FIFO의 깊이는 4개의 타일 분량으로 미리 4개의 타일을 대기시켜 연산기가 끊김 없이 데이터를 공급 받을 수 있다.
※ Weight Memory(DDR3 DRAM) : 모델의 Parameter를 저장하는 거대한 저장소
※ Weight FIFO : 저장소에서 MMU로 전달하기 전에 잠시 대기하는 곳
중간 계산 결과들은 24MiB 크기의 칩 내부 Unified Buffer에 저장되며, 이 Buffer는 다시 MMU의 입력으로 사용될 수 있다.
→ 일반 CPU의 Cache 역할을 하는 곳으로 이미지나 음성 같은 입력 데이터가 여기에 저장된다.
프로그래밍 가능한 DMA 컨트롤러가 host CPU 메모리와 통합 Buffer 간의 데이터 전송을 담당한다.
→ CPU가 일일이 데이터를 옮기면 느리므로, DMA가 CPU 방해 없이 데이터를 칩으로 전달한다.
※ DMA : Direct Memory Access
Physical Design
→ TPU는 실제 계산과 데이터 저장이 면적의 대부분을 차지한다.
24MiB 크기는 MMU과의 Pitch Matching + 컴파일러 단순화를 위해 선택되었다.
※ Pitch Matching : 반도체 설계 시 인접한 블록끼리 배선 간격을 맞춰 낭비 없이 깔끔하게 연결
Instruction Set
명령어들이 상대적으로 느린 PCIe Bus를 통해 전송되기 때문에, TPU 명령어는 CISC 명령어 체계를 따른다.
→ RISC(Reduced Instruction Set Computer) : "1번 더해" * 100 (1 clock)
→ CISC(Complex Instruction Set Computer) : "이거 100번 더해" * 1 (10~20 clock)
∴ 명령어 하나가 주어지면 칩이 10~20 cycle 동안 알아서 동작하므로 host가 명령을 자주 줄 필요가 없다.
약 12가지의 명령어 중 다음 5가지 명령어가 핵심이다.
1) Read_Host_Memory : CPU host 메모리에서 데이터를 읽어와 Unified Buffer에 저장된다.
2) Read_Weights : Weight Memory에서 가중치를 읽어와 MMU의 입력이 되는 Weight FIFO에 저장된다.
3) MatrixMultiply/Convolve : MMU가 Unified Buffer의 데이터를 이용해 연산을 수행하고 결과를 누산기에 저장하게 한다.
4) Activate : 인공 뉴런의 비선형 함수를 수행하며, 옵션으로 ReLU, Sigmoid 등을 선택할 수 있다.
→ 명령어의 입력은 누산기(계산 결과)이고, 출력은 다시 Unified Buffer로 저장된다.
→ 비선형 함수 Logic에 연결된 전용 하드웨어를 사용하여 합성곱에 필요한 Pooling 연산도 수행할 수 있다.
5) Write_Host_Memory : Unified Buffer에 있는 데이터를 CPU host 메모리로 쓴다.(결과 반환)
나머지 명령어들은 보통 메모리 R/W, 환경 설정, 2가지 버전의 동기화, Host Interrupt, Debug Tag, NOP, Halt이다.
12Byte의 CISC 명령어 중 3Byte는 Unified Buffer 주소, 2Byte는 누산기 주소, 4Byte는 길이, 나머지는 Opcode와 플래그이다.
Pipeline
CISC 명령어에 대해 Pipeline을 사용하여 TPU의 Idle Time을 없애는 것이 성능 최적화의 핵심이다.
NN은 한 계층의 활성화 값 계산이 완료되어야만 다음 계층의 행렬 곱셈을 시작할 수 있는 순차적 의존성이 있다.
→ 이때, Unified Buffer에서 안전하게 데이터를 읽기 위해 명시적인 동기화를 기다리는 RAW Hazard를 목격하게 된다.
※ RAW(Read After Write) Hazard : 이전 단계가 완료될 때까지 데이터 읽기를 멈추는(Stall) 현상
Systolic Array
대형 SRAM(메모리)을 읽는 것이 산술 연산보다 더 많은 전력을 소모한다.
∴ MMU은 Unified Buffer에 대한 R/W 횟수를 줄여 에너지를 절약하고자 Systolic Execution 방식을 사용한다.
→ WS(Weight Stationary) 방식으로 가중치를 PE(Processing Element) 내부의 레지스터에 미리 Load되어 고정된다.
※ Systolic : 심장 박동처럼 데이터를 한 번 가져오면, 연산기들끼리 데이터를 넘겨주며 재활용하여 메모리 접근을 최소화
데이터는 왼쪽에서, 가중치는 위쪽에서 로드되는 것을 보여주며 데이터가 파도처럼 대각선 방향으로 순차적으로 계산된다.
Software Stack
TPU 소프트 웨어 스택은 CPU 및 GPU용으로 개발된 스택들과 호환되도록 만들어져 application 이식이 빠르다.
GPU와 마찬가지로 Kernel Driver와 User Space Driver로 나뉜다.
※ Kernel : 최대한 단순하고 가볍고 안정적이며 메모리 관리와 인터럽트만 처리한다.
※ User Space : 복잡하고 자주 업데이트 되는 기능, 실행 제어를 처리한다.
User Space Driver는 모델이 처음 실행될 때 컴파일을 수행하고 Caching해 두어, 2번째 실행부터는 최고 속도로 돌아가게 한다.
3. Thinking
CPU에서 Job Scheduling은 FIFO말고 SJF(Shortest Job First) 또는 RR(Round-Robin) 등이 있고, Memory Replacement Policy는 FIFO말고 LRU(Least Recently Used) 등이 있는데 Weight FIFO를 대체할 수 있을까?
1) CPU는 사용자의 개입에 따라 작업의 순서가 수시로 바뀌어 미래가 불확실(= 예측 불가능)하므로 Scheduling이 필요하지만 TPU의 Systolic Array는 Deterministic이므로 컴파일러가 몇 번째 cycle에 어떤 가중치가 필요한지 순서를 정확히 알고 있으므로(= Optimal Policy) 컴파일러가 미리 최적화된 순서대로 가중치를 배치하여 FIFO에 넣어주기만 하면 된다.
2) CPU의 Job Scheduling 또는 Memory 교체는 데이터의 사용 빈도를 추적하는 태그 메모리, 비교기, Priority Queue 등의 복잡한 회로가 필요하지만 TPU는 제어 Logic을 줄이고 ALU를 늘려 Weight FIFO는 단순히 데이터를 줄 세워 밀어 넣기만 하면 되므로 회로가 단순화된다.
※ MATMUL(Matrix Multiplication) : 행렬 곱을 의미하며 NN에서는 입력 데이터와 가중치의 행렬 곱으로 표현된다.
※ GEMM(General Matrix Multiply) : 기본적인 MATMUL을 더 일반화하여 최적화한 BLAS(Basic Linear Algebra Subprograms) 라이브러리의 표준 연산 루틴이다.
→ C = α(A x B) + βC 형태로 결과 누적과 스케일링을 한 번에 처리할 수 있어 신경망 학습에 유리하다.
※ Gemmini : Systolic Array 기반의 행렬 곱셈 가속기 생성기로 사용자가 원하는 대로 Systolic Array의 크기, 데이터 타입, 메모리 구조 등을 설정하면 그에 맞는 RTL 코드를 자동으로 생성해준다.
HW 설계 관점에서 Floating Point vs Fixed Point
1) 회로 복잡도와 면적
Floating Point : 회로가 크고 복잡하여 큰 면적 차지(지수를 맞추기 위한 Bit Shifter, 계산 후 정규화하기 위한 Bit Shift, 반올림)
Fixed Point : 회로가 작고 단순(정수 연산)
2) 전력 소모
Floating Point : 정규화 과정 등에서 많은 데이터 이동과 Switching이 발생하여 전력 소모↑
Fixed Point : 단순한 Bit 연산만 수행하므로 전력 소모↓
3) Dynamic Range vs Precision
Floating Point : 지수 부분이 있어 표현 가능한 수의 범위↑ & 가중치를 미세하게 계산 가능하여 정확도↑
Fixed Point : 표현 가능한 수의 범위 제한적, but 추론 단계에서는 학습된 가중치 사용 → 양자화한 정수로 표현해도 정확도 손실↓
AI 동작의 Training과 Inference는 모두 HW를 사용?
Training : 기존 GPU 사용
∵ 학습 단계에서는 가중치를 찾기 위해 Floating Point 연산을 주로 사용하므로 Floating Point 연산에 강한 GPU 사용
Inference : TPU 제작
∵ 추론 단계에서는 양자화 과정을 통해 Floating Point를 8-bit Fixed Point로 변환해도 정확도↑ / 전력 소모 & 면적↓
But, TPU v2부터는 Training이 가능해졌으며 TPU v3는 TPU v2보다 성능이 향상되었다.
TPU v2 : Braing Floating Point 16을 사용하여 Training에 필요한 Floating Point 연산이 가능해졌다.
※ bfloat16 : 숫자의 표현 범위(지수부)를 유지하고 소수점 아래(가수부) 정밀도를 줄여 높은 정확도를 유지
Systolic Array를 사용하지 않을 때의 대안
1) SIMD(Single Instruction Multiple Data) / Vector Architecture (CPU 방식)
하나의 명령어로 여러 데이터(Vector)를 동시에 처리한다.
Register 파일 또는 Cache에서 데이터를 읽어와 연산하고, 결과를 다시 저장하는 과정을 반복한다.
2) SIMT(Single Instruction Multiple Treads) / 대규모 병렬 처리 (GPU 방식)
수천 개의 Thread를 동시에 실행하여 처리량을 높이며 각 Thread는 독립적인 Register 또는 공유 메모리를 사용한다.
데이터 흐름보다 높은 메모리 대역폭에 의존하여 데이터를 빠르게 공급 & Thread를 빠르게 교체하여 메모리 지연 시간을 감춘다.
3) 범용 제어 방식
Processor는 범용성을 위해 Cache, 분기 예측, 비순차 실행 같은 복잡한 HW 기능을 사용한다.
Hidden Layer와 Weight의 수 in MLP
Hidden Later의 개수는 개발자가 결정하며 Layer의 수와 크기가 커질수록 더 정확한 모델을 구축할 수 있다.
But, Layer가 많아지면 연산량과 가중치 데이터 양이 늘어나 처리 시간이 길어진다.
∴ 학습시키면서 모델 성능(like 정확도)에 따라 추가하거나 줄여나간다.
4. Summary
1) 학습 단계에서 가중치를 결정
2) 추론 단계에서 칩 외부의 큰 메모리(DRAM)에 저장된 각 Layer의 가중치를 Load
3) Layer1의 계산이 끝나면 PE에 있던 가중치를 지우고, Layer2의 가중치를 Load하여 계산 → 마지막 계층까지 이 과정 반복
5. Reference
"In-Datacenter Performance Analysis of a Tensor Processing Unit"
https://www.youtube.com/watch?v=cmy7LBaWuZ8
댓글
댓글 쓰기