0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

介紹CUDA編程模型及CUDA線程體系

冬至子 ? 來源:指北筆記 ? 作者:張北北 ? 2023-05-19 11:32 ? 次閱讀

可伸縮的編程模型

CUDA 編程模型主要有三個(gè)關(guān)鍵抽象:層級(jí)的線程組,共享內(nèi)存和柵同步(barrier synchronization)。

這些抽象提供了細(xì)粒度的數(shù)據(jù)并行和線程并行,可以以嵌套在粗粒的數(shù)據(jù)并行和任務(wù)并行中。它們鼓勵(lì)將問題分解為子問題。每個(gè)子問題可以獨(dú)立的在block threads中并行解決。同時(shí)每個(gè)子問題分成更細(xì)的部分,可以由塊中的所有線程并行地合作解決。

這種分解通過允許線程在解決每個(gè)子問題時(shí)進(jìn)行協(xié)作來保留語言的表達(dá)性,同時(shí)支持自動(dòng)可伸縮性。實(shí)際上,每個(gè)線程塊都可以在GPU中任何可用的多處理器上調(diào)度,以任何順序、并發(fā)或順序,因此編譯的CUDA程序可以在任意數(shù)量的多處理器上執(zhí)行,如圖所示,而且只有運(yùn)行時(shí)系統(tǒng)需要知道物理多處理器的數(shù)量。

圖片

圖1 Automatic Scalability

Note: A GPU is built around an array of Streaming Multiprocessors (SMs) (see Hardware Implementation for more details). A multithreaded program is partitioned into blocks of threads that execute independently from each other, so that a GPU with more multiprocessors will automatically execute the program in less time than a GPU with fewer multiprocessors.

Kernels

CUDA c++ 通過允許程序員定義 c++ 函數(shù)( 稱為kernel )來擴(kuò)展 c++,當(dāng)調(diào)用這些函數(shù)時(shí),由 N 個(gè)不同的 CUDA 線程并行執(zhí)行 N 次,而不是像常規(guī) c++ 函數(shù)那樣只執(zhí)行一次。

使用_ global _ 聲明說明符定義內(nèi)核,并使用新的<<<…>>>執(zhí)行配置語法(參見c++語言擴(kuò)展)。每個(gè)執(zhí)行內(nèi)核的線程都有一個(gè)惟一的線程ID,可以在內(nèi)核中通過內(nèi)置變量訪問該ID。

下面的示例代碼使用內(nèi)置變量 threadIdx ,將兩個(gè)大小為N的向量A和B相加,并將結(jié)果存儲(chǔ)到向量C中:

// Kernel definition
__global__ void VecAdd(float* A, float* B, float* C)
{
    int i = threadIdx.x;
    C[i] = A[i] + B[i];
}

int main()
{
    ...
    // Kernel invocation with N threads
    VecAdd<<<1, N>>>(A, B, C);
    ...
}

執(zhí)行VecAdd()的N個(gè)線程中的每一個(gè)都執(zhí)行一次成對(duì)的相加。

Thread Hierarchy

為了方便起見,threadIdx 是一個(gè)三分量的向量,因此可以使用一維、二維或三維線程索引來標(biāo)識(shí)線程,從而形成一維、二維或三維線程塊,稱為線程塊。這提供了一種很自然的方法來調(diào)用跨域元素(如向量、矩陣或體)的計(jì)算。

線程的索引和線程 ID 以一種直接的方式相互關(guān)聯(lián):

  • 對(duì)于一維塊,它們是相同的
  • 對(duì)于大小為(Dx, Dy)的二維塊,索引為(x, y)的線程ID為(x + y Dx);
  • 對(duì)于大小為Dx, Dy, Dz的三維塊,索引為(x, y, z)的線程ID為(x + y Dx + z Dx Dy)。

例如,下面的代碼將兩個(gè)大小為NxN的矩陣A和B相加,并將結(jié)果存儲(chǔ)到矩陣C中:

// Kernel definition
__global__ void MatAdd(float A[N][N], float B[N][N],
                       float C[N][N])
{
    int i = threadIdx.x;
    int j = threadIdx.y;
    C[i][j] = A[i][j] + B[i][j];
}

int main()
{
    ...
    // Kernel invocation with one block of N * N * 1 threads
    int numBlocks = 1;
    dim3 threadsPerBlock(N, N);
    MatAdd<<<numBlocks, threadsPerBlock>>>(A, B, C);
    ...
}

每個(gè)塊的線程數(shù)量是有限制的,因?yàn)橐粋€(gè)塊的所有線程都駐留在同一個(gè)流多處理器核心上,必須共享該核心的有限內(nèi)存資源。 在當(dāng)前的gpu上,一個(gè)線程塊可能包含多達(dá)1024個(gè)線程 。

但是,一個(gè)內(nèi)核可以由多個(gè)形狀相同的線程塊執(zhí)行,這樣 線程總數(shù)就等于每個(gè)塊的線程數(shù)乘以塊的數(shù)量 。

塊被組織成一維、二維或三維的線程塊網(wǎng)格,如圖所示。 網(wǎng)格中線程塊的數(shù)量通常由正在處理的數(shù)據(jù)的大小決定 ,數(shù)據(jù)的大小通常超過系統(tǒng)中處理器的數(shù)量。

圖片

圖2 Grid of Thread Blocks

每個(gè)塊的線程數(shù)和每個(gè)網(wǎng)格的塊數(shù)在<<<…>>>語法的類型可以是int或dim3。二維塊或網(wǎng)格可以像上面的例子中那樣指定。

網(wǎng)格中的每個(gè)塊都可以通過一個(gè)一維、二維或三維的惟一索引來標(biāo)識(shí)。該索引可以通過內(nèi)核中內(nèi)置的blockIdx變量訪問。 線程塊的維度可以在內(nèi)核中通過內(nèi)置的blockDim變量訪問 。

擴(kuò)展前面的MatAdd()示例以處理多個(gè)塊,代碼如下所示。

// Kernel definition
__global__ void MatAdd(float A[N][N], float B[N][N],
float C[N][N])
{
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    int j = blockIdx.y * blockDim.y + threadIdx.y;
    if (i < N && j < N)
        C[i][j] = A[i][j] + B[i][j];
}

int main()
{
    ...
    // Kernel invocation
    dim3 threadsPerBlock(16, 16);
    dim3 numBlocks(N / threadsPerBlock.x, N / threadsPerBlock.y);
    MatAdd<<<numBlocks, threadsPerBlock>>>(A, B, C);
    ...
}

線程塊大小為16x16(256個(gè)線程),雖然在本例中是任意的,但卻是常見的選擇 。網(wǎng)格是用足夠的塊創(chuàng)建的,每個(gè)矩陣元素都有一個(gè)線程。

為了簡(jiǎn)單起見,本示例假設(shè)每個(gè)維度中每個(gè)網(wǎng)格的線程數(shù)可以被該維度中每個(gè)塊的線程數(shù)整除,盡管事實(shí)并非如此。

線程塊需要獨(dú)立執(zhí)行 :必須能夠以任何順序執(zhí)行它們,并行或串行。這種獨(dú)立性要求允許線程塊在任意數(shù)量的核上以任意順序調(diào)度,如圖1所示,這使程序員能夠編寫隨核數(shù)量擴(kuò)展的代碼。

塊中的線程可以通過共享內(nèi)存共享數(shù)據(jù),并通過同步它們的執(zhí)行來協(xié)調(diào)內(nèi)存訪問,從而進(jìn)行協(xié)作 。更精確地說,可以通過調(diào)用__syncthreads()內(nèi)部函數(shù)來指定內(nèi)核中的同步點(diǎn);__syncthreads()充當(dāng)一個(gè)屏障,在允許任何線程繼續(xù)之前,塊中的所有線程都必須等待。除了__syncthreads()之外,Cooperative Groups API還提供了一組豐富的線程同步原語。

為了高效合作,共享內(nèi)存應(yīng)該是每個(gè)處理器核心附近的低延遲內(nèi)存(很像L1緩存),并且__syncthreads()應(yīng)該是輕量級(jí)的。

Thread Block Clusters

隨著NVIDIA Compute Capability 9.0的引入 ,CUDA編程模型引入了一個(gè)可選的層次結(jié)構(gòu)級(jí)別,稱為 線程塊集群,它由線程塊組成 。 與線程塊中的線程被保證在流多處理器上同步調(diào)度類似,集群中的線程塊也被保證在GPU中的GPU處理集群(GPC)上同步調(diào)度

與線程塊類似,集群也被組織成一維、二維或三維,如圖3所示。一個(gè)集群中的線程塊數(shù)量可以由用戶定義, CUDA支持一個(gè)集群中最多8個(gè)線程塊作為可移植的集群大小 。線程塊集群大小是否超過8取決于體系結(jié)構(gòu),可以使用cudaoccuancymaxpotentialclustersize API進(jìn)行查詢。

圖片

圖3 Grid of Thread Block Clusters

Note: In a kernel launched using cluster support, the gridDim variable still denotes the size in terms of number of thread blocks , for compatibility purposes. The rank of a block in a cluster can be found using the Cluster Group API.

線程塊集群可以在內(nèi)核中使用編譯器時(shí)間內(nèi)核屬性__cluster_dims__(X,Y,Z)或使用CUDA內(nèi)核啟動(dòng)API cudaLaunchKernelEx來啟用。下面的示例展示了如何使用編譯器時(shí)間內(nèi)核屬性啟動(dòng)集群。使用內(nèi)核屬性的集群大小在編譯時(shí)固定,然后可以使用經(jīng)典的<<<,>>>啟動(dòng)內(nèi)核。如果內(nèi)核使用編譯時(shí)集群大小,則在啟動(dòng)內(nèi)核時(shí)無法修改集群大小。

  • 編譯期指定
// Kernel definition
// Compile time cluster size 2 in X-dimension and 1 in Y and Z dimension
__global__ void __cluster_dims__(2, 1, 1) cluster_kernel(float *input, float* output)
{

}

int main()
{
    float *input, *output;
    // Kernel invocation with compile time cluster size
    dim3 threadsPerBlock(16, 16);
    dim3 numBlocks(N / threadsPerBlock.x, N / threadsPerBlock.y);

    // The grid dimension is not affected by cluster launch, and is still enumerated
    // using number of blocks. 
    // The grid dimension must be a multiple of cluster size.
    cluster_kernel<<
  • 運(yùn)行期指定

線程塊集群大小也可以在運(yùn)行時(shí)設(shè)置,并且可以使用CUDA內(nèi)核啟動(dòng)API cudaLaunchKernelEx啟動(dòng)內(nèi)核。下面的代碼示例展示了如何使用可擴(kuò)展API啟動(dòng)集群內(nèi)核。

// Kernel definition
// No compile time attribute attached to the kernel
__global__ void cluster_kernel(float *input, float* output)
{
  
}

int main()
{
    float *input, *output;
    dim3 threadsPerBlock(16, 16);
    dim3 numBlocks(N / threadsPerBlock.x, N / threadsPerBlock.y);
    cluster_kernel<<

在具有9.0計(jì)算能力的GPU中,集群中的所有線程塊都被保證在單個(gè)GPU處理集群(GPC)上共同調(diào)度,并允許集群中的線程塊使用cluster Group API cluster.sync()執(zhí)行硬件支持的同步 。集群組還提供了成員函數(shù),分別使用num_threads()和num_blocks() API根據(jù)線程數(shù)或塊數(shù)查詢集群組的大小。可以分別通過dim_threads()和dim_blocks() API查詢集群組中線程或塊的級(jí)別。

屬于一個(gè)集群的線程塊可以訪問 分布式共享內(nèi)存 。集群中的線程塊能夠?qū)Ψ植际焦蚕韮?nèi)存中的任何地址進(jìn)行讀、寫和執(zhí)行原子操作。分布式共享內(nèi)存給出了一個(gè)在分布式共享內(nèi)存中執(zhí)行直方圖的示例。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 多處理器
    +關(guān)注

    關(guān)注

    0

    文章

    22

    瀏覽量

    8934
  • C++語言
    +關(guān)注

    關(guān)注

    0

    文章

    147

    瀏覽量

    6992
  • CUDA
    +關(guān)注

    關(guān)注

    0

    文章

    121

    瀏覽量

    13628
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    CUDA編程教程

    Nvidia CUDA 2.0編程教程
    發(fā)表于 03-05 07:30

    LInux安裝cuda sdk

    1.安裝toolkit(1)cd /home/CUDA_train/software/cuda4.1(2)./cudatoolkit_4.1.28_linux_64_rhel6.x.run
    發(fā)表于 07-24 06:11

    CUDA教程之Linux系統(tǒng)下CUDA安裝教程

    CUDA教程之1:Linux系統(tǒng)下CUDA安裝教程
    發(fā)表于 06-02 16:53

    什么是CUDA?

    的時(shí)間盡可能清晰的了解這個(gè)深度學(xué)習(xí)賴以實(shí)現(xiàn)的基礎(chǔ)概念。本文在以下資料的基礎(chǔ)上整理完成,感謝以下前輩提供的資料:CUDA——“從入門到放棄”我的CUDA學(xué)習(xí)之旅——啟程介紹一篇不錯(cuò)的CUDA
    發(fā)表于 07-26 06:28

    什么是CUDA?

    什么是CUDA?
    發(fā)表于 09-28 07:37

    cuda程序設(shè)計(jì)

      •GPGPU及CUDA介紹   •CUDA編程模型   •多
    發(fā)表于 11-12 16:12 ?0次下載

    CUDA 6中的統(tǒng)一內(nèi)存模型

    NVIDIA在CUDA 6中引入了統(tǒng)一內(nèi)存模型 ( Unified Memory ),這是CUDA歷史上最重要的編程模型改進(jìn)之一。在當(dāng)今典型
    的頭像 發(fā)表于 07-02 14:08 ?2817次閱讀

    并行計(jì)算平臺(tái)和NVIDIA編程模型CUDA的更簡(jiǎn)單介紹

      這篇文章是對(duì) CUDA 的一個(gè)超級(jí)簡(jiǎn)單的介紹,這是一個(gè)流行的并行計(jì)算平臺(tái)和 NVIDIA 的編程模型。我在 2013 年給 CUDA
    的頭像 發(fā)表于 04-11 09:46 ?1442次閱讀
    并行計(jì)算平臺(tái)和NVIDIA<b class='flag-5'>編程</b><b class='flag-5'>模型</b><b class='flag-5'>CUDA</b>的更簡(jiǎn)單<b class='flag-5'>介紹</b>

    CUDA并行計(jì)算平臺(tái)的C/C++接口的簡(jiǎn)單介紹

    CUDA 編程模型是一個(gè)異構(gòu)模型,其中使用了 CPU 和 GPU 。在 CUDA 中, host 指的是 CPU 及其存儲(chǔ)器, device
    的頭像 發(fā)表于 04-11 10:13 ?1663次閱讀

    CUDA簡(jiǎn)介: CUDA編程模型概述

    CUDA 編程模型中,線程是進(jìn)行計(jì)算或內(nèi)存操作的最低抽象級(jí)別。 從基于 NVIDIA Ampere GPU 架構(gòu)的設(shè)備開始,CUDA
    的頭像 發(fā)表于 04-20 17:16 ?3004次閱讀
    <b class='flag-5'>CUDA</b>簡(jiǎn)介: <b class='flag-5'>CUDA</b><b class='flag-5'>編程</b><b class='flag-5'>模型</b>概述

    CUDA編程模型如何在c++實(shí)現(xiàn)

      NVIDIA GPU 架構(gòu)圍繞可擴(kuò)展的多線程流式多處理器 (SM: Streaming Multiprocessors) 陣列構(gòu)建。當(dāng)主機(jī) CPU 上的 CUDA 程序調(diào)用內(nèi)核網(wǎng)格時(shí),網(wǎng)格的塊被
    的頭像 發(fā)表于 04-21 16:13 ?1498次閱讀
    <b class='flag-5'>CUDA</b><b class='flag-5'>編程</b><b class='flag-5'>模型</b>如何在c++實(shí)現(xiàn)

    如何使用CUDA使warp級(jí)編程安全有效

      NVIDIA GPUs 以 SIMT (單指令,多線程)方式執(zhí)行稱為 warps 的線程組。許多 CUDA 程序通過利用 warp 執(zhí)行來獲得高性能。在這個(gè)博客中,我們將展示如何使用 CU
    的頭像 發(fā)表于 04-28 16:09 ?2935次閱讀
    如何使用<b class='flag-5'>CUDA</b>使warp級(jí)<b class='flag-5'>編程</b>安全有效

    CUDA矩陣乘法優(yōu)化手段詳解

    單精度矩陣乘法(SGEMM)幾乎是每一位學(xué)習(xí) CUDA 的同學(xué)繞不開的案例,這個(gè)經(jīng)典的計(jì)算密集型案例可以很好地展示 GPU 編程中常用的優(yōu)化技巧。本文將詳細(xì)介紹 CUDA SGEMM
    的頭像 發(fā)表于 09-28 09:46 ?1925次閱讀

    使用CUDA進(jìn)行編程的要求有哪些

    CUDA是NVIDIA的一種用于GPU編程的技術(shù),CUDA核心是GPU上的一組小型計(jì)算單元,它們可以同時(shí)執(zhí)行大量的計(jì)算任務(wù)。
    的頭像 發(fā)表于 01-08 09:20 ?2539次閱讀

    CUDA核心是什么?CUDA核心的工作原理

    CUDA核心(Compute Unified Device Architecture Core)是NVIDIA圖形處理器(GPU)上的計(jì)算單元,用于執(zhí)行并行計(jì)算任務(wù)。每個(gè)CUDA核心可以執(zhí)行單個(gè)線程的指令,包括算術(shù)運(yùn)算、邏輯操作
    發(fā)表于 09-27 09:38 ?8721次閱讀
    <b class='flag-5'>CUDA</b>核心是什么?<b class='flag-5'>CUDA</b>核心的工作原理