Unable to dynamically load the "cuda" shared library
主线位置:直接计算主线换成 CUDA 引擎之前的第一步:发现设备、打开引擎。CUDA 引擎的结构和没有驱动时三个入口各自的行为见 CUDA(只读源码) 的「没有驱动时」。本机没有 CUDA,下面只复现「没有驱动」这一种情形;有驱动时的行为只读了源码。
flowchart TB
start["选引擎"] --> dev["cuda_devices"]
dev --> drv{"有 libcuda"}
drv -->|有| ok["Ok 设备列表"]
drv -->|没有| panic["cudarc panic"]
panic --> exit["exit 101"]
start --> probe["gpu_available"]
probe -->|false| cpu["用 CpuEngine"]
probe -->|true| dev
实验 crate exp-tenferro8/pit2-cuda(从 gpu-cuda 拷来,依赖 tenferro-gpu 的 cuda feature),debug,M2 Max、macOS 27、rustc 1.99.0,本机没有 CUDA 驱动。panic = "abort" 的对照用 CARGO_PROFILE_DEV_PANIC=abort 另编了一份。
现象
照上游例子 cuda_quickstart(用户指南 devices-and-gpu.md 的「CUDA Quickstart」一节原样收录)写,第一行就是 cuda_devices()?:
fn quickstart() -> Result<(), Box<dyn std::error::Error>> {
let Some(device) = cuda_devices()?.into_iter().next() else {
println!("no CUDA device");
return Ok(());
};
let _engine = CudaEngine::open(device.id())?;
Ok(())
}
thread 'main' (9769242) panicked at .../cudarc-0.19.10/src/lib.rs:200:5:
Unable to dynamically load the "cuda" shared library - searched for library names: ["libcuda.dylib", "libcuda64.dylib", "libcuda64_12.dylib", ...]. Ensure that `LD_LIBRARY_PATH` has the correct path to the installed library. If the shared library is present on the system under a different name than one of those listed above, please open a GitHub issue.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101
(库名列表有 34 项,中间省略。)函数签名是 Result,调用方写了 ?,期望在没有 CUDA 的机器上拿到一个错误、退回 CPU;实际是 panic,? 根本没机会起作用,连 no CUDA device 那个分支也走不到。程序要是想「有 GPU 用 GPU、没有用 CPU」,在 Mac 和没装驱动的 CI 机器上会直接退出。CudaEngine::open 在没有驱动时同样 panic(CUDA(只读源码) 用 RUST_BACKTRACE=1 追过它的路径:open 先调 cuda_devices)。
出处
cuda_devices 的文档写的是返回错误:
/// # Errors
///
/// Returns [`CudaDeviceError::Discovery`] for driver initialization, device
/// enumeration, or device-name lookup failures.
// ...
pub fn cuda_devices() -> Result<Vec<CudaDeviceInfo>, CudaDeviceError> {
discover_with(&CudaDriverApi)
}
discover_with 的第一步是 driver.initialize(),即 cudarc::driver::result::init()。cudarc 0.19.10 开了动态加载,第一次调驱动函数时才去找 libcuda,找不到就在它自己的 src/lib.rs 第 200 行 panic,map_err 根本拿不到一个 Err。
gpu_available 走的是另一条路:
pub fn gpu_available() -> bool {
let available = t4a_cuda_runtime::device_count()
.map(|count| count > 0)
.unwrap_or(false);
t4a-cuda-runtime 0.1.0 先用 is_culib_present() 探测驱动库,不在就返回类型化的 DriverUnavailable,这里当成 false。同一个 crate 里,两个入口一个走 t4a-cuda-runtime、一个直接调 cudarc,行为不一样。上游知道 cudarc 会 panic:CudaDeviceInfo 几个方法的文档示例都写着「cudarc panics when the CUDA driver library is absent, so the call is guarded」,用 catch_unwind(cuda_devices) 包起来;但 cuda_devices 自己的文档和 cuda_quickstart 都直接 ?。用户指南后面另一段 CUDA 代码先 if !gpu_available() 再 cuda_devices()?,两段写法不一致。函数文档承诺返回错误、实际 panic,这是 0.8.0 仍在的上游 bug(0.7.1 一样,那时的 CUDA 后端还是 CubeCL)。
修法
先探测,再发现设备:
fn guarded() -> Result<(), Box<dyn std::error::Error>> {
if !gpu_available() {
println!("gpu_available() = false, using the CPU engine");
return Ok(());
}
quickstart()
}
gpu_available() = false, using the CPU engine
exit=0
或者照 CudaDeviceInfo 文档示例的写法,用 catch_unwind 包住:
match catch_unwind(cuda_devices) {
Ok(r) => println!("cuda_devices() returned {:?}", r.map(|d| d.len())),
Err(_) => println!("cuda_devices() panicked; treating as no CUDA"),
}
thread 'main' (9769254) panicked at .../cudarc-0.19.10/src/lib.rs:200:5:
Unable to dynamically load the "cuda" shared library - searched for library names: [...]. ...
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
cuda_devices() panicked; treating as no CUDA
exit=0
gpu_available()更干净:不打印 panic 信息。它在 0.8.0 标了#[doc(hidden)],文档里看不到,但它是公开函数,用户指南也在用。设了环境变量TENFERRO_REQUIRE_GPU时它找不到设备会故意 panic(给 GPU 测试机用的),普通程序别设。catch_unwind版本能工作,但默认的 panic hook 仍然把整段消息打到 stderr。用panic = "abort"编译的程序根本接不住:同一个catch分支以 134 退出(SIGABRT),而gpu_available()那个分支照样打印false并以 0 退出。- 有驱动时
gpu_available()会初始化驱动(cuInit)再数设备;启动时调一次、记住结果即可。
怎么确认没中招
- 在一台没有 CUDA 的机器(Mac 就行)上把选引擎的那段代码跑一次,进程应当以 0 退出并走 CPU 分支。
- 搜
cuda_devices(和CudaEngine::open(,确认每一处之前都有gpu_available()或catch_unwind。 - 检查 profile 里有没有
panic = "abort";有的话只能用gpu_available()。
小结
- 没有 CUDA 驱动时,
cuda_devices()和CudaEngine::open在 cudarc 里 panic(Unable to dynamically load the "cuda" shared library),进程以 101 退出,不返回文档承诺的CudaDeviceError。0.8.0 仍在的上游 bug。 - 上游例子
cuda_quickstart直接cuda_devices()?,在没有驱动的机器上同样会崩;gpu_available()走 t4a-cuda-runtime 的探测,返回false。 - 先调
gpu_available(),或用catch_unwind包住;panic = "abort"时只有前者可用。