DeepDriving | CUDA編程-03:線程層級

本文來源公眾號“DeepDriving”,僅用于學術分享,侵權刪,干貨滿滿。

原文鏈接:CUDA編程-03:線程層級

DeepDriving | CUDA編程-01: 搭建CUDA編程環境-CSDN博客

DeepDriving | CUDA編程-02: 初識CUDA編程-CSDN博客

1 GPU架構概述

英偉達GPU的架構是圍繞一個流式多處理器(Streaming Multiprocessors,SM)的可擴展陣列構建的,通過復制這種架構的構建來實現GPU的硬件并行。一個典型的SM包括以下幾個組件:

  • 核心

  • 共享內存/一級緩存

  • 寄存器文件

  • 加載/存儲單元

  • 特殊功能單元

  • 線程束調度器

一個GPU中通常有多個SM,每個SM上支持許多個線程并發地執行,CUDA采用單指令多線程(Single-Instruction Multiple-Thread,SIMT)來管理和執行GPU上的眾多線程,并提出一個兩級的線程層級結構的概念以便組織線程。由一個內核啟動所產生的所有線程統稱為一個線程網格,同一網格中的所有線程共享全局內存空間,一個網格由多個線程塊組成,一個線程塊包含一組線程,同一線程塊內的線程通過同步和共享內存的方式實現協作,不同塊內的線程不能協作。當host通過內核函數啟動一個內核網格時,這個內核網格的線程塊就被分配到可用的SM上來執行,一個線程塊內的多個線程在SM上并發執行,多個線程塊可以并發地在一個SM上執行,當線程塊終止時,新的線程塊又可以在騰出的SM上啟動執行。

2 線程

線程是并行程序的基礎,并行化的方式一般有兩種:任務并行和數據并行。任務并行是將一個計算任務分解為幾個子任務,通過不同的線程分別執行各個子任務,最后匯總結果;數據并行是將一個總任務在數據粒度上進行劃分,然后每個線程處理一份數據,每個線程上執行的計算任務是一樣的。

舉個搬磚的例子:

假設我們的任務是將100個磚從A點搬到B點,搬磚的任務分為3個子任務:把磚從A點裝車、從A點運送到B點、在B點把磚從車上卸下來。如果采用任務并行方式,那么可以請多個工人,然后把他們分為3個組,每個組負責一個子任務 ;如果是采用數據并行,那么可以請100個工人,每個人負責1個磚,每個人的任務都是把磚從A點搬到B點。

GPU采用數據并行的模式,它可以運行成千上萬的線程用于運行大量邏輯比較簡單的計算任務以實現高效的并行化計算。在上一篇文章中,我介紹了一個數組相加的例子,本文繼續以這個例子來介紹GPU中以多線程實現并行化的方式。

先來看一下CPU實現數組相加的方式:

void?VectorAddCPU(const?float?*const?a,?const?float?*const?b,?float?*const?c,const?int?n)?{for?(int?i?=?0;?i?<?n;?++i)?{c[i]?=?a[i]?+?b[i];}
}

CPU的代碼默認是單線程執行模式,要想實現含多個數據的數組相加任務,就必須以循環的方式實現(相當于一個人要把所有的磚搬完)。

再來看GPU的實現方式:

__global__?void?VectorAddGPU(const?float?*const?a,?const?float?*const?b,float?*const?c,?const?int?n)?{int?i?=?blockDim.x?*?blockIdx.x?+?threadIdx.x;?//?線程IDif?(i?<?n)?{c[i]?=?a[i]?+?b[i];?//每個線程需要做的事情}
}

可以看到,GPU代碼中并不需要循環,只是需要一個線程ID來進行索引,并告訴每個線程需要做的事情。線程依靠兩個內置變量來進行區分:

  • blockIdx: 線程塊在線程網格中的索引

  • threadIdx: 線程塊內的線程索引

這兩個CUDA內置變量是基于uint3定義的向量類型,是一個包含x,y,z三個無符號整數字段的結構。

在調用內核函數的時候,會在<<< >>>內設置兩個參數,分別代表線程網格的維度和線程塊的維度。CUDA可以組織三維的線程網格和線程塊,它們的維度由下列兩個內置變量來決定:

  • blockDim: 線程塊的維度,用每個線程塊中的線程數量來表示

  • gridDim: 線程網格的維度,用每個線程網格中的線程塊數量來表示

它們是基于uint3定義的dim3結構類型的變量,用于表示維度,每個維度可通過x,y,z字段獲得,未被初始化的字段會被初始化為1且忽略不計。通常情況下,一個線程網格會被組織成線程塊的二維數組形式,一個線程塊會被組織成線程的三維數組形式。

const?size_t?size?=?1024;
dim3?thread_per_block(256);
dim3?block_per_grid((size?+?thread_per_block.x?-?1)?/?thread_per_block.x);
printf("thread_per_block:?%d,?block_per_grid:?%d?\n",?thread_per_block.x,block_per_grid.x);
VectorAddGPU<<<block_per_grid,?thread_per_block>>>(da,?db,?dc,?size);

在上面的例子中,我只初始化了線程網格和線程塊的第一維x,相當于設定線程網格中的線程塊是以一維的形式排列,每個線程塊中的線程也是以一維的形式排列,在內核函數中每個線程的ID可以這樣得到:

const?unsigned?int?id?=?blockDim.x?*?blockIdx.x?+?threadIdx.x;?

我們可以在內核函數中打印gridDim,blockDim,blockIdx,threadIdx這些信息看一下:

......
gridDim:(4 1 1), blockDim:(256 1 1), blockIdx:(1 0 0), threadIdx:(29 0 0)
gridDim:(4 1 1), blockDim:(256 1 1), blockIdx:(1 0 0), threadIdx:(30 0 0)
gridDim:(4 1 1), blockDim:(256 1 1), blockIdx:(1 0 0), threadIdx:(31 0 0)
gridDim:(4 1 1), blockDim:(256 1 1), blockIdx:(0 0 0), threadIdx:(0 0 0)
gridDim:(4 1 1), blockDim:(256 1 1), blockIdx:(0 0 0), threadIdx:(1 0 0)
gridDim:(4 1 1), blockDim:(256 1 1), blockIdx:(0 0 0), threadIdx:(2 0 0)
......

thread_per_block設置為512再看一下:

......
gridDim:(2 1 1), blockDim:(512 1 1), blockIdx:(1 0 0), threadIdx:(93 0 0)
gridDim:(2 1 1), blockDim:(512 1 1), blockIdx:(1 0 0), threadIdx:(94 0 0)
gridDim:(2 1 1), blockDim:(512 1 1), blockIdx:(1 0 0), threadIdx:(95 0 0)
gridDim:(2 1 1), blockDim:(512 1 1), blockIdx:(0 0 0), threadIdx:(416 0 0)
gridDim:(2 1 1), blockDim:(512 1 1), blockIdx:(0 0 0), threadIdx:(417 0 0)
gridDim:(2 1 1), blockDim:(512 1 1), blockIdx:(0 0 0), threadIdx:(418 0 0)
......

可以看到,啟動內核函數的時候在<<< >>>內設置不同的執行參數,內核中線程的布局是不一樣的。

3 線程束

CUDA采用SIMT架構來管理和執行線程,將線程塊中的線程每32個(記住這個神奇的數字)為一組進行劃分,每一組被稱為一個線程束(warp)。線程束的大小warpSizeCUDA中的一個內部屬性,可以通過以下方式獲得:

cudaDeviceProp?prop;
cudaGetDeviceProperties(&prop,?0);
printf("warpSize:?%d\n",?prop.warpSize);

線程束是GPU的基本執行單元,當線程網格啟動后,網格中的線程塊被分配到SM中執行,一旦線程塊被調度到一個SM上,線程塊中的線程就會被進一步劃分為線程束,每個線程束中的所有線程執行相同的命令,每個線程擁有自己的指令地址計數器和寄存器狀態,利用自己的私有數據執行當前的指令。線程塊的邏輯視圖和硬件視圖之間的關系如下:

從邏輯角度看,線程塊是線程的集合,它們可以被組織成一維、二維或者三維的布局形式;從硬件角度來看,線程塊是一維線程束的集合,線程塊中的線程被組織成一維布局,每32個連續的線程組成了一個線程束。

由于在硬件上線程塊中的線程會被劃分為線程束,而線程束不會在不同線程塊之間分離,也就是說同一個線程束中的線程不會同屬于兩個線程塊。如果線程塊的大小不是線程束大小的偶數倍,那么最后一個線程束里就會有些線程沒有用,但是它們依然會消耗SM的資源,所以在設置線程塊大小的時候,最好設置為32的倍數。下圖展示了一個線程塊中包含80個線程時的情況,硬件為這些線程分配了3個線程束,最后一個線程束中有些線程是沒有用的。

4 線程塊

對于一份給定的數據,確定網格和塊的維度的一般步驟為:

  1. 確定塊的維度大小;

  2. 在已知數據大小和塊大小的基礎上計算網格的維度。

如何確定一個塊的維度大小,通常需要考慮內核的性能特性和GPU的資源限制,比如寄存器和共享內存的大小,使用合適的網格和塊大小來組織線程可以對內核性能產生較大的影響。在程序中,應該盡量避免使用小的線程塊,因為這樣無法充分利用硬件資源。為了防止不合理的內存合并,我們需要盡量做到數據內存的分布與線程的分布達到一一映射的關系。CUDA的設計思想是將數據分解到并行的線程和線程塊中,使得程序結構與內存數據的分布能夠建立一一映射的關系。假如我們需要計算二維數組的相加,那么可以將線程網格和線程塊劃分為二維:

這種情況下計算線程的ID會稍微復雜一點,首先計算當前的行索引,然后乘以每一行的線程總數,最后加上X軸方向上的偏移,這樣就能計算出線程相對于整個線程網格的絕對線程索引:

const?unsigned?int?idx?=?blockDim.x?*?blockIdx.x?+?threadIdx.x;
const?unsigned?int?idy?=?blockDim.y?*?blockIdx.y?+?threadIdx.y;
const?unsigned?int?thread_id?=?(gridDim.x?*?blockDim.x)?*?idy?+?idx;

當然,二維線程塊的布局方式也有多種,比如下面這兩種,它們的線程總數是一樣的,但左圖的布局要比右圖的更高效。因為無論是在CPU還是在GPU中都是以行的方式進行內存訪問,以右圖的布局方式,同一行的數據需要被2個線程塊訪問2次,而左圖的布局同一行的數據只需要訪問1次即可。

5 參考資料

  • Professional CUDA C Programming

  • CUDA C Programming Guide

  • CUDA Programming:A Developer's Guide to Parallel Computing with GPUs

THE END !

文章結束,感謝閱讀。您的點贊,收藏,評論是我繼續更新的動力。大家有推薦的公眾號可以評論區留言,共同學習,一起進步。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/15089.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/15089.shtml
英文地址,請注明出處:http://en.pswp.cn/web/15089.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux之共享內存mmap用法實例(六十三)

簡介&#xff1a; CSDN博客專家&#xff0c;專注Android/Linux系統&#xff0c;分享多mic語音方案、音視頻、編解碼等技術&#xff0c;與大家一起成長&#xff01; 優質專欄&#xff1a;Audio工程師進階系列【原創干貨持續更新中……】&#x1f680; 優質專欄&#xff1a;多媒…

外賣霸王餐返利外賣會員卡小程序開發

外賣霸王餐返利外賣會員卡小程序開發 "社交電商賦能下的外賣返利小程序"是專為商家與用戶雙贏而設計的創新平臺。 以下是其開發方案的詳細步驟&#xff1a; 一、需求梳理&#xff1a;首先&#xff0c;我們需要明確小程序的核心功能和特色。包括設定活動類型、返利…

Python學習(3) 函數

定義 定義一個函數的格式&#xff1a; def 函數名(參數):執行代碼如果沒有參數&#xff0c;則稱為無參函數。 定義時小括號中寫的是形參&#xff08;形式參數&#xff09;&#xff0c;調用時寫的是實參&#xff08;實際參數&#xff09;。 調用 調用格式&#xff1a; def…

【Docker】Linux 系統(CentOS 7)安裝 Docker

文章目錄 對 VMware 軟件的建議官方說明文檔Docker安裝卸載舊版本docker設置倉庫開始安裝 docker 引擎最新版 Docker 安裝指定版本 Docker 安裝&#xff08;特殊需求使用&#xff09; 啟動 Docker查看 Docker 版本查看 Docker 鏡像設置 Docker 開機自啟動 驗證開機啟動是否生效…

自定義原生小程序頂部及獲取膠囊信息

需求&#xff1a;我需要將某個文字或者按鈕放置在小程序頂部位置 思路&#xff1a;根據獲取到的頂部信息來定義我需要放的這個元素樣式 * 這里我是定義某個指定頁面 json&#xff1a;給指定頁面的json中添加自定義設置 "navigationStyle": "custom" JS&am…

新時代AI浪潮下,程序員和產品經理如何入局AIGC領域?

當下&#xff0c;AI浪潮席卷全球&#xff0c;AIGC大模型技術已經成為當今技術領域的一個重要趨勢&#xff0c;對于產品經理來說&#xff0c;掌握這項技術不僅能夠增強他們的職業技能&#xff0c;還能在競爭激烈的職場中脫穎而出。 為什么呢&#xff1f; 把握AI時代的機遇 AI技…

StringMVC

目錄 一&#xff0c;MVC定義 二&#xff0c;SpringMVC的基本使用 2.1建立連接 - RequestMapping("/...") ?編輯 2.2請求 1.傳遞單個參數 2.傳遞多個參數 3.傳遞對象 4.參數重命名 5.傳遞數組 6. 傳遞集合 7.傳遞JSON數據 8. 獲取url中數據 9. 傳遞文…

怎么通過OpenAI API調用其多模態大模型(GPT-4o)

現在只要有額度&#xff0c;大家都可以調用OpenAI的多模態大模型了&#xff0c;例如GPT-4o和GPT-4 Turbo&#xff0c;我一年多前總結過一些OpenAI API的用法&#xff0c;發現現在稍微更新了一下。主要參考了這里&#xff1a;https://platform.openai.com/docs/guides/vision 其…

python數據類型之元組、集合和字典

目錄 0.三者主要作用 1.元組 元組特點 創建元組 元組解包 可變和不可變元素元組 2.集合 集合特點 創建集合 集合元素要求 集合方法 訪問與修改 子集和超集 相等性判斷 集合運算 不可變集合 3.字典 字典特點 字典創建和常見操作 字典內置方法 pprin模塊 0.…

k8s——Pod詳解

一、Pod基礎概念 1.1 Pod定義 Pod是kubernetes中最小的資源管理組件&#xff0c;Pod也是最小化運行容器化應用的資源對象。一個Pod代表著集群中運行的一個進程。kubernetes中其他大多數組件都是圍繞著Pod來進行支撐和擴展Pod功能的&#xff0c;例如&#xff0c;用于管理Pod運行…

繆爾賽思又來到了你的面前(哈希)

定義一棵根節點為 1 1 1&#xff0c; n ( 2 ≤ n ≤ 1 0 3 ) n(2≤n≤10^3) n(2≤n≤103) 個節點的樹的哈希值為&#xff1a; H ∑ i 1 n X i Y f a ( i ) m o d 998244353 H∑^n_{i1}X^iY^{fa(i)}\ mod\ 998244353 Hi1∑n?XiYfa(i) mod 998244353 f a ( i ) fa(i) fa(i)…

斷網之后的頁面,Autox.js是點擊還是上下滑動比較好?

在處理斷網之后的頁面&#xff0c;選擇點擊還是上下滑動作為刷新操作&#xff0c;取決于應用的設計和用戶界面。通常&#xff0c;這兩種操作都可以作為刷新頁面的方式&#xff0c;但它們各自有不同的適用場景&#xff1a; 點擊刷新 - 適用場景&#xff1a;如果應用提供了一個明…

Java進階學習筆記7——權限修飾符

什么是權限修飾符&#xff1f; 就是用來限制類中的成員&#xff08;成員變量、成員方法、構造器、代碼塊....&#xff09;能夠被訪問的范圍。 protected使用的比較少&#xff0c;但是程序員還是要閱讀代碼&#xff0c;看官方文檔是怎么寫的&#xff0c;都會接觸到protected修飾…

C#串口通信-串口相關參數介紹

串口通訊(Serial Communication)&#xff0c;是指外設和計算機間&#xff0c;通過數據信號線、地線等&#xff0c;按位進行傳輸數據的一種雙向通訊方式。 串口是一種接口標準&#xff0c;它規定了接口的電氣標準&#xff0c;沒有規定接口插件電纜以及使用的通信協議&#xff0c…

ssh 配置 authorized_keys 后無法免密登錄

查看日志&#xff1a; tail -f /var/log/auth.log May 25 15:55:13 121 sudo: pam_unix(sudo:session): session opened for user root by root(uid0) May 25 15:55:13 121 sshd[550561]: Received signal 15; terminating. May 25 15:55:13 121 sshd[922866]: Server liste…

性能測試場景的設計方法

引用&#xff1a;根據2008年Aberdeen Group的研究報告&#xff0c;對于Web網站&#xff0c;1秒的頁面加載延遲相當于少了11%的PV&#xff08;page view&#xff09;&#xff0c;相當于降低了16%的顧客滿意度。如果從金錢的角度計算&#xff0c;就意味著&#xff1a;如果一個網站…

「探討」:什么是網絡審計?好用的網絡審計系統推薦【圖文詳解】

網絡是企業運營、政府管理、個人生活不可或缺的基礎設施。 然而網絡安全問題卻日益凸顯&#xff0c;數據泄露、網絡攻擊、欺詐行為等風險日益嚴重。 一、網絡審計的定義 網絡審計&#xff0c;又稱信息技術審計或電子審計&#xff0c;是指審計人員運用專業技能和工具&#xff…

fdk-aac將aac格式轉為pcm數據

int sampleRate 44100; // 采樣率int sampleSizeInBits 16; // 采樣位數&#xff0c;通常是16int channels 2; // 通道數&#xff0c;單聲道為1&#xff0c;立體聲為2FILE *m_fd NULL;FILE *m_fd2 NULL;HANDLE_AACDECODER decoder aacDecoder_Open(TT_MP4_ADTS, 1);if (!…

實戰之快速完成 ChatGLM3-6B 在 GPU-8G的 INT4 量化和本地部署

ChatGLM3 (ChatGLM3-6B) 項目地址 https://github.com/THUDM/ChatGLM3大模型是很吃CPU和顯卡的&#xff0c;所以&#xff0c;要不有一個好的CPU&#xff0c;要不有一塊好的顯卡&#xff0c;顯卡盡量13G&#xff0c;內存基本要32GB。 清華大模型分為三種(ChatGLM3-6B-Base&…

“大數據建模、分析、挖掘技術應用研修班”的通知!

隨著2015年9月國務院發布了《關于印發促進大數據發展行動綱要的通知》&#xff0c;各類型數據呈現出了指數級增長&#xff0c;數據成了每個組織的命脈。今天所產生的數據比過去幾年所產生的數據大好幾個數量級&#xff0c;企業有了能夠輕松訪問和分析數據以提高性能的新機會&am…