nvcc编译器之设备和主机独立编译(chapter 6)

news2024/11/23 20:47:22

目录

6. CUDA中的独立编译

6.1 单独编译时的代码改动

6.2 nvcc独立编译选项

 6.3 库

6.4 示例

6.5 分布编译优化

6.6 独立编译的潜在问题


6. CUDA中的独立编译

在5.0版本之前,CUDA不支持分开编译,因此CUDA代码不能访问跨文件(编译单元)的设备函数或变量, 这种编译称为全程序编译。cuda一直都支持单独编译主机侧代码,实际上,只有设备端CUDA代码在单文件中需要支持。从CUDA 5.0开始,支持了单独编译设备端代码,但旧的全程序编译模式仍然是默认的方式,所以有新的选项来触发单独编译

6.1 单独编译时的代码改动

单独编译设备端(GPU)代码所需对主机端代码的更改,与单主机端(CPU)代码分文件编译相同,即使用extern和static来控制符号的可见性。注意,之前(cuda 5.0前面)的extern,在CUDA代码中被忽略了,5.0之后则不会。通过使用static,可以在不同的文件中,使用多个具有相同名称的设备符号。由于这个原因,通过字符串名称引用符号的CUDA API调用被弃用了,取而代之,应该由使用API的地址。

6.2 nvcc独立编译选项

CUDA的工作原理是将设备端代码嵌入到主机目标对象中。 在全程序编译模式中,CUDA将可执行的设备代码嵌入到主机对象(object)。

在独立(分开)的编译中,则将可重定位设备代码嵌入到主机对象中,并通过运行nvlink(设备链接器),将所有设备代码链接到一起。 然后,nvlink的输出,再通过主机链接器与所有主机侧目标对象链接在一起,形成最终的可执行文件。

可重定位与可执行设备代码的生成是由--relocatable-device-code选项控制的.

--compile选项已经被用于控制主机端编译器对对象的编译,所以添加了一个新选项--device-c,等同于--relocable-device-code=true --compile。

需要只调用设备端链接器时,可以使用--device-link选项,它给出一个包含嵌入式可执行设备代码的主机侧目标对象。然后,它的输出必须传递给主机链接器。

或者,使用nvcc <objects>隐式调用设备端和主机端链接器。如果设备链接器没有看到任何可重定位的设备端代码,它就不会做任何事情,因而这样也是可以工作的。

图4显示了流程(nvcc --device-c具有与#unique_50/unique_50_connect_42_cuda-compiler-from-cu-to-o相同的流程)

 6.3 库

设备链接器具有读取主机侧静态库的能力(Linux和Mac OS X上的.a,Windows上.lib),但它忽略任何动态(.so或.dll)库。可以使用--library和--library-path选项将库传递给设备端和主机端链接器。当使用--library选项时,指定的库名没有库文件扩展名:

nvcc --gpu-architecture=sm_50 a.o b.o --library-path=<path> --library=foo 

或者,在windows上,不使用--library选项时可以使用带后缀名的库名。

nvcc --gpu-architecture=sm_50 a.obj b.obj foo.lib --library-path=<path>  

注意,设备链接器忽略任何没有可重定位设备代码的主机目标对象。

6.4 示例

 //---------- b.h ---------- 
 #define N 8 
 extern __device__ int g[N];
 extern __device__ void bar(void); 

//---------- b.cu ----------

#include "b.h"

__device__ int g[N];

__device__ void bar (void)

{

g[threadIdx.x]++;

}
 //---------- a.cu ---------- 
 #include <stdio.h>
 #include "b.h" 
 __global__ void foo (void) {
     __shared__ int a[N];
     a[threadIdx.x] = threadIdx.x;
     __syncthreads();
     g[threadIdx.x] = a[blockDim.x - threadIdx.x - 1]; 
     bar();
}
int main (void) {
    unsigned int i;
    int *dg, hg[N];
    int sum = 0; 
     foo<<<1, N>>>(); 
     if(cudaGetSymbolAddress((void**)&dg, g)){
         printf("couldn't get the symbol addr\n");
         return 1;
     }
     if(cudaMemcpy(hg, dg, N * sizeof(int), cudaMemcpyDeviceToHost)){
         printf("couldn't memcpy\n"); 
         return 1; 
     } 
     for (i = 0; i < N; i++) {
          sum += hg[i];
     }
     if (sum == 36) {
         printf("PASSED\n"); 
     } else {
         printf("FAILED (%d)\n", sum); 
     }
     return 0; 
 } 

Linux 环境的编译命令如下:

nvcc --gpu-architecture=sm_50 --device-c a.cu b.cu
nvcc --gpu-architecture=sm_50 a.o b.o

如果想分步触发设备和主机侧的链接器,则如下操作:

nvcc --gpu-architecture=sm_50 --device-c a.cu b.cu
nvcc --gpu-architecture=sm_50 --device-link a.o b.o --output-file device_link.o
g++ a.o b.o device_link.o --library-path=<path> --library=cudart

需要注意,所有运行的目标架构都必须传递给设备链接器(因此使用了sm_50,忽略--gpu-code使得其默认值也是cm_50),因为它指定了最终可执行文件中的生成内容(一些objects对象或库可能包含多个目标架构的设备代码,链接步骤因而会选择将哪些代码编译放入最终可执行文件中)。

如果想使用driver API来加载一个已经链接过的cubin二进制,可以直接使用--cubin生成设备端二进制代码(.cubin格式):

nvcc --gpu-architecture=sm_50 --device-link a.o b.o \
    --cubin --output-file link.cubin

使用--lib将上述目标生成库(设备链接器只支持静态库)

nvcc --gpu-architecture=sm_50 --device-c a.cu b.cu
nvcc --lib a.o b.o --output-file test.a
nvcc --gpu-architecture=sm_50 test.a

ptx码文件可以被编译到主机目标文件,并被链接使用:

nvcc --gpu-architecture=sm_50 --device.c a.ptx

使用库、主机链接器、动态机制,示例:

nvcc --gpu-architecture=sm_50 --device-c a.cu b.cu
nvcc --gpu-architecture=sm_50 --device-link a.o b.o --output-file link.o
nvcc --lib --output-file libgpu.a a.o b.o link.o
g++ host.o --library=gpu --library-path=<path> \
    --library=cudadevrt --library cudart

可以在一个主机端的可执行文件中链接多个设备端目标,只要每个设备链接是相互独立的。这种独立性要求,不能在跨设备可执行文件间共享代码,也不能共享地址(例如,可以从主机侧传递一个主机侧的函数地址给设备,以便设备侧进行回调,但这只有在设备链接时可以同时看到caller和潜在的回调callee(调用者)时才行得通。无法将一个设备可执行文件中的地址传递到另一个设备可执行文件中,因为它们是独立的地址空间)。

6.5 分布编译优化

单独编译的代码可能没有全程序代码编译那么高的性能,因为不能跨文件内联代码(inline)。

获得最佳性能的一个方法是使用链接时优化(link-time optimization),它存储中间代码,然后将中间代码链接在一起以执行高级别的优化。这可以通过--dlink-time-opt或-dlto选项支持,此选项必须在编译和链接同时指定。如果只是部分文件使用-dlto编译,则这些文件将被链接和优化在一起,其余的使用普通的单独编译。副作用是,这会将一些编译时间转移到链接阶段,并且对于真正的大型代码可能存在一些可扩展性问题。如果你想使用-gencode编译到多个目标架构(arch),使用-dc -gencode arch=compute_NN,code=lto_NN来指定要存储的中间IR(其中NN是SM架构版本)。然后使用-dlto选项链接特定的架构。对于LTO代码没有运行时JIT支持,因此需要静态链接到最终的sm_NN架构。作为一个review功能,CUDA 11.4的驱动程序API支持LTO下的JIT,请参阅CUDA驱动程序API文档获取更多信息。

6.6 独立编译的潜在问题

6.6.1 目标文件的兼容性

只有具有相同ABI版本、链接兼容的SM目标架构以及相同指针大小(32或64)的可重定位设备代码才能链接在一起。 不兼容的对象将产生链接错误。链接兼容的SM目标架构,具有兼容的SASS二进制文件,这些二进制文件无需转换就可以组合,例如:sm_52和sm_50。

一个对象可能已经被编译为支持不同的架构,但同时也包含PTX码。在这种情况下,设备连接器将把PTX 码即时编译(JIT)到所需目标架构的cubin二进制,然后进行链接。可重定位设备代码,需要CUDA 5.0或更高版本的Toolkit。

如果使用launch_bounds属性或--maxrregcount选项,将内核函数使用的寄存器限制在一定数量,那么内核调用的所有函数,都不能使用超过这个数量的寄存器;如果它们超过了限制,那么将给出一个链接错误。

6.6.2 JIT链接支持

CUDA 5.0不支持JIT(即时)链接,而CUDA 5.5支持。这意味着要使用JIT链接,必须使用CUDA 5.5或更高版本重新编译代码。

JIT链接意味着在启动时对代码进行重新链接。 设备链接器(nvlink)在cubin层进行链接。如果cubin在加载时与目标架构不匹配,驱动程序将重新调用设备链接器来,为目标架构生成cubin。方法是:首先将每个对象的PTX码即时编译(JIT)到适当的cubin,然后将新的cubin链接在一起。

6.6.3 隐式CUDA主机代码

像上面的b.cu这样的文件只包含CUDA设备代码,所以可能会认为b.o对象不需要传递给主机链接器。但实际上,只要设备符号可以从主机端访问,就会生成隐式的主机代码,无论是通过launch还是像cudaGetSymbolAddress()这样的API调用。

这个隐式的主机代码放在b.o中,需要传递给主机链接器。另外,为了支持即时编译(JIT)的链接,所有设备代码都必须传递给主机链接器,否则主机可执行文件将不包含即时编译(jit)链接所需的设备代码。

所以,一般的规则是设备链接器和主机链接器必须看到相同的主机目标文件(如果该主机目标文件中有任何设备端的引用;如果一个目标文件是纯主机文件,那么设备链接器就不需要看到它)。如果一个包含设备代码的目标文件没有传递给主机链接器,那么你将看到一个关于函数__cudaRegisterLinkedBinary_name调用一个未定义或未解析的符号__fatbinwrap_name的错误消息。

6.6.4 使用__CUDA_ARCH__宏

在单独的编译中,不能在头文件中使用__CUDA_ARCH__宏,这样不同的目标对象可能包含不同的行为。或者,必须保证所有对象都为同一个compute_arch编译。如果在头文件中定义了一个弱函数或模板函数,并且它的行为依赖于__CUDA_ARCH__,那么如果目标对象为不同的compute arch编译,该函数在对象中的实例可能会发生冲突。例如,a.h包含:

 template<typename T>
 __device__ T* getptr(void) 
 {
 #if __CUDA_ARCH__ == 500
     return NULL; /* no address */ 
 #else
     __shared__ T arr[256];
     return arr;
 #endif 
} 

如果a.cu和b.cu都include了a.h,并且实例化了相同类型的getptr,而b.cu需要一个非null地址,编译时使用:

nvcc --gpu-architecture=compute_50 --device-c a.cu
nvcc --gpu-architecture=compute_52 --device-c b.cu
nvcc --gpu-architecture=sm_52 a.o b.o

在链接时,只使用getptr的一个版本,因此行为将取决于选择的版本(为不同架构编译的不同版本)。为了避免这种情况,a.cu和b.cu必须编译为同一个计算架构,否则在共享头函数中不应该使用__CUDA_ARCH__。

6.6.5 库中的设备代码

如果一个使用非weak(弱符号)的外部链接设备函数,该函数符号同时定义在一个库和非库对象(或另一个库)中,设备链接器将产生多个定义(这有别于传统主机连接器,可以忽略库中的函数对象定义,如果它已经在前面的目标对象中找到该函数符号)的链接错误。

关于作者:

犇叔,浙江大学计算机科学与技术专业,研究生毕业,而立有余。先后在华为、阿里巴巴和字节跳动,从事技术研发工作,资深研发专家。主要研究领域包括虚拟化、分布式技术和存储系统(包括CPU与计算、GPU异构计算、分布式块存储、分布式数据库等领域)、高性能RDMA网络协议和数据中心应用、Linux内核等方向。

专业方向爱好:数学、科学技术应用

关注犇叔,期望为您带来更多科研领域的知识和产业应用。

内容坚持原创,坚持干货有料。坚持长期创作,关注犇叔不迷路

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/51277.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

挂耳式耳机品牌排行榜,五款目前排行靠前的耳机分享

耳机传声的方式无非就是空气传播以及骨骼传播&#xff0c;而骨传导耳机就属后者&#xff0c;通过骨骼震动来完成声波的传递&#xff0c;在传递的过程无需经过外耳道和鼓膜&#xff0c;在一定程度上缓解了对耳道造成的损伤&#xff0c;减少对于耳道的负担&#xff0c;看到这里如…

OSI参考模型个人总结

附&#xff1a;参考模型 OSI参考模型 基于国际标准化组织ISO的建议&#xff0c;作为各种层上使用的协议国际标准化的第一步发展起来的&#xff0c;被称作ISO开放系统互联参考模型&#xff08;open system interconnection refertence model&#xff09;&#xff0c;简称为OSI模…

[附源码]Python计算机毕业设计Django高校后勤保障系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【解决问题】413错误 413 Request Entity Too Large 接口返回413 报413nginx

文章目录问题排查解决方案1、修改nginx配置文件nginx.conf2、更新完成后需要重启nginx3、其他可能性博客背景&#xff1a;JAVA项目&#xff0c;前端想弄个便捷富文本&#xff0c;直接很多图片转base64编码直接存库了。字段为longtext类型。这种问题通常是在使用http请求对象太大…

python实现中缀表达式转后缀表达式

前缀、中缀、后缀表达式&#xff08;逆波兰表达式&#xff09; 前缀表达式称为波兰表达式&#xff0c;前缀表达式的运算符位于操作符之前 举例说明&#xff1a;&#xff08;34&#xff09;x 5 – 6 对应的前缀表达式就是- X 3 4 5 6 中缀表达式转为后缀表达式&#xff1a; …

概率论与数理统计——事件间的关系

包含 事件A发生必然导致事件B发生。 代数中经常用这种方法证明两个事件相等。 事件的并&#xff08;和&#xff09; A与B至少有一个发生 事件的交&#xff08;积&#xff09; A与B同时发生 无限可列个&#xff1a;能按某种规律能把他排成一个序列&#xff08;实变函数…

Typora导出Word

1.Typora导出Word配置 1.访问GitHub&#xff1a; https://github.com/jgm/pandoc/releases/tag/2.11.3.2https://github.com/jgm/pandoc/releases/tag/2.11.3.2 2.下载对应的文件 3.到本地安装Typora目录下解压 4.配置环境变量 我的电脑–属性–高级系统设置–高级–环境变量…

Python数据分析案例12——网飞影视剧数据分析及其可视化

背景介绍 Netflix是最受欢迎的媒体和视频流平台之一。他们的平台上有超过 8000 部电影或电视节目。截至 2021 年年中&#xff0c;他们在全球拥有超过 2 亿订阅者。 博主看美剧也较为多&#xff0c;像《怪奇物语》、《性爱自修室》等高分美剧都是网飞的。 对于网飞的影视剧&a…

【Docker学习系列】Docker学习1-docker安装

从本篇开始&#xff0c;凯哥将和大家一起学学docker。本篇是docker学习系列第一篇&#xff1a;安装docker。 docker安装前提条件&#xff1a;目前,centos发行版中的内核支持Docker.Docker运行在Centos7 64位上&#xff0c;要求系统为64位、linux系统内核版本为3.8以上的。凯哥所…

mybatis实战:三、mybatis多表查询的映射

依照上一期的继续 1.UserMapper.xml 除了直接对应基本数据类类型、表的实体类&#xff0c;还可能用到多表查询。 <select id"selectRolesByUserId" resultType"tk.mybatis.simple.model.SysRole">select r.id, r.role_name roleName, r.enabled, r…

一篇个人陈述应该包括这三个内容

大家好呀&#xff0c;申请季正如火如荼地进行着&#xff0c;不知道大家都忙碌得怎么样了呢&#xff1f;今天我们来聊聊个人陈述Personal statement,的撰写&#xff0c;包括一篇个人陈述应该包括哪些部分。 申请时&#xff0c;除了学习成绩等“硬背景”&#xff0c;个人陈述(PS…

五款朴实无华却又能极大提升办公效率的软件

最近后台收到好多小伙伴的私信&#xff0c;今天继续推荐五款小工具&#xff0c;都是免费使用的&#xff0c;大家可以去试试看。 1.光追动画制作——Luxion KeyShot Luxion KeyShot是一款互动性的光线追踪与全域光渲染3D渲染与动画制作软件&#xff0c;内置丰富多样的材质&…

Redis使用基础教程

本篇文章转载自&#xff1a;通俗易懂的Redis数据结构基础教程_Java程序员-张凯的博客-CSDN博客 Redis有5个基本数据结构&#xff0c;string、list、hash、set和zset。它们是日常开发中使用频率非常高应用最为广泛的数据结构&#xff0c;把这5个数据结构都吃透了&#xff0c;你…

Spring Security-全面详解(学习总结---从入门到深化)

目录 Spring Security介绍 Spring Security认证_项目搭建 Spring Security认证_内存认证 Spring Security认证_UserDetailsService Spring Security认证_数据库认证 Spring Security认证_PasswordEncoder Spring Security认证_自定义登录页面 Spring Security认证_会…

package-info.java

package-info.java 文件估计大家见过但是自己却很少去创建和使用它、因为对于一般应用来说可能真的太少见了。 它的作用主要是三个 描述包使用注解修饰包、达到修饰该包下的类声明包中使用的类和常量(这个比较少用) 描述包 package-info.java 文件 /*** 我是描述信息*/ pa…

如何用蓝牙实现无线定位(三)--本地定位显示

1. 被定位目标 本项目设计有两个定位装置&#xff0c;一个用于固定目标&#xff0c;一个用于可移动设备。在定位系统的帮助下&#xff0c;我们可以操作可移动设备向固定目标移动。假设这是一个救援场景的话&#xff0c;我们就可以把固定的目标看作等待救援的人或物&#xff0c;…

【使用 BERT 的问答系统】第 2 章 :用于自然语言处理的神经网络

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

大数据开发之词频统计传参打包成jar包发送到Hadoop运行并创建可执行文件方便运行

文章目录添加spark的jar包main传参调试打包成jar包发送到Hadoop运行使用脚本运行参考添加spark的jar包 点击Project Structure Global Libararies中 点击 选择java 然后选择spark文件里的jars下所有的jar包 然后点击ok即可。 main传参调试 首先给出词频统计代码 //包 imp…

OpenCV图像处理——光流估计

总目录 图像处理总目录←点击这里 二十二、光流估计 22.1、原理 光流 是空间运动物体在观测成像平面上的像素运动的“瞬时速度”&#xff0c;根据各个像素点的速度矢量特征&#xff0c;可以对图像进行动态分析&#xff0c;例如目标跟踪。 亮度恒定&#xff1a;同一点随着时…

HTML5期末考核大作业——学生网页设计作业源码HTML+CSS+JavaScript 中华美德6页面带音乐文化

&#x1f468;‍&#x1f393;静态网站的编写主要是用HTML DIVCSS JS等来完成页面的排版设计&#x1f469;‍&#x1f393;,常用的网页设计软件有Dreamweaver、EditPlus、HBuilderX、VScode 、Webstorm、Animate等等&#xff0c;用的最多的还是DW&#xff0c;当然不同软件写出的…