在调查可重定位设备代码的一些问题时,我遇到了一些我不太理解的东西。
这是幻灯片6上所示内容的一个用例。我使用Robert Crovella的答案作为复制代码的基础。想法是我们有一些编译成静态库(例如某些数学/工具箱库)的可重定位设备代码,并且我们想要在程序的另一个设备库中使用该预编译库的某些函数:
但是当我生成
链接器找不到我们的虚拟函数
链接器成功了,之后一切都正常工作。
进一步调查:
符号在存档的
这是幻灯片6上所示内容的一个用例。我使用Robert Crovella的答案作为复制代码的基础。想法是我们有一些编译成静态库(例如某些数学/工具箱库)的可重定位设备代码,并且我们想要在程序的另一个设备库中使用该预编译库的某些函数:
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.so test.o test_dlink.o libutil.a -L${CUDA_LIBDIR} -lcudart
但是当我生成
test_dlink.o
时,会出现以下错误:nvlink error : Undefined reference to '_Z9my_squarei' in 'test.o'
链接器找不到我们的虚拟函数
my_square(int)
。如果我们使用(假设我们可以访问util.o
):nvcc ${NVCC_FLAGS} -dlink test.o util.o -o test_dlink.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
调用)似乎找不到它。为什么会这样?根据官方文档的说法:
当然,我们可以提取对象文件并与其链接:设备链接器有能力读取静态主机库格式(Linux和Mac上的.a,Windows上的.lib)。
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
时是否存在错误?
请注意,此测试是在Arch Linux上使用CUDA 6.5进行的。
编辑:使用带注释的代码修复
Makefile
NVCC_FLAGS=-m64 -arch=sm_20 -Xcompiler '-fPIC'
CUDA_LIBDIR=${CUDA_HOME}/lib64
testmain : main.cpp libtest.so
g++ -c main.cpp
g++ -o testmain -L. -ldl -Wl,-rpath,. -ltest -L${CUDA_LIBDIR} -lcudart main.o
libutil.a : util.cu util.cuh
nvcc ${NVCC_FLAGS} -dc util.cu
# ---> FOLLOWING LINES THAT WERE WRONG <---
# nvcc ${NVCC_FLAGS} -dlink util.o -o util_dlink.o
# nvcc ${NVCC_FLAGS} -lib util.o util_dlink.o -o libutil.a
# INSTEAD:
nvcc ${NVCC_FLAGS} -lib util.o -o libutil.a
# Assuming util is an external library, so util.o is not available
rm util.o
libtest.so : test.cu test.h libutil.a util.cuh
nvcc ${NVCC_FLAGS} -dc test.cu
# Use NVCC for device linking + G++
nvcc -v ${NVCC_FLAGS} -dlink test.o libutil.a -o test_dlink.o
g++ -shared -o libtest.so test.o test_dlink.o libutil.a -L${CUDA_LIBDIR} -lcudart
# Or let NVCC generate the shared library
#nvcc -v ${NVCC_FLAGS} -shared -L. -lutil test.o -o libtest.so
clean :
rm -f testmain *.o *.a *.so
test.h
#ifndef TEST_H
# define TEST_H
int my_test_func();
#endif //! TEST_H
test.cu
#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;
}
util.cuh
#ifndef UTIL_CUH
# define UTIL_CUH
__device__ int my_square (int a);
#endif //! UTIL_CUH
util.cu
#include "util.cuh"
__device__ int my_square (int a)
{
return a * a;
}
main.cpp
#include "test.h"
int main()
{
my_test_func();
return 0;
}