GPU 코드에서 Rust의 표준 라이브러리(std)를 사용할 수 있게 되었다. 구현 접근 방식과 이것이 GPU 프로그래밍에 열어주는 가능성을 소개한다.
URL: https://www.vectorware.com/blog/rust-std-on-gpu/
2026년 1월 20일 · 읽는 데 11분
이제 GPU 코드에서 Rust의 표준 라이브러리를 사용할 수 있습니다. 우리는 구현 접근 방식과 이것이 GPU 프로그래밍에 열어주는 가능성을 공유합니다.
VectorWare에서는 최초의 GPU 네이티브 소프트웨어 회사를 만들고 있습니다. 오늘 우리는 GPU에서 Rust의 표준 라이브러리를 성공적으로 사용할 수 있게 되었음을 기쁜 마음으로 발표합니다. 이 이정표는 익숙한 Rust 추상화를 사용해 GPU 하드웨어의 모든 성능을 활용하는 복잡하고 고성능인 애플리케이션을 개발자가 작성할 수 있도록 하겠다는 우리의 비전에 중요한 발걸음입니다.
이 글은 우리가 구축한 것의 미리보기입니다. 우리는 우리의 작업을 업스트림에 반영할 가능성을 염두에 두고 준비하고 있으며, 향후 글에서 더 깊은 기술적 세부 내용을 공유할 예정입니다.
std 라이브러리Rust의 표준 라이브러리는 계층화된 추상화들의 집합으로 구성되어 있습니다:
core는 언어의 기반을 정의하며 힙도 운영체제도 가정하지 않습니다.alloc은 core 위에 힙 할당을 추가합니다.std는 core와 alloc의 최상단에 있으며 파일, 네트워킹, 스레드, 프로세스 같은 운영체제 관련 API를 추가합니다.Rust의 정의적 특징 중 하나는 2번과 3번 계층이 _선택 사항_이라는 점입니다. 코드는 #![no_std] 어노테이션을 통해 std를 사용하지 않도록 선택할 수 있으며, core만(필요하다면 alloc도) 의존할 수 있습니다. 이는 전통적인 운영체제가 없는 임베디드, 펌웨어, 드라이버 같은 영역에서 Rust를 사용할 수 있게 해줍니다.
std가 지원되지 않는 이유우리는 GPU에서 Rust 코드를 실행할 수 있게 해주는 오픈 소스 프로젝트인 rust-cuda와 rust-gpu의 메인테이너입니다. 이 프로젝트들로 GPU를 타깃팅할 때 Rust 코드는 #![no_std]로 컴파일되는데, GPU에는 운영체제가 없어서 std를 지원할 수 없기 때문입니다.
#![no_std]는 Rust 언어의 일부이므로, 다른 목적을 위해 작성된 crates.io의 #![no_std] 라이브러리들은 대체로 수정 없이 GPU에서 실행할 수 있습니다. 기존 오픈 소스 라이브러리를 재사용할 수 있는 이 능력은 다른(비 Rust) GPU 생태계에 비해 훨씬 낫습니다.
그럼에도 std를 포기하는 데에는 비용이 있습니다. Rust에서 가장 유용하고 인체공학적인(ergonomic) 추상화 다수가 표준 라이브러리에 있고, 오픈 소스 라이브러리의 대부분이 std를 가정합니다. GPU에서 의미 있는 std 지원을 가능하게 하면 훨씬 더 큰 범위의 애플리케이션이 열리고, 코드 재사용도 더 많이 할 수 있습니다.
std가 가능해질지도 모르는 이유머신러닝과 AI 같은 현대적 GPU 워크로드는 GPU에서 스토리지와 네트워킹에 빠르게 접근해야 합니다. NVIDIA의 GPUDirect Storage, GPUDirect RDMA, ConnectX 같은 기술은 데이터센터에서 GPU가 디스크 및 네트워크와 더 직접적으로 상호작용할 수 있게 해줍니다. NVIDIA의 DGX Spark나 Apple M 시리즈 기기 같은 시스템을 통해 유사한 기능이 소비자 하드웨어에서도 나타나기 시작했습니다. 전통적으로 운영체제가 제공하던 API와 기능들이 GPU 코드에서도 사용 가능해지고 있으며, 우리는 그 추세가 계속될 것이라고 봅니다.
AMD의 APU 설계나 다양한 CPU에 통합되는 NPU/TPU 같은 사례에서 보듯, CPU와 GPU는 아키텍처적으로 수렴하고 있다고 믿습니다. 이런 수렴은 GPU와 다른 코어를 고립된 가속기로 취급하기보다, 장치 간 추상화를 공유하는 것이 더 실용적이면서도 더 매력적으로 만듭니다.
stdGPU 생태계는 CPU 생태계보다 더 젊습니다. GPU 하드웨어 역량은 빠르게 변화하고 있고 소프트웨어 표준도 아직 형성 중입니다. Rust의 std는 생태계가 성숙할 때까지 내부 구현을 바꾸면서도 안정적인 표면(인터페이스)을 제공할 수 있습니다. 예를 들어 std는 어떤 시스템에서는 GPUDirect를 사용할 수 있고, 다른 시스템에서는 호스트 중개(host mediation)로 폴백하거나, 하드웨어 지원에 따라 통합 메모리(unified memory)와 명시적 전송(explicit transfers) 사이를 적응적으로 선택할 수 있습니다. 핵심은 기반 기능 지원이 달라져도 사용자 코드는 바꿀 필요가 없다는 점입니다.
VectorWare에서는 상위 수준 추상화가 핵심인 매우 복잡한 GPU 네이티브 애플리케이션을 만들고 있습니다. 우리는 이 공간을 개척하게 되어 매우 기대하고 있으며, 앞으로 우리가 한 일을 더 많이 공유할 것입니다.
std다음은 Rust의 std 라이브러리를 사용해 stdout에 출력하고, 사용자 입력을 읽고, 현재 시간을 얻고, 파일에 쓰는 등의 여러 작업을 수행하는 간단한 GPU 커널입니다. 이 코드는 GPU에서 실행됩니다:
rustuse std::io::Write; use std::time::{SystemTime, UNIX_EPOCH}; #[unsafe(no_mangle)] pub extern "gpu-kernel" fn kernel_main() { println!("Hello from VectorWare and the GPU!"); print!("Enter your name: "); let _ = std::io::stdout().flush(); let mut name = String::new(); std::io::stdin().read_line(&mut name).unwrap(); let name = name.trim_end(); println!("Hello, {}! Nice to meet you from the GPU!", name); let now = SystemTime::now(); let duration_since_epoch = now.duration_since(UNIX_EPOCH).unwrap(); println!( "Current time (seconds since epoch): {}", duration_since_epoch.as_secs() ); let msg = format!( "This file was created *from a GPU*, using the Rust standard library 🦀\n\ We are {:?}\n\ Current time (seconds since epoch): {}\n\ User name: {}", "VectorWare".to_owned(), duration_since_epoch.as_secs(), name ); std::fs::File::create("rust_from_gpu.txt") .unwrap() .write_all(msg.as_bytes()) .unwrap(); println!("File written successfully!"); }
일반적인 Rust 코드와의 유일한 차이는 이 함수가 GPU 커널의 엔트리 포인트임을 나타내는 #[unsafe(no_mangle)] 및 extern "gpu-kernel" 어노테이션입니다. 우리는 향후 컴파일러 변환을 통해 이것들도 제거할 예정입니다.
이것이 가능한 이유는 우리의 커스텀 hostcall 프레임워크 덕분입니다. 호스트콜은 시스템 콜(syscall)과 유사합니다. 호스트콜은 GPU 코드가 스스로 실행할 수 없는 일을 호스트 CPU가 수행해 달라고 보내는 구조화된 요청입니다. GPU에서 호스트로 보내는 원격 프로시저 호출(RPC)처럼 생각해도 되며, syscall과 유사한 기능을 달성하기 위한 것입니다.
최종 사용자 입장에서는 Rust의 std API가 바뀌지 않은 것처럼 보이고 기대한 대로 동작합니다. 하지만 내부적으로는 특정 std 호출이 호스트콜을 통해 재구현되어 있습니다. 호스트콜로 어떤 API든 구현할 수 있지만, 이번에는 Rust의 std에 대한 변경을 최소화하기 위해 libc 파사드(facade)를 구현했습니다(std는 Unix 계열 운영체제와 통신하기 위해 libc를 사용합니다). 예를 들어 std::fs::File::open은 호스트에 open 호스트콜을 발행하도록 재구현되며, 호스트는 자신의 파일시스템 API를 사용해 실제 파일 열기 작업을 수행합니다.
GPU에서 std를 사용할 수 있게 되면 개발자 인체공학이 크게 향상되고, Rust 생태계의 훨씬 더 큰 부분을 이제 GPU에서 사용할 수 있습니다.
"Hostcall"이라는 이름은 다소 오해의 소지가 있습니다. 실제로 호스트콜 요청은 호스트 또는 GPU 어느 쪽에서든 처리될 수 있습니다. 이는 GPU 하드웨어 및 소프트웨어가 성숙함에 따라 점진적 향상(progressive enhancement)을 가능하게 합니다. 예를 들어 std::time::Instant는 CUDA의 %globaltimer 같은 API가 있는 플랫폼에서는 디바이스 타이머를 사용해 GPU에서 구현되지만, std::time::SystemTime는 GPU에 벽시계(wall-clock) 지원이 부족하기 때문에 호스트에서 구현됩니다.
호스트콜을 어디서 처리할지 선택할 수 있으면 디바이스 측 캐시(호스트콜 결과를 GPU가 로컬에 캐시하여 반복적인 왕복을 피함)나 파일시스템 가상화(GPU가 자체적인 파일시스템 뷰를 유지하고 필요 시 호스트와 동기화함) 같은 고급 기능도 가능해집니다.
호환성을 깨지 않으면서 새로운 의미(semantics)를 혁신적으로 추가할 수도 있습니다. 예를 들어 /gpu/tmp에 파일을 쓰면 GPU에 남겨두라는 의미로 해석한다든지, localdevice:42로 네트워크 호출을 해 워프/워크그룹 42와 통신하도록 만들 수도 있습니다. 우리는 향후 이 분야에서의 작업도 공유할 예정입니다.
아이디어 자체는 꽤 단순하지만, 정확성과 성능을 보장하기 위해 구현에는 많은 디테일이 있습니다. 구현에 대해서는 이후 블로그 글에서 더 깊게 다룰 예정입니다. 여기서는 몇 가지 상위 수준의 디테일만 다룹니다.
우리는 더블 버퍼링과 원자적 연산 같은 표준 GPU 프로그래밍 기법을 사용하고, 데이터 티어링(data tearing)을 피하며 메모리 일관성(memory consistency)을 보장하도록 주의를 기울였습니다. 프로토콜은 GPU 측 로직을 단순하게 유지하기 위해 의도적으로 최소화되어 있으며, 적절한 경우 GPU 힙 할당을 피하기 위해 결과를 패킹(packing)하는 기능도 지원합니다. 호스트가 요청을 처리하는 동안 GPU를 블로킹하지 않기 위해 CUDA 스트림 같은 API를 활용합니다. 호스트 측 호스트콜 핸들러는 1:1 libc 트램폴린이 아니라 std를 사용해 구현되어 이식성과 테스트 가능성을 확보했습니다.
Rust 프로그래머로서 우리는 안전성과 정확성을 매우 중요하게 생각합니다. 우리는 GPU를 CPU 스레드로 에뮬레이션하여, 호스트콜 런타임/커널의 수정된 버전을 miri 아래에서 실행해 코드가 최소한으로라도 건전한지(sound) 확인했습니다. 테스트 및 검증 노력에 대해서도 향후 글에서 더 공유하겠습니다.
현재 구현은 CUDA를 통해 NVIDIA GPU를 사용하는 Linux 호스트를 타깃으로 합니다. 다른 호스트 OS나 다른 GPU 벤더를 지원하는 데 근본적인 장애물은 없습니다. 호스트콜 프로토콜은 벤더 불가지론적(vendor-agnostic)이며, HIP을 통해 AMD GPU를 타깃팅하거나 rust-gpu로 Vulkan을 사용할 수도 있습니다.
GPU 코드에 시스템 같은 API를 노출하는 데에는, 주로 CUDA 및 C++ 생태계를 중심으로 상당한 선행 사례가 있습니다.
NVIDIA는 CUDA의 일부로 GPU 측 C/C++ 런타임을 제공하며, 여기에는 libcu++, libdevice, CUDA 디바이스 런타임( CUDA Programming Guide 참조) 등이 포함됩니다. 이 구성 요소들은 디바이스 코드용 C/C++ 표준 라이브러리의 상당 부분을 구현합니다. 하지만 CUDA에 강하게 결합되어 있고, C/C++에 특화되어 있으며, 범용 운영체제 추상화를 제공하도록 설계되지 않았습니다. 파일시스템, 네트워킹, 벽시계 시간 같은 기능은 여전히 호스트 전용 관심사로 남아 있으며, 통합된 표준 라이브러리 인터페이스가 아니라 CUDA 특화 API를 통해 노출됩니다.
또한 호스트로 호출을 포워딩하여 GPU에 C 또는 POSIX 유사 계층을 제공하려는 GPU용 libc 같은 실험과 관련 프로젝트들도 있었습니다. 우리는 자체 작업을 시작한 뒤에야 이러한 시도를 알게 되었지만, 독립적으로 비슷한 아이디어와 구현에 도달했다는 점이 매우 흥미롭습니다. 또한 이런 계층을 Rust의 std 같은 고수준 언어 표준 라이브러리와 통합한 선행 작업은 우리가 알기로는 없습니다.
OpenCL, HIP, SYCL 같은 다른 GPU 프로그래밍 모델들도 각자의 표준 라이브러리와 런타임 추상화를 제공합니다. 이 라이브러리들은 이식성과 성능에 초점을 맞추기 위해 의도적으로 최소화되고 도메인 특화되어 있으며, 기존 CPU 코드베이스와의 소스 호환성이나 범용 표준 라이브러리 생태계의 재사용을 목표로 하지 않습니다.
우리의 접근은 두 가지 핵심 측면에서 다릅니다. 첫째, 새로운 GPU 전용 API 표면을 도입하기보다 Rust의 std 자체를 직접 타깃팅합니다. 이는 기존 Rust 코드와 라이브러리와의 소스 호환성을 보존합니다. 둘째, 호스트 중개를 눈에 보이는 프로그래밍 모델이 아니라 std 뒤에 숨은 구현 세부사항으로 취급합니다.
그 의미에서, 이 작업은 새로운 GPU 런타임을 발명하는 것이라기보다 Rust의 기존 추상화 경계를 이기종 시스템 전반으로 확장하는 것에 가깝습니다. VectorWare에서는 GPU에 Rust를 가져오는 것에 그치지 않고, GPU 자체를 Rust로 끌어오고 있습니다.
우리는 변경 사항을 정리하고 오픈 소스화할 준비를 하고 있습니다. VectorWare 팀에는 Rust 컴파일러 팀의 여러 멤버가 포함되어 있어, 가능한 한 업스트림에서 작업하기를 희망합니다. 다만 Rust 표준 라이브러리에 대한 변경은 신중한 리뷰가 필요하며 업스트림 반영에는 시간이 걸립니다.
한 가지 열린 질문은 올바른 추상화 경계가 어디에 있어야 하느냐입니다. 이 구현은 Rust의 기존 std 라이브러리 변경을 최소화하기 위해 libc 스타일 파사드를 사용하지만, 장기적으로 libc가 올바른 계층인지 아직 확실하지 않습니다. 호스트콜 메커니즘은 매우 범용적이어서, Rust 네이티브 API를 통해 std를 GPU 인지적으로 만드는 대안도 가능합니다. 이는 std 측에서 더 많은 작업이 필요하지만, libc를 모방하는 것보다 더 안전하고 효율적일 수 있습니다.
회사로서 우리는 모든 사람이 Rust를 쓰는 것이 아니라는 점을 이해합니다. 우리의 미래 제품은 여러 프로그래밍 언어와 런타임을 지원할 것입니다. 하지만 Rust는 고성능이고 신뢰할 수 있는 GPU 네이티브 애플리케이션을 구축하는 데 독보적으로 적합하다고 믿으며, 이것이 우리가 가장 흥미를 느끼는 부분입니다.
진행 상황을 받아보려면 X, Bluesky, LinkedIn을 팔로우하거나 블로그를 구독해 주세요. 앞으로 몇 달 동안 호스트콜 구현과 기타 컴파일러 작업에 대한 더 깊은 기술적 분석, 테스트 및 검증 노력, Rust를 활용한 GPU 프로그래밍의 미래에 대한 생각 등 더 많은 내용을 공유할 예정입니다. 또한 hello@vectorware.com으로 연락하실 수도 있습니다.