Chapter 14

既存のC++やCUDAコードと連携する(cc crate)

Rustにはccというcrateがあって、build.rs内でC/C++のコードをコンパイルしてリンクするのに便利なのですが、これがCUDAもコンパイルできるらしいので試してみた記録(を発掘したので和訳)したものです。

発掘現場:https://github.com/termoshtt/link_cuda_kernel

CUDAサンプルにあるvector_add.cukernel.cuとしてコピーしてきます。

kernel.cu
/** CUDA Kernel Device code */
__global__ void vectorAdd(const float *A, const float *B, float *C, int numElements) {
    int i = blockDim.x * blockIdx.x + threadIdx.x;
    if (i < numElements)
    {
        C[i] = A[i] + B[i];
    }
}

extern "C" {  // CUDAはデフォルトでC++のマングリングを採用するのでそれを抑制する

/** この関数を静的ライブラリとして提供する */
int vectorAdd_main (void) {
    /* 長いので手元のCUDAサンプルを読んでね */
}

} // extern C

これをlibvector_add.a のように静的ライブラリにしてbuild.rsでビルドしましょう:

build.rs
extern crate cc;

fn main() {
    cc::Build::new()
        .cuda(true)               // CUDAをコンパイルします
        .flag("-cudart=shared")   // 以下3行はnvccの引数
        .flag("-gencode")
        .flag("arch=compute_61,code=sm_61")
        .file("kernel.cu")
        .compile("libvector_add.a");  // 静的ライブラリにコンパイルします

    /* Link CUDA Runtime (libcudart.so) */

    // Add link directory
    // - This path depends on where you install CUDA (i.e. depends on your Linux distribution)
    // - This should be set by `$LIBRARY_PATH`
    println!("cargo:rustc-link-search=native=/usr/local/cuda/lib64");
    println!("cargo:rustc-link-lib=cudart");

    /* Optional: Link CUDA Driver API (libcuda.so) */

    // println!("cargo:rustc-link-search=native=/usr/local/cuda/lib64/stub");
    // println!("cargo:rustc-link-lib=cuda");
}

CUDAにはGPGPU向けに公開されているCUDA Runtime (libcudart.so)に加えてCUDA Driver API (libcuda.so)があるので必要に応じてリンクフラグを追加します。このようにしてリンクしたvectorAdd_mainは普通にCのAPIとして呼び出せます:

#[link(name = "vector_add", kind = "static")]
extern "C" {
    fn vectorAdd_main();
}

fn main() {
    unsafe {
        vectorAdd_main();
    }
}

既存のC++製のCUDAコードの資産を使いつつRust使いたいみたいな状況があればお使いください(/・ω・)/