首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么即使在使用-cudart静态编译时,库用户仍然需要链接到cuda运行时?

为什么即使在使用-cudart静态编译时,库用户仍然需要链接到cuda运行时?
EN

Stack Overflow用户
提问于 2020-07-12 11:16:10
回答 1查看 1.2K关注 0票数 0

我有一些使用nvcc编译到静态库中的简单的cuda代码,还有一些我正在用g++编译的用户代码,并针对先前编译的静态库进行链接。在尝试链接时,即使在cudaMalloc编译命令行中使用-cudart static选项,也会得到类似nvcc的链接器错误。

这是我的代码:

代码语言:javascript
运行
复制
//kern.hpp
#include <cstddef>

class Kern
{
    private:
        float* d_data;
        size_t size;

    public:
        Kern(size_t s);
        ~Kern();
        void set_data(float *d); 
};
代码语言:javascript
运行
复制
//kern.cu
#include <iostream>
#include <kern.hpp>

__global__ void kern(float* data, size_t size)
{
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if(idx < size) 
    {
        data[idx] = 0;
    }
} 

Kern::Kern(size_t s) : size(s)
{
    cudaMalloc((void**)&d_data, size*sizeof(float));
}

Kern::~Kern()
{
    cudaFree(d_data);
}

void Kern::set_data(float* d)
{
    size_t grid_size = size;
    std::cout << "Starting kernel with grid size " << grid_size << " and block size " << 1 <<
        std::endl;
    kern<<<grid_size, 1>>>(d_data, size);
    cudaError_t err = cudaGetLastError();
    if(err != cudaSuccess)
        std::cout << "ERROR: " << cudaGetErrorString(err) << std::endl;
    cudaDeviceSynchronize();
    cudaMemcpy((void*)d, (void*)d_data, size*sizeof(float), cudaMemcpyDeviceToHost);
    cudaDeviceSynchronize();
}
代码语言:javascript
运行
复制
//main.cpp
#include <iostream>
#include <kern.hpp>

int main(int argc, char** argv)
{
    std::cout << "starting" << std::endl;
    Kern k(256);
    float arr[256];
    k.set_data(arr);
    bool ok = true;
    for(int i = 0; i < 256; ++i) ok &= arr[i] == 0;
    std::cout << (ok ? "done" : "wrong") << std::endl;
}

我正在用nvcc编译kern,如下所示:

代码语言:javascript
运行
复制
nvcc -I ./ -lib --compiler-options '-fPIC' -o libkern.a kern.cu -cudart static

然后主要使用g++,如下所示:

代码语言:javascript
运行
复制
g++ -o main main.cpp -I ./ -L. -L/opt/cuda/lib64 -lkern

它会产生错误:

代码语言:javascript
运行
复制
/usr/bin/ld: ./libkern.a(tmpxft_00001d30_00000000-8_kern.o): in function `Kern::Kern(unsigned long)':
tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x4d): undefined reference to `cudaMalloc'
/usr/bin/ld: ./libkern.a(tmpxft_00001d30_00000000-8_kern.o): in function `Kern::~Kern()':
tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x6b): undefined reference to `cudaFree'
/usr/bin/ld: ./libkern.a(tmpxft_00001d30_00000000-8_kern.o): in function `Kern::set_data(float*)':
tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x152): undefined reference to `__cudaPushCallConfiguration'
/usr/bin/ld: tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x175): undefined reference to `cudaGetLastError'
/usr/bin/ld: tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x1a1): undefined reference to `cudaGetErrorString'
/usr/bin/ld: tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x1c6): undefined reference to `cudaDeviceSynchronize'
/usr/bin/ld: tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x1ee): undefined reference to `cudaMemcpy'
/usr/bin/ld: tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x1f3): undefined reference to `cudaDeviceSynchronize'
/usr/bin/ld: ./libkern.a(tmpxft_00001d30_00000000-8_kern.o): in function `__cudaUnregisterBinaryUtil()':
tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x24e): undefined reference to `__cudaUnregisterFatBinary'
/usr/bin/ld: ./libkern.a(tmpxft_00001d30_00000000-8_kern.o): in function `__nv_init_managed_rt_with_module(void**)':
tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x269): undefined reference to `__cudaInitModule'
/usr/bin/ld: ./libkern.a(tmpxft_00001d30_00000000-8_kern.o): in function `__device_stub__Z4kernPfm(float*, unsigned long)':
tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x305): undefined reference to `__cudaPopCallConfiguration'
/usr/bin/ld: ./libkern.a(tmpxft_00001d30_00000000-8_kern.o): in function `__nv_cudaEntityRegisterCallback(void**)':
tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x430): undefined reference to `__cudaRegisterFunction'
/usr/bin/ld: ./libkern.a(tmpxft_00001d30_00000000-8_kern.o): in function `__sti____cudaRegisterAll()':
tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x44b): undefined reference to `__cudaRegisterFatBinary'
/usr/bin/ld: tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x47c): undefined reference to `__cudaRegisterFatBinaryEnd'
/usr/bin/ld: ./libkern.a(tmpxft_00001d30_00000000-8_kern.o): in function `cudaError cudaLaunchKernel<char>(char const*, dim3, dim3, void**, unsigned long, CUstream_st*)':
tmpxft_00001d30_00000000-5_kern.cudafe1.cpp:(.text+0x4d9): undefined reference to `cudaLaunchKernel'
collect2: error: ld returned 1 exit status

但如果我这样做的话:

代码语言:javascript
运行
复制
g++ -o main main.cpp -I ./ -L. -L/opt/cuda/lib64 -lkern -lcudart

一切都正常。我的问题是,既然我在-cudart static编译行中有一个nvcc,那么libkern.a不应该已经解析了用于cuda运行时的符号吗?为什么-lcudartg++行中仍然是必需的?

另外,如果我将libkern.a更改为共享对象,则不能链接到g++行中的cuda运行时。也就是说,以下工作:

代码语言:javascript
运行
复制
nvcc -I ./ -shared --compiler-options '-fPIC' -o libkern.so kern.cu -cudart static
g++ -o main main.cpp -I ./ -L. -L/opt/cuda/lib64 -lkern

为什么静态库版本会失败,但是共享对象版本会工作呢?

请注意,在用-cudart static行中的-lcudart_static替换nvcc之后,我尝试了上述场景,并且在进行替换时行为没有改变。这是预期的,因为这两个选项本质上做了同样的事情,对吗?

我在linux上。

代码语言:javascript
运行
复制
nvcc --version
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2019 NVIDIA Corporation
Built on Wed_Oct_23_19:24:38_PDT_2019
Cuda compilation tools, release 10.2, V10.2.89
代码语言:javascript
运行
复制
g++ --version
g++ (GCC) 10.1.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

任何帮助和/或澄清都是非常感谢的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-07-12 15:06:04

如果研究nvcc文档,很明显,-lib选项创建静态库(并指定没有链接),而-shared选项则创建共享库并指定链接。例如,节选:

4.2.2.1.-链接(-link)指定默认行为:编译和链接所有输入文件。4.2.2.2.-lib (-lib)在必要时将所有输入文件编译成对象文件,并将结果添加到指定的库输出文件中。4.2.3.11.-共享(-shared)在链接期间生成共享库。使用选项-链接器-选项时,其他链接选项是需要更多的控制。

我认为这与典型的gcc/g++用法大致一致。如果您在"g++创建静态库“上进行谷歌搜索,您将得到任意数量的参考文献,这表明您基本上应该这样做:

代码语言:javascript
运行
复制
g++ -c my_source_file.cpp ...
ar ...

换句话说,指定了源到对象的编译,但没有指定链接。举个例子,cudaMalloc是CUDA运行时库的一部分,连接将在链接阶段完成。

nvcc是一个非常复杂的工具,但是我们应该记住,对于某些函数,它主要使用已安装的主机工具链。这包括主机代码的编译,也包括最后的链接阶段。

结合这一点,我相信您在这里要做的是“部分”链接或增量链接。在最后的链接阶段之前执行一些最后的链接阶段。

GNU (同样,默认情况下,nvcc将在linux上使用什么) 支持这一点,所以如果我们不考虑编译可重新定位的设备代码,应该可以按如下方式执行:

代码语言:javascript
运行
复制
$ nvcc  -Xcompiler '-fPIC' -I.  -c kern.cu
$ ld -o kern.ro -r kern.o -L/usr/local/cuda/lib64 -lcudart_static -lculibos
$ ar rs libkern.a kern.ro
ar: creating libkern.a
$ g++ -o main main.cpp  -I ./ -L.  -lkern -lpthread -lrt -ldl
$ cuda-memcheck ./main
========= CUDA-MEMCHECK
starting
Starting kernel with grid size 256 and block size 1
done
========= ERROR SUMMARY: 0 errors
$

备注:

  1. -lpthread -lrt -ldl是cudart/culibos的标准库依赖项,因此需要在最后的链接阶段提供它们,但它们不依赖于任何CUDA工具包项。如果您希望从增量链接对象中删除这些依赖项,我认为这是一个单独的问题,与CUDA无关。
  2. 对于这种简单的情况,归档步骤(创建库)并不是必需的。我们可以直接将增量链接(-r)对象kern.ro传递到最后的编译/链接步骤。
  3. 请注意,您的CUDA安装显然位于不同的位置,因此上述一些库路径(-L)可能需要更改。
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62860375

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档