사용법
이 기능은 아직 개발 중이며 사용할 수 있는 단계가 아닙니다. 이 안내는 기여자나 최신 진행 상황을 확인하고자 하는 분들을 위한 것입니다. 현재 다음 Rust 커널을 GPU에서 실행하는 작업을 진행하고 있습니다. 따라 해 보시려면 이 코드를 src/lib.rs 파일에 복사하십시오.
#![allow(internal_features)]
#![feature(gpu_offload)]
#![cfg_attr(target_os = "linux", feature(core_intrinsics))]
#![cfg_attr(target_arch = "amdgpu", feature(stdarch_amdgpu, abi_gpu_kernel))]
#![cfg_attr(target_arch = "nvptx64", feature(stdarch_nvptx, abi_gpu_kernel))]
#![no_std]
#[cfg(target_os = "linux")]
extern crate libc;
use core::offload::offload_kernel;
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
loop {}
}
#[cfg(target_arch = "amdgpu")]
use core::arch::amdgpu::{workgroup_id_x as block_idx_x, workitem_id_x as thread_idx_x};
#[cfg(target_arch = "nvptx64")]
use core::arch::nvptx::{
_block_dim_x as block_dim_x, _block_idx_x as block_idx_x, _thread_idx_x as thread_idx_x,
};
// Kernels can be generic, like any other Rust function.
// The concrete instantiations required by the host code are collected in a
// manifest, which the device compilation then reads (see below).
#[offload_kernel]
fn kernel<T: Copy>(x: *mut [T; 256], value: T) {
unsafe {
let n = (*x).len();
let i = (thread_idx_x() + block_idx_x() * block_dim_x()) as usize;
if i < n {
(*x)[i] = value;
}
}
}
#[cfg(target_os = "linux")]
#[unsafe(no_mangle)]
fn main() {
let mut x = [0.0f64; 256];
core::offload::offload! {
kernel = kernel,
workgroup_dim = [256, 1, 1],
args = (&mut x as *mut [f64; 256], 2.5),
}
for i in 0..x.len() {
assert_eq!(x[i], 2.5);
}
unsafe { libc::printf(c"all checks passed".as_ptr()); }
}
컴파일 방법
rustc와 같은 LLVM으로 빌드된 clang 컴파일러를 사용하는 것이 중요합니다. 전체 경로 없이 clang만 호출하면 시스템에 설치된 clang이 사용될 가능성이 높으며, 이 clang은 호환되지 않을 가능성이 큽니다. 따라서 아래의 clang/lld 호출을 절대 경로로 바꾸거나, PATH를 그에 맞게 설정하십시오.
컴파일은 세 개의 패스로 진행됩니다:
HostMetadata: 호스트 코드를 컴파일하고, 호스트 코드에 필요한 커널 인스턴스화(제네릭 인스턴스화 포함)를 나열한 매니페스트를 작성합니다.Device: GPU용 커널을 컴파일하며, 매니페스트를 읽어 기록된 제네릭 인스턴스화가 코드 생성되도록 합니다.Host: 디바이스 산출물을 포함하는 최종 호스트 코드를 생성합니다.
target-cpu(gfx90a)를 사용하시는 GPU에 맞는 코드로 바꾸십시오. 이러한 코드는 흔히 “LLVM 타깃 이름”1이라고 불립니다.
먼저 호스트 코드로부터 매니페스트를 생성합니다:
RUSTFLAGS="--emit=llvm-bc,llvm-ir -Csave-temps -Zoffload=HostMetadata=/absolute/path/to/offload.manifest -Zunstable-options" cargo +offload build -r
이 패스는 매니페스트만 작성합니다.
이제 매니페스트를 전달하여 디바이스(GPU) 코드를 생성합니다:
RUSTFLAGS="-Ctarget-cpu=gfx90a --emit=llvm-bc,llvm-ir -Zoffload=Device=/absolute/path/to/offload.manifest -Csave-temps -Zunstable-options" cargo +offload build -Zunstable-options -r -v --target amdgcn-amd-amdhsa -Zbuild-std=core
다음으로 호스트(CPU) 코드를 생성합니다.
RUSTFLAGS="--emit=llvm-bc,llvm-ir -Csave-temps -Zoffload=Host=$PWD/target/amdgcn-amd-amdhsa/release/deps/device.bin -Zunstable-options" cargo +offload build -r
마지막 단계로, 이제 바이너리를 실행할 수 있습니다
LD_LIBRARY_PATH=$(rustc +nightly --print sysroot)/lib ./target/x86_64-unknown-linux-gnu/release/binary-name
all checks passed!
모든 단계를 테스트할 시간이 더 확보되면 이 세 단계는 곧 하나의 명령으로 통합될 예정입니다.
메모리 전송에 대한 더 자세한 정보를 받으려면 바이너리 호출 앞에 LIBOMPTARGET_INFO=-1을 추가하여 정보 출력을 활성화할 수 있습니다.
-
https://rocm.docs.amd.com/en/latest/reference/gpu-arch-specs.html 또는 https://developer.nvidia.com/cuda/gpus. 또는
rustc --print target-cpus를 확인하십시오. ↩