본문 바로가기
AI2026년 9월 14일10분 읽기

러스트로 GPU 커널 짜기 — CUDA C++ 없이 병렬 코드를 작성하는 2026년 선택지

YS
김영삼
조회 104
러스트로 GPU 커널 짜기 — CUDA C++ 없이 병렬 코드를 작성하는 2026년 선택지

러스트로 GPU 커널을 작성한다는 건, 호스트 코드와 디바이스 코드를 한 언어로 쓰고 카고(cargo) 빌드 체계 안에서 관리한다는 뜻이다. 선택지는 크게 세 갈래다. 러스트 커널을 PTX로 직접 컴파일하는 길, 기존 CUDA 프로그래밍 모델을 러스트 바인딩으로 쓰는 길, 그리고 wgpu·Vulkan 컴퓨트처럼 벤더 중립 경로를 쓰는 길.

중요한 사실부터 말해 두자. 언어를 바꾼다고 커널이 빨라지지 않는다. GPU 성능은 메모리 접근 패턴과 점유율에서 결정되며 그건 언어와 무관하다. 러스트가 주는 건 경계 관리와 빌드 재현성이고, 그 결과로 얻는 건 커널을 자주 갈아엎을 수 있는 용기다.

나는 몇 년 전 러스트 프로젝트에 GPU 연산을 붙이려다 두 손 든 적이 있다. 바인딩 크레이트는 유지보수가 멈춰 있었고, build.rs가 CUDA 툴킷 버전마다 다르게 깨졌다. 결국 커널만 C++로 따로 빌드하고 FFI로 부르는, 아무도 행복하지 않은 구조로 마무리했다. 2026년의 상황은 그때보다 많이 낫다. 엔비디아가 러스트 지원을 공식 트랙으로 들고 나왔고, 벤더 중립 경로도 성숙했다.

세 갈래 경로 비교

경로커널 언어이식성성숙도적합한 경우
네이티브 러스트 → PTX러스트엔비디아 전용초기 단계러스트 단일 언어로 통일하려는 신규 프로젝트
CUDA 바인딩C++ 커널 + 러스트 호스트엔비디아 전용안정적기존 CUDA 커널 자산이 있는 팀
wgpu / Vulkan 컴퓨트WGSL·GLSL·SPIR-V넓음(AMD·인텔·애플 포함)실용 수준벤더 중립이 필요하거나 데스크톱 앱에 내장

선택 기준은 단순하다. 텐서 코어 같은 벤더 고유 기능이 성능의 핵심이면 엔비디아 경로를, 여러 GPU에서 돌아야 하면 wgpu를 고른다. 중간은 없다. "나중에 이식하겠다"는 계획은 거의 실현되지 않는다.

GPU 프로그래밍의 사고 모델부터

언어 이전에 실행 모델을 이해해야 한다. GPU는 수천 개의 스레드를 동시에 돌리되, 스레드들은 워프(warp, 보통 32개) 단위로 같은 명령을 함께 실행한다. 여기서 두 가지 결론이 나온다.

  • 분기(if)가 워프 안에서 갈리면 양쪽을 순차 실행한다 — 이것이 워프 다이버전스이고, 최악의 경우 성능이 절반 이하로 떨어진다.
  • 인접한 스레드가 인접한 메모리를 읽으면 한 번의 트랜잭션으로 묶인다(coalescing). 흩어져 읽으면 대역폭이 그만큼 낭비된다.
실무 감각 GPU 커널 최적화의 80%는 메모리에서 나온다. 연산을 줄이는 것보다 메모리를 어떻게 읽느냐가 훨씬 큰 차이를 만든다. 프로파일러에서 처음 볼 지표는 연산 효율이 아니라 메모리 처리량과 점유율이다.

커널의 기본 형태

벡터 덧셈 같은 가장 단순한 커널의 구조를 보면 공통 패턴이 보인다. 전역 인덱스를 계산하고, 경계를 검사하고, 작업을 수행한다. 어떤 경로를 택하든 이 뼈대는 같다.

// 개념적 형태 (경로에 따라 문법은 달라진다)
// 1) 전역 인덱스 = 블록 인덱스 * 블록 크기 + 스레드 인덱스
// 2) 배열 길이를 넘지 않는지 반드시 검사
// 3) 각 스레드는 자기 몫만 처리

fn vector_add(a: &[f32], b: &[f32], out: &mut [f32], idx: usize) {
    if idx < out.len() {          // 경계 검사를 빼먹으면 조용히 이웃 데이터를 덮는다
        out[idx] = a[idx] + b[idx];
    }
}

// 호스트 측: 블록/그리드 크기 결정
// 블록 크기는 보통 128 또는 256에서 시작해 실측으로 조정한다.
// grid = (n + block - 1) / block   (올림 나눗셈)

경계 검사를 생략하면 어떻게 되는지 굳이 경험해 볼 필요는 없다. GPU에서는 배열 밖 접근이 세그멘테이션 폴트로 친절하게 알려 주지 않는다. 이웃 스레드의 데이터를 덮고, 결과는 그럴듯한 숫자로 나오며, 며칠 뒤에 학습 손실이 이상하다는 걸 깨닫게 된다.

러스트가 실제로 막아 주는 것

컴파일 단계에서 걸러지는 실수
  • 호스트–디바이스 버퍼 수명 관리 오류
  • 해제된 메모리 참조
  • 에러 처리 누락(Result 강제)
  • 빌드 의존성 불일치(카고가 관리)
여전히 남는 문제
  • 커널 내부 인덱스 계산 오류
  • 동기화 누락으로 인한 경쟁 상태
  • 워프 다이버전스·뱅크 충돌
  • 메모리 접근 패턴에서 오는 대역폭 병목

이 표를 오해하면 안 된다. 러스트가 잡아 주는 항목들은 GPU 코드에서 디버깅이 가장 고통스러운 부류다. 호스트 쪽 버퍼 수명 문제는 재현이 어렵고, 에러 코드를 무시한 CUDA 호출은 한참 뒤에 엉뚱한 지점에서 터진다. 이 둘만 사라져도 개발 경험이 크게 달라진다.

공유 메모리와 타일링 — 성능이 갈리는 지점

행렬 곱을 예로 들면, 순진한 구현은 같은 데이터를 전역 메모리에서 반복해 읽는다. 타일링은 블록 단위로 데이터를 공유 메모리에 올려 두고 재사용한다. 전역 메모리 접근은 수백 사이클, 공유 메모리는 수십 사이클 수준이라 차이가 크다.

1
타일 크기 결정
블록당 스레드 수와 공유 메모리 용량으로 제약된다. 16×16이나 32×32에서 시작한다.
2
협력적 적재
블록의 모든 스레드가 함께 타일을 공유 메모리로 옮긴다. 각자 한 조각씩 담당한다.
3
동기화
모든 적재가 끝난 뒤에 계산을 시작해야 한다. 배리어를 빠뜨리면 비결정적 오류가 생긴다.
4
계산
공유 메모리에서 읽어 부분 합을 누적한다.
5
다음 타일로
다시 동기화한 뒤 반복. 동기화를 양쪽에 넣어야 하는 이유는 이전 타일을 아직 쓰고 있는 스레드가 있기 때문이다.
뱅크 충돌 공유 메모리는 여러 뱅크로 나뉘어 있고, 같은 뱅크에 동시에 접근하면 직렬화된다. 타일 폭을 32로 잡았을 때 자주 발생하며, 폭을 33으로 늘려 패딩하는 고전적 우회법이 있다. 프로파일러에서 공유 메모리 효율이 낮게 나오면 이것부터 의심한다.

벤더 중립 경로 — wgpu를 쓸 때

데스크톱 앱이나 크로스 플랫폼 도구에 GPU 연산을 넣는다면 wgpu가 현실적이다. WGSL로 컴퓨트 셰이더를 작성하고, 같은 코드가 Vulkan·Metal·DirectX 백엔드에서 돌아간다. 웹까지 대상이라면 WebGPU로 그대로 이어진다.

항목CUDA 계열wgpu
하드웨어엔비디아AMD·인텔·애플·엔비디아
정밀도 제어FP8·FP4 등 세밀한 지원기본 타입 위주
텐서 코어 활용가능제한적
생태계 라이브러리풍부(cuBLAS 등)직접 구현 비중 높음
배포드라이버·툴킷 의존상대적으로 단순

AI 학습·추론처럼 텐서 코어가 성능의 핵심인 워크로드에서는 wgpu가 불리하다. 반대로 이미지 처리, 시뮬레이션, 데이터 변환처럼 범용 병렬 연산이면 충분히 실용적이다.

측정 없이는 아무것도 아니다

커널 최적화 순서
먼저 CPU 대비 실제 이득이 있는지 확인 — 전송 비용이 연산 이득을 넘는 경우가 흔하다
전송과 연산을 겹치게(스트림·비동기 복사) 만들었는가
메모리 접근이 연속적인가 (인접 스레드 → 인접 주소)
점유율(occupancy)이 낮다면 레지스터·공유 메모리 사용량을 줄인다
분기가 워프 안에서 갈리지 않도록 데이터를 정렬할 수 있는가
프로파일러 수치로 확인 — 추측으로 최적화하지 말 것

첫 항목을 특히 강조하고 싶다. 작은 배열을 GPU로 보내 계산하고 다시 가져오면, 거의 항상 CPU보다 느리다. GPU가 이기려면 데이터가 충분히 크거나, 한 번 올린 데이터를 여러 번 재사용해야 한다.

언제 GPU를 쓰지 말아야 하나

분기가 데이터마다 크게 갈리는 작업, 순차 의존성이 강한 알고리즘, 데이터가 작은 작업은 GPU에 맞지 않는다. 트리 탐색이나 상태 기계처럼 스레드마다 다른 경로를 가는 코드는 워프 다이버전스로 이득이 사라진다. 이런 경우 SIMD를 쓴 CPU 구현이 더 빠른 경우가 많다.

자주 묻는 질문

러스트로 GPU 커널을 쓰면 C++보다 빨라지나요?

아닙니다. GPU 성능은 메모리 접근 패턴, 점유율, 공유 메모리 사용, 워프 다이버전스에서 결정되며 이는 언어와 무관합니다. 러스트의 이점은 호스트 측 메모리 안전성과 빌드 재현성이고, 그 결과 커널을 더 자주 개선할 수 있다는 점입니다.

어떤 경로를 선택해야 하나요?

텐서 코어 같은 엔비디아 고유 기능이 성능의 핵심이면 CUDA 계열을, 여러 벤더 GPU에서 동작해야 하면 wgpu·Vulkan 컴퓨트를 선택하세요. 기존 CUDA 커널 자산이 있다면 커널은 두고 호스트 코드만 러스트로 옮기는 점진적 방식이 안전합니다.

GPU 커널 최적화는 무엇부터 봐야 하나요?

메모리입니다. 인접 스레드가 인접 주소를 읽는지(합병 접근), 반복 읽는 데이터를 공유 메모리로 올렸는지, 뱅크 충돌이 없는지 순서로 확인하세요. 연산량 줄이기는 그다음입니다.

작은 데이터에도 GPU를 쓰면 좋은가요?

대개 손해입니다. 호스트와 디바이스 간 전송 비용이 연산 이득을 넘기기 때문입니다. 데이터가 충분히 크거나, 한 번 올린 데이터를 여러 커널에서 재사용하는 구조여야 이득이 납니다.

워프 다이버전스는 어떻게 줄이나요?

같은 워프 안의 스레드들이 같은 분기를 타도록 데이터를 정렬하거나, 분기 대신 산술 연산으로 대체하는 방법이 있습니다. 조건이 데이터에 따라 무작위로 갈리는 알고리즘은 애초에 GPU에 적합하지 않을 수 있습니다.

프로덕션에 러스트 GPU 스택을 써도 되나요?

네이티브 러스트 커널 경로는 아직 성숙 단계에 있어, 검증된 프로덕션 커널을 서둘러 옮기는 것은 권하지 않습니다. 신규 실험 워크로드나 비핵심 경로부터 시작해 툴체인 안정성을 직접 측정한 뒤 확대하는 방식이 안전합니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록