Cuda nvlink、可重定位设备代码和静态设备库

Cuda nvlink、可重定位设备代码和静态设备库,cuda,linker,nvcc,Cuda,Linker,Nvcc,在调查可重定位设备代码的一些问题时,我偶然发现了一些我不太理解的东西 这是图中所示内容的用例。我用a作为复制代码的基础。我们的想法是将一些可重新定位的设备代码编译成一个静态库(例如,一些数学/工具箱库),我们希望将该预编译库的一些函数用于我们程序的另一个设备库: libutil.a ---> libtest.so ---> test_pgm 假设此外部库包含以下函数: __device__ int my_square (int a); libutil.a例如生成如下(在另一个项目

在调查可重定位设备代码的一些问题时,我偶然发现了一些我不太理解的东西

这是图中所示内容的用例。我用a作为复制代码的基础。我们的想法是将一些可重新定位的设备代码编译成一个静态库(例如,一些数学/工具箱库),我们希望将该预编译库的一些函数用于我们程序的另一个设备库:

libutil.a ---> libtest.so ---> test_pgm
假设此外部库包含以下函数:

__device__ int my_square (int a);
libutil.a
例如生成如下(在另一个项目中):

nvcc${nvcc_FLAGS}-dc util.cu
nvcc${nvcc_FLAGS}-dlink util.o-o util_dlink.o
nvcc${nvcc_FLAGS}-lib util_dlink.o util.o-o libutil.a
然后,在我们的项目中,要生成
libtest.so

nvcc${nvcc_FLAGS}-dc test.cu
nvcc${nvcc_FLAGS}-dlink test.o libutil.a-o test_dlink.o
g++-shared-Wl,-soname,libtest.so-o libtest.o test_dlink.o libutil.a-L${CUDA_LIBDIR}-lcudart
但是我在生成
test\u dlink.o
时遇到以下错误:

nvlink error   : Undefined reference to '_Z9my_squarei' in 'test.o'
nvcc ${NVCC_FLAGS} -dlink test.o util.o -o test_dlink.o
链接器找不到我们的伪
my_square(int)
函数。如果我们改为使用(假设我们可以访问
util.o
):

链接器成功,之后一切正常

进一步调查:

$ nm -C libutil.a

util_dlink.o:
                 U atexit
                 U __cudaRegisterFatBinary
0000000000000015 T __cudaRegisterLinkedBinary_39_tmpxft_0000106a_00000000_6_util_cpp1_ii_843d693d
  ...

util.o:
                 U __cudaInitModule
                 U __cudaRegisterLinkedBinary_39_tmpxft_0000106a_00000000_6_util_cpp1_ii_843d693d
  ...
0000000000000015 T my_square(int)
  ...
符号在存档的
util.o
中,但是
nvlink
(由
nvcc
调用)似乎找不到它。为什么呢?根据报告:

设备链接器能够读取静态主机库 格式(.a在Linux和Mac上,.lib在Windows上)

我们当然可以提取目标文件并与之链接:

ar x libutil.a`ar t libutil.a | grep-v“dlink”`
nvcc${nvcc_FLAGS}-dlink test.o util.o-o test_dlink.o
但这并不是预期的解决方案。。。那么我在这里错过了什么?另一个解决这个问题的
nvcc
选项?生成
libutil.a
和/或
libtest.so
时是否有错误

注意,这是在ArchLinux上用CUDA6.5测试的

编辑:修复了带有注释行的复制代码 生成文件 test.cu util.cu main.cpp
我建议在问题中加入一个完整的简单示例,正如我在下面所做的那样。不支持代码的外部链接。当它们过时时,问题就变得不那么有价值了

是的,您在生成
libutil时出错。
创建具有公开设备链接的静态库与创建(根据定义)没有公开设备链接的共享库不同。注意我在你链接的前一个问题中提到的“CUDA免费包装器”。本问题中的示例公开了设备链接,因为
my_square
位于库中,但被库外部的代码使用

查看,您将找到一个生成设备可链接静态库的库。静态库创建中没有设备链接步骤。设备链接步骤在最终可执行文件创建时完成(或者在本例中,在so的创建时,即“CUDA边界”)。静态库创建中的“额外”设备链接操作是您观察到的错误的最接近的原因

下面是一个充分发挥作用的示例:

$ cat util.h

__device__ float my_square(float);

$ cat util.cu

__device__ float my_square(float val){ return val*val;}

$ cat test.h

float dbl_sq(float val);

$ cat test.cu
#include "util.h"

__global__ void my_dbl_sq(float *val){
  *val = 2*my_square(*val);
}

float dbl_sq(float val){
  float *d_val, h_val;
  cudaMalloc(&d_val, sizeof(float));
  h_val = val;
  cudaMemcpy(d_val, &h_val, sizeof(float), cudaMemcpyHostToDevice);
  my_dbl_sq<<<1,1>>>(d_val);
  cudaMemcpy(&h_val, d_val, sizeof(float), cudaMemcpyDeviceToHost);
  return h_val;
}
$ cat main.cpp
#include <stdio.h>
#include "test.h"

int main(){

  printf("%f\n", dbl_sq(2.0f));
  return 0;
}
$ nvcc -arch=sm_35 -Xcompiler -fPIC -dc util.cu
$ nvcc -arch=sm_35 -Xcompiler -fPIC -lib util.o -o libutil.a
$ nvcc -arch=sm_35 -Xcompiler -fPIC -dc test.cu
$ nvcc -arch=sm_35 -shared -Xcompiler -fPIC -L. -lutil test.o -o libtest.so
$ g++ -o main main.cpp libtest.so
$ cuda-memcheck ./main
========= CUDA-MEMCHECK
8.000000
========= ERROR SUMMARY: 0 errors
$
$cat util.h
__设备浮动我的正方形(浮动);
$cat util.cu
__设备uuu_u平方浮动(float val){return val*val;}
$cat test.h
浮动dbl_sq(浮动val);
$cat test.cu
#包括“util.h”
__全局\uuuuu无效我的\uDBL\uSQ(浮动*val){
*val=2*my_square(*val);
}
浮动dbl_sq(浮动val){
浮动*d_val,h_val;
cudaMalloc(&d_val,sizeof(float));
h_val=val;
cudaMemcpy(d_val和h_val,sizeof(float),cudaMemcpyHostToDevice);
my_dbl_sq(d_val);
cudaMemcpy(&h_val,d_val,sizeof(float),cudaMemcpyDeviceToHost);
返回h_val;
}
$cat main.cpp
#包括
#包括“test.h”
int main(){
printf(“%f\n”,dbl_sq(2.0f));
返回0;
}
$nvcc-arch=sm_35-Xcompiler-fPIC-dc util.cu
$nvcc-arch=sm_35-Xcompiler-fPIC-libutil.o-o libutil.a
$nvcc-arch=sm_35-Xcompiler-fPIC-dc test.cu
$nvcc-arch=sm_35-shared-Xcompiler-fPIC-L-lutil test.o-o libtest.so
$g++-o main.cpp libtest.so
$cuda memcheck./main
==========CUDA-MEMCHECK
8
======错误摘要:0个错误
$

在本例中,设备链接在用于创建.so库的
nvcc
调用中自动发生。在这里的示例中,我已经将我的
LD\u LIBRARY\u PATH
环境变量设置为包含我的工作目录。在CentOS 6.2上使用CUDA 6.5进行测试(请注意,在创建可执行文件期间,可以执行多个设备链接操作,但这些设备链接操作必须位于单独的链接域内,即用户代码或用户代码入口点不能在域之间共享。此处不是这种情况。)

这似乎是一个直截了当的名称混乱问题。注意链接器正在查找一个被损坏的C++符号名称,并且该对象包含一个C风格未加密的名称。这里没有足够的信息来告诉您如何/如何修复。感谢您指出,在生成
libutil.a
时,设备链接是无用的,并且在与它链接时需要这样做。只需删除该步骤即可解决此问题。在那之后,我不需要在那个虚拟示例中依赖于
nvcc
,但是
nvcc
是否做了一些事情,证明总是使用它来生成类似的
libtest是正确的。那么
nvcc
生成对
g++
的调用,其中包含一些额外的链接器选项(
-lcudadevrt-lcudart\u static-lrt-lpthread-ldl
)。请注意,我正在调查一个复杂的基于CMake的CUDA构建链中的一个奇怪错误,因此这实际上很重要。我们可能需要深入了解您(或我)案例的细节。在我的例子中,我遇到了
libtest的问题。因此
g++
#ifndef TEST_H
# define TEST_H

int my_test_func();

#endif //! TEST_H
#include <stdio.h>

#include "test.h"
#include "util.cuh"

#define DSIZE 1024
#define DVAL 10
#define SQVAL 3
#define nTPB 256

#define cudaCheckErrors(msg)                             \
  do {                                                   \
    cudaError_t __err = cudaGetLastError();              \
    if (__err != cudaSuccess) {                          \
      fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
              msg, cudaGetErrorString(__err),            \
              __FILE__, __LINE__);                       \
      fprintf(stderr, "*** FAILED - ABORTING\n");        \
      exit(1);                                           \
    }                                                    \
  } while (0)

__global__ void my_kernel(int *data){
  int idx = threadIdx.x + (blockDim.x *blockIdx.x);
  if (idx < DSIZE) data[idx] =+ DVAL + my_square (SQVAL);
}

int my_test_func()
{
  int *d_data, *h_data;
  h_data = (int *) malloc(DSIZE * sizeof(int));
  if (h_data == 0) {printf("malloc fail\n"); exit(1);}
  cudaMalloc((void **)&d_data, DSIZE * sizeof(int));
  cudaCheckErrors("cudaMalloc fail");
  for (int i = 0; i < DSIZE; i++) h_data[i] = 0;
  cudaMemcpy(d_data, h_data, DSIZE * sizeof(int), cudaMemcpyHostToDevice);
  cudaCheckErrors("cudaMemcpy fail");
  my_kernel<<<((DSIZE+nTPB-1)/nTPB), nTPB>>>(d_data);
  cudaDeviceSynchronize();
  cudaCheckErrors("kernel");
  cudaMemcpy(h_data, d_data, DSIZE * sizeof(int), cudaMemcpyDeviceToHost);
  cudaCheckErrors("cudaMemcpy 2");
  for (int i = 0; i < DSIZE; i++)
    if (h_data[i] != DVAL + SQVAL*SQVAL)
    {
      printf("Results check failed at offset %d, data was: %d, should be %d\n",
             i, h_data[i], DVAL);
      exit(1);
    }
  printf("Results check passed!\n");
  return 0;
}
#ifndef UTIL_CUH
# define UTIL_CUH

__device__ int my_square (int a);

#endif //! UTIL_CUH
#include "util.cuh"

__device__ int my_square (int a)
{
  return a * a;
}
#include "test.h"

int main()
{
  my_test_func();
  return 0;
}
$ cat util.h

__device__ float my_square(float);

$ cat util.cu

__device__ float my_square(float val){ return val*val;}

$ cat test.h

float dbl_sq(float val);

$ cat test.cu
#include "util.h"

__global__ void my_dbl_sq(float *val){
  *val = 2*my_square(*val);
}

float dbl_sq(float val){
  float *d_val, h_val;
  cudaMalloc(&d_val, sizeof(float));
  h_val = val;
  cudaMemcpy(d_val, &h_val, sizeof(float), cudaMemcpyHostToDevice);
  my_dbl_sq<<<1,1>>>(d_val);
  cudaMemcpy(&h_val, d_val, sizeof(float), cudaMemcpyDeviceToHost);
  return h_val;
}
$ cat main.cpp
#include <stdio.h>
#include "test.h"

int main(){

  printf("%f\n", dbl_sq(2.0f));
  return 0;
}
$ nvcc -arch=sm_35 -Xcompiler -fPIC -dc util.cu
$ nvcc -arch=sm_35 -Xcompiler -fPIC -lib util.o -o libutil.a
$ nvcc -arch=sm_35 -Xcompiler -fPIC -dc test.cu
$ nvcc -arch=sm_35 -shared -Xcompiler -fPIC -L. -lutil test.o -o libtest.so
$ g++ -o main main.cpp libtest.so
$ cuda-memcheck ./main
========= CUDA-MEMCHECK
8.000000
========= ERROR SUMMARY: 0 errors
$