You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在gprbuild中配置nvcc编译CUDA代码并解决Ada与CUDA代码链接报错问题

解决Ada调用CUDA代码时的链接未定义错误

看起来你已经走对了大部分路——gprbuild的配置思路完全没问题,链接时出现的undefined reference to « inter_add_two »错误,其实是CUDA编译的一个常见坑:nvcc默认会以C++的规则处理代码,对函数名进行名字修饰(name mangling),而Ada里用Convention => C声明的导入函数,期望的是C风格的未修饰函数名,两者不匹配就导致链接器找不到符号了。

下面是具体的修复步骤,帮你搞定这个问题:

1. 给CUDA函数强制加上C链接约定

你需要在inter_add_two函数的声明和定义处,都加上extern "C"修饰,让nvcc编译时保留C风格的原始函数名,不做修饰。

修改后的kernel.cuh

#ifndef __KERNEL_CUH__
#define __KERNEL_CUH__

#include <cuda.h>

__global__ void kernel_add_two(unsigned int *a, unsigned int length);

// 用extern "C"包裹,确保C链接约定
#ifdef __cplusplus
extern "C" {
#endif
void inter_add_two(unsigned int *a, unsigned int length);
#ifdef __cplusplus
}
#endif

#endif // __KERNEL_CUH__

修改后的kernel.cu

#include "kernel.cuh"
#include <math.h>

#define THREADS_PER_BLOCK (1024)

__global__ void kernel_add_two(unsigned int *a, unsigned int length) {
   unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;
   if (tid < length)
      a[tid] += 2;
}

// 同样给函数定义加上extern "C",和头文件保持一致
extern "C"
void inter_add_two(unsigned int *a, unsigned int length) {
   unsigned int block_number = ceil(((float)length) / THREADS_PER_BLOCK);
   unsigned int *d_a;
   cudaMalloc((void**)&d_a, sizeof(unsigned int) * length);
   cudaMemcpy(d_a, a, sizeof(unsigned int) * length, cudaMemcpyHostToDevice);
   kernel_add_two<<<block_number, THREADS_PER_BLOCK>>>(d_a, length);
   cudaMemcpy(a, d_a, sizeof(unsigned int) * length, cudaMemcpyDeviceToHost);
   cudaFree(d_a);
}

2. 优化gpr项目文件的编译选项

你的gpr文件已经配置了CUDA语言支持,可以再补充一些选项,让编译更稳妥:

project Cuda_Interface is
   for Languages use ("Ada", "Cuda");
   for Source_Dirs use ("src");
   for Object_Dir use "obj";
   for Exec_Dir use ".";
   for Main use ("cuda_interface.adb");
   for Create_Missing_Dirs use "True";

   package Naming is
      for Body_Suffix("Cuda") use ".cu";
      for Spec_Suffix("Cuda") use ".cuh";
   end Naming;

   package Compiler is
      for Driver("Cuda") use "nvcc";
      -- 加上-std=c++11(或你环境支持的C++标准),以及-fPIC确保生成位置无关代码
      for Leading_Required_Switches("Cuda") use ("-c", "-std=c++11", "-fPIC");
      -- 可选:添加你的GPU架构参数,比如针对RTX 20系列用-arch=sm_75,根据实际情况调整
      for Switches("Cuda") use ("-arch=sm_75");
   end Compiler;

   package Linker is
      for Default_Switches("Ada") use ("-L/usr/local/cuda/lib64", "-lcuda", "-lcudart", "-lm");
   end Linker;

end Cuda_Interface;

3. 编译验证

现在执行gprbuild -P Cuda_Interface.gpr,应该就能正常编译和链接了。如果还是遇到问题,可以做两个检查:

  • 确认obj目录下生成了kernel.o文件,说明nvcc正确编译了你的CUDA代码
  • nm obj/kernel.o | grep inter_add_two查看符号表,应该能看到T inter_add_two(T表示这是一个全局文本段符号),如果显示的是类似_Z13inter_add_twoPjj这种长名字,说明名字修饰还没解决,需要再检查extern "C"的使用。

最后回答你最初的另一个疑问:是否必须先手动用nvcc生成目标文件再链接? 完全不需要!你当前的gpr配置已经让gprbuild自动识别.cu文件并调用nvcc编译了,只要解决名字修饰的问题,就能让整个流程自动完成。

内容的提问来源于stack exchange,提问作者Louis Etienne

火山引擎 最新活动