CUDA可分离编译搭配共享库出现无效设备函数/段错误问题
问题根因
你遇到的报错核心原因是公共CUDA设备代码被重复打包进了多个共享库,触发CUDA运行时的符号注册冲突:
- 当前构建流程里,
liba.so和libb.so都完整打包了common.cu.o的设备代码,且各自做了设备链接生成了独立的设备代码段 - 程序运行时动态加载两个共享库,CUDA运行时注册设备符号时,
common()设备函数的符号出现重复,内核启动时寻址到错误的设备地址,最终抛出invalid device function错误或段错误 - 静态库场景不会报错的原因是主程序链接静态库时会做符号合并,不会出现重复的设备符号
可行解决方案
不需要修改A、B库的编译类型,也不需要合并库,只要把公共CUDA代码单独抽成独立的共享库即可,改造逻辑如下:
- 将公共CUDA代码(
common.cu)单独编译、设备链接为独立的共享库libcommon.so - 编译
liba.so、libb.so时,只编译自身的CUDA代码,设备链接阶段不引入common.cu.o,最终生成共享库时链接libcommon.so - 主程序链接时同时链接
liba.so、libb.so、libcommon.so
修改后的构建脚本
#!/usr/bin/env bash set -euxo pipefail CUDA_ROOT=/usr/local/cuda-10.2 NVCC=$CUDA_ROOT/bin/nvcc CC=/usr/bin/g++ GENCODE="arch=compute_75,code=sm_75" # Clean previous build rm -f *.o *.so main # Compile relocatable CUDA code $NVCC -gencode=$GENCODE -dc -Xcompiler -fPIC,-fvisibility=hidden common.cu -o common.cu.o $NVCC -gencode=$GENCODE -dc -Xcompiler -fPIC,-fvisibility=hidden a.cu -o a.cu.o $NVCC -gencode=$GENCODE -dc -Xcompiler -fPIC,-fvisibility=hidden b.cu -o b.cu.o # 单独构建公共CUDA共享库 $NVCC -gencode=$GENCODE -dlink common.cu.o -o common.dlink.o $CC -shared common.cu.o common.dlink.o -L$CUDA_ROOT/lib64 -lcudart -o libcommon.so # Build shared library A:设备链接不引入common.cu.o,链接时依赖libcommon.so $NVCC -gencode=$GENCODE -dlink a.cu.o -o a.dlink.o $CC -shared a.cu.o a.dlink.o -L. -lcommon -L$CUDA_ROOT/lib64 -lcudart -o liba.so # Build shared library B:同A的逻辑 $NVCC -gencode=$GENCODE -dlink b.cu.o -o b.dlink.o $CC -shared b.cu.o b.dlink.o -L. -lcommon -L$CUDA_ROOT/lib64 -lcudart -o libb.so # Build final executable:链接三个共享库 $CC main.cpp -L. -la -lb -lcommon -o main # Run it LD_LIBRARY_PATH=. ./main
改造后运行输出为预期结果:
Running A: 579 Running B: 444
额外说明
如果你的项目中公共CUDA代码无法单独抽库,也可以通过给CUDA设备符号添加隐藏可见性规避冲突:编译所有CUDA代码时给nvcc添加参数-Xcompiler -fvisibility=hidden,仅对外暴露需要的主机接口,也可以解决符号重复的问题。但单独抽公共共享库的方案更符合多共享库复用代码的最佳实践,后续维护成本更低。
内容的提问来源于stack exchange,提问作者user1011113
相关产品推荐
相关产品推荐

