Introduction - Naver future AI computing 팀
- Efficient AI를 위해서는 AI model과 CS 지식(kernel, software)등이 모두 필요
- 최적화와 경량화
- 서비스 로직을 이해해서, 어떤 하드웨어가 더 효율적인지까지 업무를 수행 중 (Holistic Approach)
- 삼전, Intel과 협업 중 (가속기)
Part 1: Introduction
- 방법론에 깊이 들어가지는 않지만, 기본 적인 원리는 이해 해야함
- 카이스트 예종철 교수님 랩 출신 이준형 연구원님 (전문연)
- 왜 바이오 랩에서 Efficient AI?
- 최근에야 LLM이 computing이 중요해졌지만, 원래는 medical이 efficient AI의 많은 수혜자였음
DL as Matrix Ops
- Hardware와 DL과의 관계
- 왜 DL을 하는데 GPU가 필요한가?
- DL은 인공신경망 (뉴런에서 아이디어)
- 인공신경망은 결국 vector간의 dot product
- 이러한 연산을 무수히 많이 병렬적으로 수행 가능
- 결국 기본은 vector의 matmul - 선형대수를 적용 가능
- CNN도 행렬로 표현할 수 있나요?
- 가능! 수학적으로도 증명 가능
- CNN은 Hankel matrix로 표현할 수 있음
- Convolution은 수학적으로, cross-correlation 연산을 진행하는 것
- 결론적으로, DL은 행렬곱, 벡터연산이므로 병렬화가 가능 -> 특화된 하드웨어를 사용하는 것이 가격 대비 효율적
Parallel Hardware
- Flynn's Taxonomy
- CPU vs GPU
- CPU는 모든 일을 다 해야함
- 즉 복잡한 논리 구조, 실제로 연산하는 부분은 작음
- GPU는 그래픽을 연산하기 위해 만들어진 것으로, 병렬 연산에만 집중되어 있음
- 하드웨어 제어가 없기에, 개발자가 수행해야한다는 단점
- Tiled Matrix Multiplication
- 행렬을 블럭 단위로, 계산하여도 수학적으로 동일
- 컴퓨터의 cache는 빠르지만, 작음
- 연산 중에는 block matmul이 서로 간섭이 없기에, 병렬화가 가능함
- Alternative processors: Systolic Arrays
- GPU말고 다른 하드웨어를 통해 DL
- Tensor core, TPU, Gaudi가 이런 systolic array 구조
Managing Data Movement
- modern process
- 무어의 법칙, 황의 법칙
프로세스의 속도는 exponential하지만, memory는 따라오지 못했음
즉 현대 병목은 대부분 memory에 의해 발생
- Caching and Data Locality
- A100은 고사양 GPU
- 메모리 위계가 바로 cache
- Host to Device Data Transfer
- PCI bus (GPU와 CPU를 연결)은 매우 느림
- Storage to Device Data Transfer
- SSD에서 들어오는 데이터는 더 느림
- Cache > Dram > Host > Storage의 속도 위계
- 효율적인 프로그램은 메모리들 사이에서의 이동을 느리게 해야 함
Parallel Computing Concepts
- Concept
Network의 속도가 빠르다는 3가지 의미
Bandwidth : 고점
Throughput : 실제로 처리되는 양 (단위 시간당 얻는 output)
Latency : 하나의 작업이 끝나는데 필요한 시간 (하나의 output을 얻는데 필요한 시간)
- Online은 latency가 중요 (빠르게 반응 해야함)
- DL 학습은 Throughput이 중요 (많은 양을 처리해야 함)
- 가장 흔한건 memory-bound (메모리 병목)
Array Views
- Basic Linear Algebra Subprograms
- 선형 대수를 위한 routine의 정리 (라이브러리가 아닌 표준!)
- 실제 구현은 MKL, cuBLAS, Magma같은 라이브러리가 제공
- Pytorch, Numpy는 BLAS를 api로 호출하는 것
- Array Views
- Computer는 matrix를 1차원으로 봄
- 표준을 통해 어떻게 보는지를 정의
- Non contiguous view는 메모리를 바꾸는게 아닌, 다른 view를 제공하는 것 (slicing 등)
- Deep copy가 아닌 pointer만 제공
- 이러한 것은 다 메모리 사용을 적게 하기 위함
- (Batch, Sequence, Feature)도 이러한 알고리즘이 다 들어가 있는 것
- 왜 가장 연산을 많이 하는게 안쪽이어야 하는가? - 하드웨어는 가까운 메모리를 더 효율적으로 처리하기에
Parallel Software & Algorithms
- CUDA
- Compute Unified Device Architecture
- Nvidia에서 GPU를 프로그래밍 하기 위해 만든 C++기반 언어
- 하드웨어 세대 번호에 따라서 어떤 기능이 제공되는지 결정 (호환성을 고려하는데 중요)
- PTX & SASS라는 어셈블리 어를 사용해 프로그램이 돌아감
- Pytorch의 위계
- Hardware - SASS - PTX - CUDA - CUDA Libraries(cuDNN 등) - C++ libraries (Aten, Caffe2) - Pytorch!
- 매우 상위 라이브러리
- Cuda Driver
- 하드웨어와 직접 소통하게 해주는 친구
- Driver만 설치하면, Runtime을 설치할 수 있고, 대다수의 프로그램은 Runtime만 사용하면 됨
- Just-in-Time Compilation
- Triton Compiler
- torch.compile, Deepspeed에서 사용되는 compiler
- 단순한 python code를 PTX의 최적화된 코드로 변환해줌
* 유일하게 driver를 건드려야 할 때가, 이거를 사용할 때
Asynchronous Techniques
- Cuda GPU = Device
- CPU = Host
- Device와 Host는 원래 비동기적 (Asynchronous - 뭐 시키고, 따로 할거 하고 있는 것)
- 연산, 전송에서 비동기적인게 중요
- CPU는 명령을 보내고, 자기 일 하러 넘어감
- 실제 코드에서, .item()이나 .cpu()를 쓰면 동기적으로 변화시켜서 느려짐
- Pytorch 논문을 보면 CPU가 명령을 쭈르륵 내리면서, GPU가 놀지 않게 만듬
- Pinned Memory
- CPU는 memory를 옮길수도 있음
- 이때 연산에 속도가 느려지니, memory를 고정시키는 것
- Data Pre-fetch for Efficient Compute Utilization
- .to(non_blocking=True) 하면 빨라짐
Case Study - Dataloader
- 모든 내용이 dataloader안에 담겨있음
- num_worker가 0보다 크면 왜 오류날 때가 있나요?
- python은 multiprocessing이 잘 안됨
- pickling을 할 수 없는 object가 있으면 에러 나는 것
Case Study - Flash Attention
- 실제 efficient AI를 할 때 무엇을 중요시해야하는가
- Transformer에서 Self-Attention이 메모리를 많이 차지하고, Softmax할 때 오래 걸림
- 그러나 중요하기에, memory bound가 걸림
- Computation O(n^2), memory O(n^2)이라 왕 오래걸림
- 커서 cache에도 못들어가고, dram으로 읽어야해서 더 오래걸림
- Flash Attention
- 메모리 위계에서 가장 빠른 부분 만을 활용할 수 있도록 한 것
- Fused Kernel로 matmul, dropout 등을 한 번에 처리하는 것
- Hardware aware한 함수..
Conclusion
- HPC에 대부분은 memory management
- Not To do
- 데이터를 최대한 옮기지 마라
- 최대한 새로운 array를 생성하지 마라
- Synchronize 하지 마라
- To do
- 병렬화, 비동기
- 어디서 느려지는지를 꼭 profiling 해라