GPU에 대한 기본 사실

7 hours ago 1

  • GPU는 연산 속도가 메모리 접근 속도보다 월등히 빨라서, 메모리 계층 구조가 성능의 병목을 일으킴
  • 연산 집약도(Arithmetic Intensity, AI) 에 따라 연산이 메모리 바운드, 계산 바운드 상태로 구분되며, A100 GPU의 임계점은 약 13 FLOPs/Byte임
  • 성능 최적화 주요 전략으로 연산 합치기(Fusion)와 타일링(Tiling)이 있음; Fusion은 불필요한 메모리 왕복을 줄이고, Tiling은 데이터 재사용을 극대화함
  • 동기화, Coalesced Load, 뱅크 충돌 해결 등 GPU 하드웨어의 구조적 특성을 이해하는 것이 고성능 커널 작성에 중요함
  • 점유율(Occupancy), 스레드 분기 최소화, 양자화(Quantization) 등 추가적인 고려사항이 실제 성능에 중요한 영향을 미침

GPU의 컴퓨트 및 메모리 계층 구조

  • GPU는 일반적으로 메모리 대역폭보다 산술 연산 처리 속도가 훨씬 높음
  • 예를 들어, NVIDIA A100은 약 19.5 TFLOPS(32비트 부동소수점) 성능을 내지만, 메모리 대역폭은 약 1.5TB/s 수준임
  • 4바이트 데이터를 읽는 동안 수십 개의 계산을 처리할 수 있으므로, 데이터 이동이 성능의 병목임
  • 글로벌 메모리(VRAM) 는 모든 데이터가 존재하는 느린 오프칩 메모리이며, Streaming Multiprocessor(SM) 는 컴퓨팅을 담당함
  • SM에는 고속 온칩 Shared Memory(SRAM) 가 있고, 여기서 프로그램이 직접 관리하는 캐시로 활용 가능함
  • 스레드는 가장 작은 실행 단위이며, 각 스레드는 개별 레지스터 집합을 갖고 있음
  • 32개의 스레드Warp를 이루며, Block은 동일 SM에서 실행될 스레드 그리드임

성능 구간: 메모리 바운드 vs 컴퓨트 바운드

  • 커널의 성능은 메모리 바운드(데이터 이동 속도에 의해 제한) 또는 컴퓨트 바운드(SM 연산 능력에 의해 제한) 중 하나임
  • 연산 집약도(AI) 는 Total FLOPs / Total Bytes Accessed로 정의되며, 이 값이 중요 지표가 됨
  • Roofline 모델: x축이 AI, y축이 FLOPS/s인 그래프에서 커널의 실현 성능을 나타냄
    • AI가 낮아 메모리 바운드이면 대각선(메모리 대역폭 계단)
    • AI가 높아 계산 바운드이면 수평선(최고 연산 성능 계단)
  • A100의 Ridge Point는 19.5 TFLOPS / 1.5 TB/s ≈ 13 FLOPs/Byte임
  • AI를 높이면 성능이 향상되며, 커널이 컴퓨트 바운드에 도달할 수 있음

연산 집약도 높이기 전략

  • 간단한 모델: 스레드 1개가 C[i,j] 1개 계산 → AI = 0.25 (매우 낮음, 메모리 바운드)
  • 스레드가 2x2 타일 계산 시에도 AI = 0.5 (여전히 낮음)
  • AI를 높이기 위해서는 여러 스레드가 블록 단위로 Shared Memory에 대형 타일을 적재해 데이터 재사용을 극대화해야 함
  • Block 내 스레드 협력을 통해 AI > 13로 증가시켜 컴퓨트 바운드에 진입 가능함

오버헤드 바운드 상태

  • CPU(호스트)가 GPU에 작업을 할당하는 과정에서 오버헤드 발생 가능
  • GPU 커널이 너무 작거나 많으면, GPU는 작업을 기다리며 대기하는 상황 발생
  • 현대 프레임워크는 비동기 실행을 도입해, 커맨드 스트림을 미리 큐잉하여 오버헤드를 최소화함

성능 증진을 위한 두 가지 핵심 전략: Fusion과 Tiling

Operator Fusion (연산 합치기)

  • 단순 연산(chain), 예: y = relu(x + 1)에서는 각 연산이 별도 커널로 동작하면 데이터가 글로벌 메모리를 왕복함
  • Fusion은 여러 연산을 하나의 커널로 합쳐 중간 값을 글로벌 메모리에 저장하지 않고, 레지스터 내에서 연산 처리 후 최종 결과만 기록함
  • 예시: Triton, torch.compile Inductor 등 JIT 컴파일러가 자동화 처리

Tiling (타일링)

  • 행렬곱 등 복잡한 연산에서, 단일 스레드 모델로는 AI가 낮음
  • 블록 단위로 타일을 나눈 후, 블록 내 모든 스레드가 협력해 데이터 타일을 Shared Memory에 적재, 대규모 데이터 재사용 구현
  • 연산은 "Load(글로벌 -> Shared Memory) - Synchronize(동기화) - Compute(연산)" 3단계 패턴을 따름

Coalesced Load와 벡터화

  • 글로벌 메모리에서 Shared Memory로 데이터를 옮길 땐, Coalesced Access(워프 32개 스레드가 연속된 128바이트 구간 접근)가 중요
  • 벡터화(예: float4) 로 한 번에 여러 데이터 로드 시, 하드웨어 리소스 절약 및 메모리 대역폭 활용 극대화
  • 데이터 정렬(alignment)이 필수이며, 행렬 내 바이트 수 K값이 4의 배수여야 효율적임

Shared Memory 뱅크와 뱅크 충돌

  • Shared Memory는 32개의 독립된 뱅크로 구성되어, 워프 32개 스레드가 각기 다른 뱅크에 접근해야 충돌 없이 동작
  • 행 단위 접근은 충돌 없음, 열 단위 접근은 충돌(같은 뱅크 접근) 발생
  • B 타일은 "로드 및 전치" 전략으로 Shared Memory에 전치로 저장해, 연산 시 행 접근 중심으로 뱅크 충돌을 피함

고속 온칩 연산 패턴

기본 전략 1: 한 스레드가 한 output 계산

  • BLOCK_DIM=32 제한 하에 AI 최대 8로, 컴퓨트 바운드 진입 불가

전략 2: 한 스레드가 여러 output 계산

  • BLOCK_DIM=16, TILE_DIM=64로 설정 시, 한 스레드가 4x4 출력 계산 → AI=16
  • AI>13이므로 A100 기준으로 compute-bound 성능 달성 가능
  • Shared Memory에서 float4 등 벡터화 로드로 효율적 연산 가능

타일화의 실제 한계: 타일 양자화

  • 행렬 크기가 타일 크기의 배수가 아니면, 경계 블록이 실제보다 큰 영역을 계산(불필요한 연산)하며 패딩 처리됨
  • 경계의 스레드는 guard 조건으로 불필요한 메모리 접근은 막지만, 연산 루프는 동일하게 돌려 쓰레기 계산(예: C += A * 0) 발생

추가 성능 튜닝 요소

점유율(Occupancy)와 대기(Latency) 숨기기

  • 워프가 메모리 읽기 등 장시간 대기 시, SM은 다른 워프로 즉각 전환해 유휴 시간을 줄임(대기 숨기기, latency hiding)
  • 여러 Thread Block을 동시에 할당하면, 높은 점유율로 대기 시간 최소화
  • Block이나 타일 크기가 너무 커지면 resident block 수가 줄고, 점유율 저하로 성능 저하 발생

스레드 분기 최소화

  • 워프 내 if-else 조건 분기가 발생하면, 두 경로를 순차적으로 실행해 효과적인 성능 절반 수준으로 감소함
  • min, max 등 branchless 코드로 분기 최소화 필요

양자화(Quantization)

  • FP32 → FP16/BFP16 등 정밀도 축소 시, 메모리 이동량 및 처리 가능 데이터 수가 각각 2배로 증가함
  • A100 기준 FP16 연산은 312 TFLOPS(상대적으로 FP32의 19.5 TFLOPS 대비 최대 16배 성능) 도달 가능
  • 양자화로 Roofline상의 AI 오른쪽(메모리 효율)과 위쪽(최고 연산 성능) 동시 달성 가능

전체 요약

  • GPU 성능의 본질적 한계는 메모리 대역폭온칩 연산 능력의 불균형에 기인함
  • 성능 향상은 데이터 재사용 극대화(타일링) 및 중간 메모리 트래픽 최소화(Fusion) 로 달성
  • 하드웨어 구조(워프, 뱅크, 코얼레스드 액세스, 동기화) 특성을 이해해야 고성능 커널 작성 및 최적화가 가능함
  • 실전에서는 점유율, 분기 최소화, 양자화 등 추가적인 요소가 실질적 속도에 직접적인 영향 미침
  • 고성능 GPU 연산 설계는 이론적 AI 향상, 하드웨어 특성 활용, 실제 데이터 배치 및 사이즈 대응 등 복합적인 고려가 필요함

Read Entire Article