파이썬에서 실리콘까지: AI 컴퓨트 스택 설명
PyTorch에서 AI 모델을 실행하면 실제로 어떤 일이 일어나나요?
많은 AI 실무자들은 이것에 대해 생각할 필요가 거의 없습니다. 그들은 고수준 프레임워크를 사용해 모델을 설계하고 학습시키며, 계산이 GPU에서 이루어진다는 것을 알지만, 그 원리가 실제로 어떻게 작동하는지에 대해서는 거의 생각하지 않습니다.
이는 PyTorch와 같은 이러한 프레임워크가 GPU와 상호작용할 수 있는 사용하기 쉬운 인터페이스를 제공하기 때문입니다. 그러나 이를 가능하게 하려면 이 코드를 실제 칩이 이해할 수 있는 명령으로 변환하는 소프트웨어 계층이 여러 겹 필요합니다. 이러한 계층을 „AI 컴퓨트 스택“이라고 하며, 하드웨어 제공업체마다 각자의 버전이 있습니다. NVIDIA는 CUDA, AMD는 ROCm입니다.
이번 포스트에서는 이러한 컴퓨트 스택의 추상화 계층을 하나씩 자세히 살펴보겠습니다. 물리적 GPU에서부터 PyTorch와 같은 고급 프레임워크까지 각 계층을 단계별로 구축하면서, NVIDIA와 AMD 생태계의 대응하는 구성 요소를 강조해 설명할 것입니다.
레이어 0: 하드웨어 (기초)
가장 아래층에는 물리적 구성 요소인 GPU 자체(예: NVIDIA H200 또는 AMD Instinct MI300X)가 있습니다.
이 부품들은 행렬 곱셈을 수행하는 데 매우 효율적이기 때문에 가치가 있습니다. 행렬 곱셈은 LLM과 같은 많은 AI 모델을 구동하는 수학적 연산입니다. 하나의 행렬 곱셈을 계산하려면 컴퓨터는 “스칼라 곱셈"이라고 불리는 수십억 번의 일반적인 곱셈(예: 2 x 3)을 수행해야 합니다.
사람들은 이러한 연산을 컴퓨터의 '두뇌'라고 불리는 CPU(중앙 처리 장치)에서 수행하곤 했습니다. 하지만 2012년에 AlexNet이라는 모델은 GPU(그래픽 처리 장치)라고 불리는 다른 종류의 하드웨어 구성 요소가 사실상 이러한 행렬 곱 연산 작업에 더 적합하다는 것을 보여주었습니다.
CPU는 똑똑하지만 순차적입니다. CPU는 복잡한 작업을 하나씩 매우 빠르게 처리하도록 설계되었습니다. GPU는 '단순하지만' 대규모입니다. 수천 개의 '코어'를 가지고 있어 간단한 연산을 병렬로 처리할 수 있습니다.
GPU는 많은 단순 연산(녹색 상자)을 병렬로 수행할 수 있어, 이러한 연산을 순차적으로 수행해야 하는 CPU에 비해 매우 높은 효율성을 제공합니다
GPU가 효율적으로 수행할 수 있는 이러한 '간단한 연산'에는 스칼라 곱셈이 포함되며, 이것은 행렬 곱셈(따라서 AI 모델)을 구동하는 정확한 연산입니다. 따라서 GPU는 CPU보다 AI 모델을 학습하고 실행하는 데 훨씬 더 효율적입니다.
제약 조건은 GPU가 언어 장벽을 가지고 있다는 것입니다. GPU는 Python, C++ 또는 심지어 Assembly와 같은 프로그래밍 언어를 이해하지 못합니다. GPU는 오직 특정 칩을 위해 설계된 기계어 코드(명령어 집합 아키텍처, ISA)만 이해합니다.
이 격차를 메우기 위해 우리는 소프트웨어가 필요합니다.
계층 1: 커널 드라이버
먼저, 전문 용어 경고: AI에서 '커널(kernel)'이라는 단어를 자주 듣게 될 것입니다. 이 레이어, 커널 드라이버(Kernel Driver)는 운영체제 커널 드라이버를 의미합니다. 이는 4번째 레이어에서 다룰 'GPU 연산 커널(GPU Compute Kernel)'과는 다릅니다.
커널 드라이버는 시스템 커널 내의 모듈로, 물리적 GPU와 컴퓨터 사이의 다리 역할을 합니다. 이는 CUDA(발음: '쿠다')와 ROCm(발음: '록-엄') 생태계 내에서 가장 먼저 자리하는 스택의 첫 번째 계층입니다.
물리적으로, GPU는 PCIe를 통해 마더보드에 연결됩니다. 커널 드라이버는 이러한 물리적 연결을 통해 데이터를 주고받는 소프트웨어 구성 요소로, 컴퓨터의 운영 체제(예: MacOS, Windows 11, Ubuntu 등)가 GPU의 펌웨어와 통신할 수 있도록 해줍니다. 커널 드라이버는 '커널 모드'에서 가장 높은 권한으로 작동하며, 이를 통해 이 연결을 통해 GPU의 메모리, 전력 소비 및 온도를 관리할 수 있습니다.
위에 보이는 흰색과 빨간색 커넥터는 PCIe 슬롯입니다. 이는 컴퓨터 마더보드에 GPU를 연결하는 곳이며, 커널 드라이버가 GPU와 통신하는 연결 경로입니다 (출처
중요하게도, 커널 드라이버는 GPU와 직접 통신하는 유일한 계층입니다. 이 위의 모든 계층은 메시지를 커널 드라이버를 통해 전달해야 합니다.
계층 2: 시스템 런타임
한 단계 위로 올라가면 시스템 런타임에 도달합니다. 이 계층은 결합된 CPU + GPU 시스템을 하나의 통합된 계산 단위로 취급하는 항공 교통 관제사로 생각할 수 있습니다.
시스템 런타임은 작업 스케줄러 역할을 합니다. 명령의 '패킷'을 생성하고, 이를 큐로 구성한 후 GPU로 전송합니다. 또한 GPU가 '이 작업을 끝냈다'고 신호를 보내기를 기다립니다.
우리는 Layer 2가 필요합니다. 왜냐하면 Layer 1인 커널 드라이버는 GPU와의 직접적인 통신에만 관여하기 때문입니다. GPU를 사용하려는 각각의 프로그램이 직접 통신하려고 하는 것은 말이 되지 않습니다. 시스템 런타임은 중앙 컨트롤러 역할을 하며, GPU에서 수행해야 할 모든 작업을 조율하여 커널 드라이버가 개별 작업을 완료하기 위해 GPU와 통신하는 데 집중할 수 있도록 합니다.
ROCm의 시스템 런타임은 ROCr이라고 합니다. 이는 '이기종 시스템 아키텍처(Heterogeneous System Architecture, HSA)'라는 오픈 표준을 구현합니다. 이 표준은 이기종 계산 장치(예: CPU와 GPU가 결합된 시스템)가 하나의 단위로서 어떻게 함께 작동해야 하는지를 정의합니다.
CUDA에는 정확한 대응물이 없지만, 가장 가까운 것은 CUDA 드라이버 API입니다. 이 API는 유사한 기능을 수행하는 독점 API이며(직접 프로그래밍할 수 있기 때문에 다음 계층의 일부로 간주될 수도 있습니다).
계층 3: 프로그래밍 인터페이스
레이어 3은 GPU용 코드를 작성하는 소프트웨어 엔지니어들이 실제로 스택과 상호작용하기 시작하는 층으로, 이는 그들이 자신들의 생태계를 위한 프로그래밍 인터페이스를 통해 이루어진다.
GPU를 기계어로 직접 프로그래밍하는 것은 사람에게 비현실적입니다. 이를 해결하기 위해, 벤더들은 개발자들이 일반적인 코드를 작성하면 이를 GPU용 기계어로 컴파일할 수 있는 C++ 확장을 제공합니다.*
이 C++ 확장에는 두 가지가 있습니다:
- NVIDIA는 CUDA C++를 사용하며, 이는 업계 표준으로 10년 넘게 자리잡아 왔습니다
- AMD는 HIP(이기종 컴퓨팅 이식성 인터페이스)을 사용합니다
기술적으로 HIP에 해당하는 것은 CUDA C++와 CUDA 런타임 API를 함께 조합한 것이며, 일반적으로 단순히 'CUDA'라고 부릅니다. HIP와 CUDA는 기본적으로 모두 C++이지만, GPU 프로그래밍과 관련된 핵심 기능을 위한 추가 키워드와 함수들을 제공합니다. 예를 들어:
- 할당: GPU에서 메모리 예약
- 이동: 데이터를 CPU에서 GPU로 이동
- 실행: GPU에서 계산 시작
HIP나 CUDA(프로그래밍 인터페이스)로 작성된 프로그램을 실행하면, 이러한 GPU 관련 명령은 시스템 런타임(레이어 2)**에 전달되며, 시스템 런타임이 해당 명령이 실행되도록 처리합니다.
2층과 3층에서 두 생태계 간의 경계가 조금 모호해집니다. 이 층에서는 두 생태계가 직접적으로 동일하지 않습니다. 예를 들어, ROCr은 순수한 인프라스트럭처로 개발자가 직접 프로그래밍할 수 없지만, CUDA 드라이버 API는 CUDA C++로 프로그래밍할 수 있는 기능을 제공합니다. 하지만 이러한 세부 사항은 대부분의 개발자에게는 중요하지 않습니다.
중요한 것은 (1) ROCm과 CUDA(생태계)가 구조적으로 다르다는 것을 이해하는 것이며, 더욱 중요한 것은 (2) 사람들이 ‘CUDA’라고 말할 때, 이를 C++용 CUDA와 CUDA 런타임 API의 프로그래밍 인터페이스를 가리키는 것일 수도 있고, 전체 CUDA 생태계를 가리키는 것일 수도 있다는 점입니다.
호환성에 대한 참고
AMD는 CUDA가 출시된 지 수년 후에 HIP과 함께 전략적 선택을 했습니다. AMD는 HIP가 거의 CUDA와 똑같이 보이도록 설계했습니다. 이를 통해 개발자들은 CUDA 코드를 최소한의 수정으로 HIP로 이전할 수 있습니다. AMD는 심지어 이 변환을 자동화하는 hipify라는 도구도 제공합니다. HIP는 이식 가능하게 설계되어 AMD GPU 뿐만 아니라 NVIDIA GPU에서도 실행될 수 있습니다.
CUDA는 ROCm의 라이브러리가 아직 충분히 성숙하지 않기 때문에 항상 HIP으로 이전될 수 있는 것은 아니므로, CUDA를 HIP으로 변환할 수 있는지는 특정 사용 사례에 따라 달라집니다. 격차는 점점 줄어들고 있지만, NVIDIA는 여전히 양자 컴퓨팅과 같은 일부 틈새 분야에서 우위를 점하고 있습니다. 그러나 일반적인 AI 애플리케이션과 표준 과학 계산의 경우, AMD의 생태계는 상당히 성숙해졌으며 많은 작업에서 동등한 수준에 도달했습니다.
* 각주: “기계어로 컴파일됨”은 단순화한 표현입니다. 실제로 CUDA C++ 및 HIP 프로그램은 컴파일될 때 나뉩니다. 코드의 CPU 부분은 시스템의 일반 C++ 컴파일러로 컴파일되고, GPU 부분은 GPU 컴파일러로 컴파일되어 프로그램 내에 데이터 블록 형태로 저장됩니다.
프로그램이 실행되면 CPU에서 실행됩니다. 코드의 GPU 섹션이 발견되면 CPU는 해당 실행을 GPU에 위임하며, 사전에 컴파일된 코드 블록을 GPU 런타임으로 보내 GPU에서 코드가 실행되도록 관리합니다.
** 각주: 완전성을 위해, AMD는 HIP과 ROCr 사이에 ROCclr(ROCm 공용 언어 런타임)이라는 중간 계층을 사용합니다. 이 추상화 계층은 AMD가 동일한 기본 런타임으로 여러 프로그래밍 인터페이스(HIP 및 OpenCL)를 지원할 수 있게 합니다. 단순화를 위해, 우리는 이를 HIP → ROCr 연결의 일부로 간주합니다.
레이어 4: 수학 라이브러리
AI 애플리케이션을 개발하고 있다면, 아마 행렬을 곱하기 위해 직접 CUDA C++ 또는 HIP를 작성하고 있지는 않을 것입니다. 그것은 마치 사용자의 이메일 주소를 저장하기 위해 직접 데이터베이스 엔진을 작성하는 것과 같아서 비효율적이고 오류가 발생하기 쉽습니다.
대신, 여러분은 수학 라이브러리를 사용합니다 - 이는 CUDA와 ROCm 생태계 내에서 스택의 마지막 계층입니다.
이것들은 하드웨어 공급업체가 직접 작성한 최적화된 '컴퓨트 커널'(GPU에서 실행되는 작은 프로그램) 모음입니다. 하드웨어 성능을 최대한 끌어내도록 고도로 최적화되어 있습니다.
이들 라이브러리 대부분은 서로 다른 생태계에서도 비슷한 역할을 합니다:
| 작업 유형 | 엔비디아 (CUDA) | AMD (ROCm) |
|---|---|---|
| 기초 선형대수 | cuBLAS, cuSPARSE | rocBLAS, rocSPARSE |
| 딥 러닝 | cuDNN | MIOpen |
| 해결자 | cuSOLVER | rocSOLVER |
연구자가 '커널 최적화'에 대해 이야기할 때, 일반적으로 특정 하드웨어 아키텍처에서 더 빠르게 실행되도록 이러한 라이브러리 내부의 코드를 다시 작성하는 것을 의미합니다.
계층 5: AI 프레임워크
마침내, 우리는 스택의 최상단에 도달했습니다: AI 프레임워크.
여기가 바로 PyTorch, TensorFlow, JAX가 존재하는 곳이며, 대부분의 AI 개발자들이 일하는 장소입니다. 이 프레임워크들은 우리가 방금 논의한 모든 복잡성을 추상화하여, 메모리 할당이나 PCIe 대역폭에 대해 걱정하지 않고도 직관적이고 파이썬다운 방식으로 모델을 만들 수 있게 해줍니다. 이 프레임워크들은 각각 CUDA용과 ROCm용으로 다른 버전의 백엔드를 가지고 있습니다.
하지만 이러한 단순함이 속도 저하를 의미하지는 않습니다. 파이토치에서 함수를 실행할 때, 이는 대부분 고도로 최적화된 레이어 4 라이브러리를 호출하는 래퍼 역할을 합니다. 따라서 프레임워크를 사용할 때 파이썬의 유연성과 단순함을 유지하면서 최적화된 GPU 커널의 성능을 얻을 수 있습니다.
이 프레임워크를 사용하면 이렇게 읽기 쉬운 코드를 작성할 수 있습니다:
import torch
# 1. define the device (the GPU)
device = torch.device('cuda')
# 2. Create two random matrices on the GPU
A = torch.randn(1024, 1024, device=device)
B = torch.randn(1024, 1024, device=device)
# 3. Perform the calculation (Matrix Multiplication)
C = torch.matmul(A, B)
print(f"Calculation complete. Result shape: {C.shape}")이러한 프로그램을 저수준의 혼란스러운 GPU 명령어로 작성하려고 하기보다는:
// Address // Hex Encoding // Disassembly (Operation)
/*0000*/ 0x00005a0000057ab9 IMAD.MOV.U32 R1, RZ, RZ, c[0x0][0x28]
/*0010*/ 0x0000980000000a00 IMAD.MOV.U32 R0, RZ, RZ, c[0x0][0x20]
/*0020*/ 0x0000020000027a02 MOV R2, c[0x0][0x0]
/*0030*/ 0x3800000000025910 IMAD.WIDE.U32 R2, R2, R0, c[0x0][0x0]
/*0040*/ 0x0000580000000a00 IMAD.MOV.U32 R0, RZ, RZ, c[0x0][0x24]
/*0050*/ 0x0000000002037202 IADD3 R3, R2, c[0x0][0x0], RZ
/*0060*/ 0x0001940003027981 LDG.E.SYS R2, [R2]
/*0070*/ 0x0000020000007200 IADD3 R0, R0, 0x1, RZ
/*0080*/ 0x000000000000794d STG.E.SYS [R2], R0
/*0090*/ 0x0000000000007943 EXIT요약: 전체 추적
이 모든 것이 어떻게 연결되는지 시각적으로 이해하기 위해, 하나의 명령어를 추적해 봅시다. AMD 기기에서 PyTorch를 사용하고 있다고 상상하고, 행렬 곱셈을 실행한다고 합시다: torch.matmul().
- 파이썬 (레이어 5): 명령어를 실행합니다. PyTorch의 백엔드가 활성화됩니다.
- 수학 라이브러리(레이어 4): PyTorch가
rocBLAS를 호출하여 행렬 곱셈 커널을 요청합니다. - 프로그래밍 인터페이스(레이어 3):
rocBLAS는 해당 커널을 실행하기 위해 HIP API를 호출합니다. - 시스템 런타임(레이어 2): 런타임은 작업을 큐에 넣고 스케줄링합니다.
- 커널 드라이버(레이어 1): 드라이버는 패킷을 가져와 PCIe 버스를 통해 전송합니다.
- 하드웨어 (레이어 0): GPU 실리콘이 기계어를 받아 수학 연산을 수행합니다.
파이썬에서 실리콘까지 이 전체 스택은 놀라운 공학적 성취입니다. 인간의 논리와 순수 계산 능력 사이의 격차를 연결하며, 오늘날 우리가 보는 AI 폭발을 가능하게 합니다.
하드웨어(녹색)에서부터 CUDA 및 ROCm 생태계(파란색), AI 프레임워크(노란색)에 이르는 전체 AI 컴퓨트 스택
Linux-Console.net 커뮤니티와 함께 학습해 주셔서 감사합니다.










