Advice on linking CUDA to Zig

Alright, I’ve got it working from Zig - we’re calling kernels, folks… let the good times roll.

Here’s a basic idea of how to compile kernels in Cuda for Zig using NVCC and the build system. Note: I’m running on Linux. I’ve decided to go down the shared object route (I’m sure there’s other ways, but most cuda libraries are .so anyhow). So…

First, you have to build your library file - couple things to note:

  • I’m using @nurpax’s suggestion for defining extern to extern "C"
  • In the source file, keep everything marked as extern "C", only use the macro on the header declarations.
  • Mark kernels as extern "C"extern "C" __global__ void foo...

Then, compiling it - here’s the command line argument for compiling that worked for me:

nvcc --shared -o libkernels.so kernels.cu --gpu-architecture=sm_89 --compiler-options '-fPIC' -I/usr/local/cuda/include -L/usr/local/cuda/lib -lcudart

Note that we put the prefix “lib” on kernels - this helps the Zig build search for the library file. You can put this as a build step as well… but that’s trivial.

Then, import your header using @cImport and use your functions like so:

const cu = @cImport({
    @cInclude("/path/to/your/header/file.h");
});

// sometime later...

const dev_ptr = cu.deviceMalloc(i32, 42);

Now, for the build… here’s the basic idea… you need to link your shared object, libc, and the cuda requirements you’ll be using… here’s a rough way to do that.

    const exe = b.addExecutable(.{
        .name = "Cuda",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });

    // for our newly created file
    exe.addLibraryPath(.{ .path = "/path/to/shared_object" });

    // for cudart and nvrt libraries
    exe.addLibraryPath(.{ .path = "/usr/local/cuda/lib64" });

    exe.linkSystemLibrary("cuda");
    exe.linkSystemLibrary("cudart");
    exe.linkSystemLibrary("nvrt");
    exe.linkSystemLibrary("kernels");  // this is the one we made
    exe.linkLibC();

Again, to link to libkernels.so like we compiled above, you just need the string kernels.

And that’s it! It builds for me - now back to the main show.

4 Likes