CUDA 技術手冊翻譯 Day 3

「CUDA 技術手冊翻譯」這一系列文章是個人的嘗試翻譯,原文是 CUDA C Programming Giude 文檔 ,而標題的 Day N 分篇則源自於文章底部的原文網址
若有任何錯誤還煩請各位指出,希望能在翻譯與閱讀的同時增進對 Nvidia CUDA 的認識。個人也會在進一步的學習後,於文章末分享自己的心得。

3.編程接口

CUDA C 利用 C 語言提供了使用者一個簡單的的路徑,用來輕鬆撰寫程序以提供設備執行。
它由一組最小的 C 語言擴展集和一個運行庫組成。
這個核心的延伸已經在 DAY2 中介紹,他們允許程序員用 C function 去定義一個核心,並在每一次方法被呼叫時,使用一些新的語法去定義格子與區塊大小。任何一個包含這些擴展集的原代碼已經被編譯為nvcc檔案。
這些執行期已經在Compilation Workflow介紹。它提供了可以在host上執行,用以分配和釋放設備內存、在主機內存和設備內存之間傳輸數據、管理具有多個設備的系統等的C函數。一個完整執行期的描述可以在CUDA reference manual 中查到。
這個執行期備建立在一個低階C API的頂部,CUDA 驅動程式 API,也可由應用程序訪問。這個驅動程序API通過利用底層的概念來提供額外的控制級別,CUDA contexts 用以設備主機進程的模擬;以及CUDA modules 用以設備動態加載庫的類比。大多數應用程序不使用驅動程序API,因為它們不需要此額外級別的控制,並且在使用運行時時, contexts 和 modules 管理是隱含的,從而產生更簡潔的代碼。驅動程序API在Driver API中描述,並在參考手冊中有詳細資料。

3.1 NVCC 的編撰

內核可以由 CUDA 指令集系統撰寫而成,其被命名為 PTX ,這在PTX參考手冊中有描述,這多麼通常而更有效的去使用一個例如C的高階程式語言。在兩個案例中,內核必須被利用 nvcc 編譯成一個二進制代碼使其執行於裝置上。

nvcc 是一個編譯器驅動,其簡化了編譯C或PTX代碼的過程,他提供了簡易且友善的命令行選單,並匯集實現不同的編譯階段的工具,藉由調用其並執行,本節概述了nvcc工作流程和命令選項。完整的描述可以在nvcc用戶手冊中找到。

3.1.1 Compilation Workflow

3.1.1.1 離線編譯

運用 nvcc 編譯原代碼可以混合包含主機的代碼(即,在主機上執行的代碼)以及設備的代碼(即,在設備上執行的代碼),nvcc的基本工作流程是將設備代碼與主機代碼分離,然後:

  • 將設備代碼編譯成彙編形式(PTX)和/或 二進制形式(Cubin),

  • 藉由必要的 CUDA C 運行時的 function 呼叫,從 PTX 或 cubin 中讀取與安裝每個編譯核心,更換<<<…>>>語法,以修改 host 代碼 (<<<…>>>在Kernels中介紹,並在 Execution Configuration 中更詳細地描述)

修改後的主機代碼可以作為C代碼輸出,可以使用其他工具編譯,也可以直接通過讓nvcc在上一個編譯階段調用主編譯器來直接編譯為目標代碼。
此應用程序可以:

  • 鏈接到已編譯的主機代碼(這是最常見的情況),

  • 或者忽略修改的主機代碼(如果有)並使用CUDA驅動程序API(請參閱驅動程序API)加載和執行PTX代碼或Cubin對象。

3.1.1.2 即時編譯

應用程序在運行時加載的任何PTX代碼都由設備驅動程序進一步編譯為二進制代碼,這就是所謂的即時編譯。即時編譯會增加應用程序加載時間,但允許應用程序受益於每個新設備驅動程序隨附的任何新編譯器改進。它也是應用程序在編譯應用程序時不存在的設備上運行的唯一方法,如應用程序兼容性中所述。
當設備驅動程序即時編譯某些應用程序的某些PTX代碼時,它會自動緩存生成的二進制代碼的副本,以避免在後續調用應用程序時重複編譯。緩存(稱為計算緩存)在設備驅動程序升級時會自動失效,以便應用程序可以從設備驅動程序中內置的新即時編譯器中的改進中受益。
環境變量可用於控制即時編譯,如 UDA Environment Variables 所述。

3.1.2 二進制兼容性

二進制代碼是特定於體系結構的。使用指定目標體系結構的編譯器選項-code生成cubin對象:例如,使用-code = sm_35進行編譯會為計算能力3.5的設備生成二進制代碼。二進制兼容性可以保證從一個小版本到下一個版本,但不能從一個小版本到前一個版本或跨主要版本。換句話說,為計算能力X.y生成的Cubin對像只能在計算能力為X.z的設備上執行,其中z≥y。

3.1.3 PTX兼容性

某些PTX指令僅在具有較高計算能力的設備上受支持。例如,Warp Shuffle函數僅在計算能力3.0及以上的設備上受支持。 -arch編譯器選項指定將C編譯為PTX代碼時的計算能力。因此,例如,包含warp shuffle的代碼必須使用-arch = compute_30(或更高版本)進行編譯。

針對某些特定計算能力生成的PTX代碼始終可以編譯為具有更高或相同計算能力的二進制代碼。請注意,從早期的PTX版本編譯的二進製文件可能無法使用某些硬件功能。例如,從為計算能力6.0(Pascal)生成的PTX編譯的計算能力7.0(Volta)的二進制目標設備將不使用Tensor Core指令,因為這些指令在Pascal上不可用。因此,如果二進製文件是使用最新版本的PTX生成的,則最終的二進製文件可能會執行得更差。

3.1.4。應用兼容性

要在具有特定計算能力的設備上執行代碼,應用程序必須加載與此計算能力兼容的二進製或PTX代碼,如二進制兼容性和PTX兼容性中所述。特別是,為了能夠在具有更高計算能力的未來體系結構上執行代碼(尚未生成二進制代碼),應用程序必須加載PTX代碼,該代碼將被即時編譯用於這些設備(Just-in-Time Compilation)。

在CUDA C應用程序中嵌入哪些PTX和二進制代碼由-arch和-code編譯器選項或-gencode編譯器選項控制,詳見nvcc用戶手冊。例如,

嵌入與計算能力3.5和5.0(第一和第二代碼選項)兼容的二進制代碼以及與計算能力6.0(第三代碼選項)兼容的PTX和二進制代碼。
生成主機代碼以在運行時自動選擇要加載和執行的最合適的代碼,在以上示例中,代碼將為:

  • 具有計算能力3.5和3.7的設備的3.5二進制代碼,

  • 具有計算能力5.0和5.2的設備的5.0二進制代碼,

  • 具有計算能力6.0和6.1的設備的6.0二進制代碼,

  • PTX代碼在運行時編譯為計算能力為7.0或更高的設備的二進制代碼。

例如,x.cu可以具有使用warp shuffle操作的優化代碼路徑,這些操作僅在計算能力3.0和更高版本的設備中受支持。 __CUDA_ARCH__宏可用於根據計算能力區分各種代碼路徑。它僅為設備代碼定義。例如,當使用-arch = compute_35進行編譯時,__​​CUDA_ARCH__等於350。

使用驅動程序API的應用程序必須編譯代碼以分離文件,並在運行時顯式加載和執行最合適的文件。

Volta體系結構引入了獨立線程調度,它改變了線程在GPU上的調度方式。對於依賴於以前的架構中SIMT調度的特定行為的代碼,獨立線程調度可能會改變參與的線程集合,從而導致不正確的結果。為了在執行獨立線程調度中詳述的糾正措施的同時幫助遷移,Volta開發人員可以使用編譯器選項組合-arch = compute_60 -code = sm_70來選擇加入Pascal的線程調度。

nvcc用戶手冊列出了-arch,-code和-gencode編譯器選項的各種簡寫。例如,-arch = sm_35是-arch = compute_35-code = compute_35,sm_35(這與-gencodearch = compute_35,code = 'compute_35,sm_35 '相同)的簡寫。

3.1.5 C / C++ 兼容性

編譯器的前端根據C ++語法規則處理CUDA源文件。所有 C++ 代碼皆被 host 支持。但是,如 C / C ++ 語言支持中所述,只有一個 C++ 子集完整被 device 支持。

3.1.6 64位兼容性

64位版本的nvcc以64位模式編譯 device 代碼(即指針是64位)。只有在64位模式下編譯的 host 代碼才支持以64位模式編譯的 device 代碼。

類似地,32位版本的nvcc以32位模式編譯 device 代碼,而以32位模式編譯的 device 代碼僅支持以32位模式編譯的 host 代碼。

32位版本的nvcc也可以使用 -m64 編譯器選項在64位模式下編譯 device 代碼。

64位版本的nvcc也可以使用 -m32 編譯器選項以32位模式編譯 device 代碼。

本文备注/经验分享:

just-in-time compilation缩写为JIT,中文也叫“及时翻译”或者“及时编译”。具体的说法是在即将要被执行前的瞬间被编译。(反义词叫AOT。Ahead Of Time)。从你的角度看,普通编译发生在当下编译者的机器上。JIT编译发生了以后发布给用户,在用户的机器上进行有。或者有一个未来的时间,例如新一代的显卡发布了,因为编译者现在的机器上,在开发的时候,还没有新卡,编译器也不知道未来如何给新卡编译。采用JIT就不怕了,未来的编译器集成在未来的显卡驱动中,到时候在JIT编译即可。这样就解决了时间上的矛盾。而且如果将来有一天,编译器技术发生了进步,JIT编译可以在开发完成后很多年,甚至开发者都已经挂了的情况下(例如团队解散),依然能享受未来的更先进编译技术。因为它不是普通编译那样一次完成的,而是在将来在用户的机器上再即时的完成,所以这就是为何叫“即时编译”(Just in time)

Binary code is architecture-specific,这说的是SASS,SASS(Shader ASSembly的缩写)是每种架构的卡是固定的。为一种卡编译出来的SASS(例如cubin)只能在这种架构的卡上用。不像PTX那样通用。(二进制兼容性就像你的CPU。你的一个exe可能是10年前的。但CPU是今年出的,但这个CPU却依然可以运行当年的exe),GPU只能在PTX级别上保持兼容性,普通的SASS代码不能保持,除非是同一代架构的卡。等于你买了v5的CPU,只能运行v5上编译的exe,不能运行之前的,也不能运行之后的。

PTX Compatibility即PTX兼容性。PTX有几个不同的版本。越往后的驱动或者卡,
支持的PTX版本越高。低版本的PTX写的东西,能在高版本下运行。这样就保持了对老代码的兼容性。而不像是二进制的SASS,一代就只能在一代上运行。不能在老一代上,也不能上新一代上运行。这是SASS或者说二进制发布的最大坏处。PTX可以持续在未来的新卡上运行(JIT么),你可以直接将PTX理解成一种虚拟机和之上的虚拟指令。

Full C++ is supported for the host code. However, only a subset of C++ is fully supported for the device code 在HOST代码中,具有完整的C++支持(也就是普通的CPU上); 在DEVICE代码中,只有部分C++(的特性)被完全支持(也就是在GPU上)。

Device code compiled in 64-bit mode is only supported with host code compiled in 64-bit mode.

GPU端如果是64-bit,CPU端也必须是。这个看起来很正常,为何要特别说明?? 因为CUDA 3.2和之前的版本,支持混合模式。允许一部分是64-bit,一部分是32-bit的。 后来发现这对很多人造成了困扰。于是直接要求都必须是统一的了。 这也是CUDA易用性的体验。 例如OpenCL就不要求这点。 所以CUDA可以很容易的将结构体(里面含有各种和字长相关的东西(32-bit或者64-bit)之类的在GPU和CPU上传递。 而OpenCL很难做到这种。

原文網址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值