Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

사용법

이 기능은 아직 개발 중이며 사용할 수 있는 단계가 아닙니다. 이 안내는 기여자나 최신 진행 상황을 확인하고자 하는 분들을 위한 것입니다. 현재 다음 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를 그에 맞게 설정하십시오.

컴파일은 세 개의 패스로 진행됩니다:

  1. HostMetadata: 호스트 코드를 컴파일하고, 호스트 코드에 필요한 커널 인스턴스화(제네릭 인스턴스화 포함)를 나열한 매니페스트를 작성합니다.
  2. Device: GPU용 커널을 컴파일하며, 매니페스트를 읽어 기록된 제네릭 인스턴스화가 코드 생성되도록 합니다.
  3. 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을 추가하여 정보 출력을 활성화할 수 있습니다.


  1. https://rocm.docs.amd.com/en/latest/reference/gpu-arch-specs.html 또는 https://developer.nvidia.com/cuda/gpus. 또는 rustc --print target-cpus를 확인하십시오. ↩