CUDA.jl 6.4: NVIDIA Jetson support, and compiled code caching


Tim Besard

CUDA.jl 6.4 greatly improves support for NVIDIA Jetson boards, and further enhances caching of compiled GPU code for much faster TTFX.

NVIDIA Jetson support

One of the highlights is that NVIDIA Jetson devices are now properly supported. The CUDA.jl binary packages have been reworked to provide builds for older CUDA versions so that artifacts can be used, and many fixes have been applied to improve compatibility with Tegra hardware.

Since many Jetson devices rely on older CUDA toolkits, we have relaxed our requirement for the CUDA toolkit version: version 12 is still the minimum for full support, but we have reinstated best-effort support for CUDA 10 and 11, as used by default on older Jetson boards such as the Jetson Nano.

The following JetPack generations have been tested on:

Board (JetPack, L4T)System CUDACUDA.jl 6.4 uses
Jetson Nano, TX1, TX2 (JetPack 4, r32)10.2system driver, CUDA 10.2 artifacts
Xavier, Xavier NX (JetPack 5, r35)11.4bundled CUDA 12.2 L4T driver, CUDA 12.5 artifacts
Orin (JetPack 6, r36)12.xbundled CUDA 12.9 L4T driver, CUDA 12.9 artifacts
Orin, Thor (JetPack 7, r39)13.xsystem driver, CUDA 13.3 artifacts

The CUDA.jl README now clearly documents the level of support for each Jetson board and CUDA toolkit version in general.

JetPack 6+: full support on Jetson SBCs

Orin and Thor devices are fully supported. All CUDA.jl tests are expected to pass, both the core functionality that only relies on the CUDA toolkit, and any external packages that integrate with vendor libraries like cuDNN or cuTENSOR.

JetPack 5: limited support through CUDA 12

Xavier boards ship with CUDA 11.4 and, unlike a desktop GPU, you cannot simply install a newer driver: the kernel-mode driver is part of the L4T BSP. NVIDIA does publish a forward-compatibility driver for the r35 kernel driver, though, and CUDADriverjll now bundles it, enabling use of CUDA toolkit 12.5 on these devices.

With the upgraded driver and CUDA toolkit 12.5, all core CUDA.jl functionality is expected to work correctly. However, some external vendor libraries, specifically cuDNN and cuTENSOR, lack support for the hardware present on Xavier boards, or at least the versions packaged for CUDA.jl do. As a result, we do not consider Xavier boards as fully supported, however, most users should not encounter significant issues in most common use cases.

JetPack 4: best-effort support with several limitations

Previous versions of CUDA.jl used to flat-out reject the older toolkits found on JetPack 4:

ERROR: LoadError: CUDA.jl requires PTX 8.0.0, which is not supported by ptxas 10.2.89
ERROR: Failed to precompile CUDACore [bd0ed864-bdfe-4181-a5ed-ce625a5fdea2]

After fixing CUDA.jl and adding binaries for the older toolkits, the Jetson Nano can now support CUDA.jl, even without a local installation of the CUDA toolkit. The old toolkit and driver causes several issues though, which manifest as certain pieces of core functionality not being supported on these boards:

We aim to keep CUDA.jl reasonably working on these boards, but the support is best-effort, implying that we will not degrade the experience for users on fully supported hardware or otherwise do significant development specifically to improve support for these older boards.

Caching compiled code

CUDA.jl v6.3 introduced the ability to cache inferred GPU code, greatly improving the so-called TTFX for GPU applications. With CUDA.jl v6.4 we take this further by actually caching compiled GPU code (CUBINs) as well. This relies on work in GPUCompiler.jl to make compiled code relocatable across sessions, and as such is available to any GPU back-end that opts in.

To evaluate, let's go back to the example from the previous blog post:

module Blur

using CUDA
using PrecompileTools

function blur_kernel!(dst, src, ::Val{R}) where R
    i = (blockIdx().x - 1) * blockDim().x + threadIdx().x
    if i <= length(dst)
        acc = zero(eltype(src))
        for k in -R:R
            @inbounds acc += src[clamp(i + k, 1, length(src))] / (1 + abs(k))
        end
        @inbounds dst[i] = sqrt(abs(acc))
    end
    return
end

function blur(src, ::Val{R} = Val(4)) where R
    dst = similar(src)
    @cuda threads=256 blocks=cld(length(dst), 256) blur_kernel!(dst, src, Val(R))
    return dst
end

@setup_workload begin
    @compile_workload begin
        blur(CUDA.zeros(Float32, 1024))
    end
end

end

Timing the first call in a fresh session, on an RTX 5080 with Julia 1.13 and CUDA 13.4:

julia> using Blur, CUDA

julia> src = CUDA.rand(Float32, 1024);

julia> @time Blur.blur(src);
  0.004722 seconds (255 allocations: 1.052 MiB, 80.82% compilation time)

This is a significant improvement over the 0.13s it took on CUDA.jl v6.3, and the 1.3s it took without any caching at all. It is also a major step towards supporting static compilation of CUDA.jl with JuliaC.jl, though there is still lots of work to be done in that area.

Other changes

The full list is in NEWS.md and the release notes. If something in here breaks for you, please file an issue. And if you are running a Jetson board we have not tested, we would like to hear about it either way.