吴忠躺衫网络科技有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

通過使用CUDA GPU共享內存

星星科技指導員 ? 來源:NVIDIA ? 作者:Mark Harris ? 2022-04-11 10:03 ? 次閱讀

共享內存是編寫優化良好的 CUDA 代碼的一個強大功能。共享內存的訪問比全局內存訪問快得多,因為它位于芯片上。因為共享內存由線程塊中的線程共享,它為線程提供了一種協作機制。利用這種線程協作使用共享內存的一種方法是啟用全局內存合并,如本文中的數組反轉所示。通過使用 CUDA GPU 共享內存,我們可以在 GPU 上執行所有讀操作。在下一篇文章中,我將通過使用共享內存來優化矩陣轉置來繼續我們的討論。


在 上一篇文章 中,我研究了如何將一組線程訪問的全局內存合并到一個事務中,以及對齊和跨步如何影響 CUDA 各代硬件的合并。對于最新版本的 CUDA 硬件,未對齊的數據訪問不是一個大問題。然而,不管 CUDA 硬件是如何產生的,在全局內存中大步前進都是有問題的,而且在許多情況下似乎是不可避免的,例如在訪問多維數組中沿第二個和更高維的元素時。但是,在這種情況下,如果我們使用共享內存,就可以合并內存訪問。在我在下一篇文章中向您展示如何避免跨越全局內存之前,首先我需要詳細描述一下共享內存。

共享內存

因為它是片上的,共享內存比本地和全局內存快得多。實際上,共享內存延遲大約比未緩存的全局內存延遲低 100 倍(前提是線程之間沒有內存沖突,我們將在本文后面討論這個問題)。共享內存是按線程塊分配的,因此塊中的所有線程都可以訪問同一共享內存。線程可以訪問由同一線程塊中的其他線程從全局內存加載的共享內存中的數據。此功能(與線程同步結合)有許多用途,例如用戶管理的數據緩存、高性能的協作并行算法(例如并行縮減),以及在不可能實現全局內存合并的情況下促進全局內存合并。

線程同步

在線程之間共享數據時,我們需要小心避免爭用情況,因為雖然塊中的線程并行運行 邏輯上 ,但并非所有線程都可以同時執行 身體上 。假設兩個線程 A 和 B 分別從全局內存加載一個數據元素并將其存儲到共享內存中。然后,線程 A 想從共享內存中讀取 B 的元素,反之亦然。我們假設 A 和 B 是兩個不同翹曲中的線。如果 B 在 A 嘗試讀取它之前還沒有完成它的元素的編寫,我們就有一個競爭條件,它可能導致未定義的行為和錯誤的結果。

為了保證并行線程協作時的正確結果,必須同步線程。 CUDA 提供了一個簡單的屏障同步原語 __syncthreads() 。一個線程的執行只能在其塊中的所有線程都執行了 __syncthreads() 之后通過 __syncthreads() 繼續執行。因此,我們可以通過在存儲到共享內存之后和從共享內存加載任何線程之前調用 __syncthreads() 來避免上面描述的競爭條件。需要注意的是,在發散代碼中調用 __syncthreads() 是未定義的,并且可能導致死鎖,線程塊中的所有線程都必須在同一點調用 __syncthreads()

共享內存示例

使用 Clara 變量 D __shared__ 指定說明符在 CUDA C / C ++設備代碼中聲明共享內存。在內核中聲明共享內存有多種方法,這取決于內存量是在編譯時還是在運行時已知的。下面的完整代碼( 在 GitHub 上提供 )演示了使用共享內存的各種方法。

#include __global__ void staticReverse(int *d, int n)
{ __shared__ int s[64]; int t = threadIdx.x; int tr = n-t-1; s[t] = d[t]; __syncthreads(); d[t] = s[tr];
} __global__ void dynamicReverse(int *d, int n)
{ extern __shared__ int s[]; int t = threadIdx.x; int tr = n-t-1; s[t] = d[t]; __syncthreads(); d[t] = s[tr];
} int main(void)
{ const int n = 64; int a[n], r[n], d[n]; for (int i = 0; i < n; i++) { a[i] = i; r[i] = n-i-1; d[i] = 0; } int *d_d; cudaMalloc(&d_d, n * sizeof(int)); // run version with static shared memory cudaMemcpy(d_d, a, n*sizeof(int), cudaMemcpyHostToDevice); staticReverse<<<1,n>>>(d_d, n); cudaMemcpy(d, d_d, n*sizeof(int), cudaMemcpyDeviceToHost); for (int i = 0; i < n; i++) if (d[i] != r[i]) printf("Error: d[%d]!=r[%d] (%d, %d)n", i, i, d[i], r[i]); // run dynamic shared memory version cudaMemcpy(d_d, a, n*sizeof(int), cudaMemcpyHostToDevice); dynamicReverse<<<1,n,n*sizeof(int)>>>(d_d, n); cudaMemcpy(d, d_d, n * sizeof(int), cudaMemcpyDeviceToHost); for (int i = 0; i < n; i++) if (d[i] != r[i]) printf("Error: d[%d]!=r[%d] (%d, %d)n", i, i, d[i], r[i]);?

}此代碼使用共享內存反轉 64 元素數組中的數據。這兩個內核非常相似,只是在共享內存數組的聲明方式和內核的調用方式上有所不同。

靜態共享內存

如果共享內存數組大小在編譯時已知,就像在 staticReverse 內核中一樣,那么我們可以顯式地聲明一個該大小的數組,就像我們對數組 s 所做的那樣。

__global__ void staticReverse(int *d, int n)
{ __shared__ int s[64]; int t = threadIdx.x; int tr = n-t-1; s[t] = d[t]; __syncthreads(); d[t] = s[tr];

}在這個內核中, ttr 是分別表示原始順序和反向順序的兩個索引。線程使用語句 s[t] = d[t] 將數據從全局內存復制到共享內存,然后在兩行之后使用語句 d[t] = s[tr] 完成反轉。但是在執行最后一行之前,每個線程訪問共享內存中由另一個線程寫入的數據,請記住,我們需要通過調用 __syncthreads() 來確保所有線程都已完成對共享內存的加載。

在這個例子中使用共享內存的原因是為了在舊的 CUDA 設備(計算能力 1 . 1 或更早版本)上促進全局內存合并。由于全局內存總是通過線性對齊索引 t 訪問,所以讀寫都可以實現最佳的全局內存合并。反向索引 tr 僅用于訪問共享內存,它不具有全局內存的順序訪問限制以獲得最佳性能。共享內存的唯一性能問題是銀行沖突,我們將在后面討論。(請注意,在計算能力為 1 . 2 或更高版本的設備上,內存系統甚至可以將反向索引存儲完全合并到全局內存中。但是這種技術對于其他訪問模式仍然有用,我將在下一篇文章中展示。)

動態共享內存

本例中的其他三個內核使用動態分配的共享內存,當編譯時共享內存的數量未知時,可以使用該內存。在這種情況下,必須使用可選的第三個執行配置參數指定每個線程塊的共享內存分配大小(以字節為單位),如下面的摘錄所示。

dynamicReverse<<<1, n, n*sizeof(int)>>>(d_d, n);

動態共享內存內核 dynamicReverse() 使用未大小化的外部數組語法 extern shared int s[] 聲明共享內存數組(注意空括號和 extern 說明符的使用)。大小在內核啟動時由第三個執行配置參數隱式確定。內核代碼的其余部分與 staticReverse() 內核相同。

如果在一個內核中需要多個動態大小的數組怎么辦?您必須像前面一樣聲明一個 extern 非大小數組,并使用指向它的指針將其劃分為多個數組,如下面的摘錄所示。

extern __shared__ int s[];
int *integerData = s; // nI ints
float *floatData = (float*)&integerData[nI]; // nF floats
char *charData = (char*)&floatData[nF]; // nC chars

在內核中指定啟動所需的總內存。

myKernel<<>>(...);

共享內存庫沖突

為了實現并發訪問的高內存帶寬,共享內存被分成大小相等的內存模塊(庫),這些模塊可以同時訪問。因此,任何跨越 b 不同內存組的 n 地址的內存負載或存儲都可以同時進行服務,從而產生的有效帶寬是單個存儲庫帶寬的 b 倍。

但是,如果多個線程的請求地址映射到同一個內存庫,則訪問將被序列化。硬件根據需要將沖突內存請求拆分為多個獨立的無沖突請求,將有效帶寬減少一個與沖突內存請求數量相等的因子。一個例外情況是,一個 warp 中的所有線程都使用同一個共享內存地址,從而導致廣播。計算能力 2 . 0 及更高版本的設備具有多播共享內存訪問的額外能力,這意味著在一個 warp 中通過任意數量的線程對同一個位置的多個訪問同時進行。

為了最小化內存沖突,了解內存地址如何映射到內存庫是很重要的。共享存儲庫被組織成這樣,連續的 32 位字被分配給連續的存儲庫,帶寬是每個庫每個時鐘周期 32 位。對于計算能力為 1 . x 的設備, warp 大小為 32 個線程,庫的數量為 16 個。一個 warp 的共享內存請求被分為一個對 warp 前半部分的請求和一個對 warp 后半部分的請求。請注意,如果每個內存庫只有一個內存位置被半個線程訪問,則不會發生庫沖突。

對于計算能力為 2 . 0 的設備, warp 大小是 32 個線程,而 bank 的數量也是 32 個。 warp 的共享內存請求不會像計算能力為 1 . x 的設備那樣被拆分,這意味著 warp 前半部分的線程和同一 warp 后半部分的線程之間可能會發生庫沖突。

計算能力為 3 . x 的設備具有可配置的存儲大小,可以使用 CUDA Devicsetsharedmeconfig() 將其設置為四個字節( CUDA SharedMemBankSizeFourByte ,默認值)或八個字節( cudaSharedMemBankSizeEightByte) 。將存儲大小設置為 8 字節有助于避免訪問雙精度數據時的共享內存庫沖突。

配置共享內存量

在計算能力為 2 . x 和 3 . x 的設備上,每個多處理器都有 64KB 的片上內存,可以在一級緩存和共享內存之間進行分區。對于計算能力為 2 . x 的設備,有兩個設置: 48KB 共享內存/ 16KB 一級緩存和 16KB 共享內存/ 48KB 一級緩存。默認情況下,使用 48KB 共享內存設置。這可以在運行時 API 期間使用 cudaDeviceSetCacheConfig() 為所有內核配置,也可以使用 cudaFuncSetCacheConfig() 在每個內核的基礎上進行配置。它們接受以下三個選項之一: cudaFuncCachePreferNonecudaFuncCachePreferSharedcudaFuncCachePreferL1 。驅動程序將遵循指定的首選項,除非內核每個線程塊需要比指定配置中可用的共享內存更多的共享內存。計算能力為 3 . x 的設備允許使用選項 cudaFuncCachePreferEqual 獲得 32KB 共享內存/ 32kbl1 緩存的第三個設置。

關于作者

Mark Harris 是 NVIDIA 杰出的工程師,致力于 RAPIDS 。 Mark 擁有超過 20 年的 GPUs 軟件開發經驗,從圖形和游戲到基于物理的模擬,到并行算法和高性能計算。當他還是北卡羅來納大學的博士生時,他意識到了一種新生的趨勢,并為此創造了一個名字: GPGPU (圖形處理單元上的通用計算)。

審核編輯:郭婷

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 處理器
    +關注

    關注

    68

    文章

    19408

    瀏覽量

    231187
  • NVIDIA
    +關注

    關注

    14

    文章

    5076

    瀏覽量

    103723
收藏 人收藏

    評論

    相關推薦

    Triton編譯器與GPU編程的結合應用

    優化,以及生成高效的并行執行計劃。 GPU編程的挑戰 GPU編程面臨的主要挑戰包括: 編程復雜性 :GPU編程需要對硬件架構有深入的理解,包括線程、塊和網格的概念。 內存管理 :
    的頭像 發表于 12-25 09:13 ?324次閱讀

    《CST Studio Suite 2024 GPU加速計算指南》

    。 2. 操作系統支持:CST Studio Suite在不同操作系統上持續測試,可在支持的操作系統上使用GPU計算,具體參考相關文檔。 3. 許可證:GPU計算功能通過CST Studio Suite
    發表于 12-16 14:25

    【「算力芯片 | 高性能 CPU/GPU/NPU 微架構分析」閱讀體驗】--了解算力芯片GPU

    每個CUDA單元在 OpenCL 編程框架中都有對應的單元。 倒金字塔結構GPU存儲體系 共享內存是開發者可配置的編程資源,使用門檻較高,編程上需要更多的人工顯式處理。 在并行計算架構
    發表于 11-03 12:55

    有沒有大佬知道NI vision 有沒有辦法通過gpucuda來加速圖像處理

    有沒有大佬知道NI vision 有沒有辦法通過gpucuda來加速圖像處理
    發表于 10-20 09:14

    16 口多模反射內存交換機:高速數據共享的核心樞紐

    在當今數字化和信息化高速發展的時代,數據的快速傳輸、實時共享以及高效處理成為了眾多行業和領域追求的關鍵目標。在這樣的背景下,16口多模反射內存交換機應運而生,成為了構建高性能數據共享網絡的重要
    的頭像 發表于 09-04 14:38 ?331次閱讀
    16 口多模反射<b class='flag-5'>內存</b>交換機:高速數據<b class='flag-5'>共享</b>的核心樞紐

    多模反射內存交換機:實現高速實時數據共享的關鍵設備

    在當今數字化、信息化的時代,數據的快速傳輸和實時共享對于許多領域的系統運行至關重要。多模反射內存交換機作為一種先進的網絡設備,為滿足這些需求提供了高效、可靠的解決方案。多模反射內存交換機是一種專門
    的頭像 發表于 09-04 10:55 ?361次閱讀
    多模反射<b class='flag-5'>內存</b>交換機:實現高速實時數據<b class='flag-5'>共享</b>的關鍵設備

    打破英偉達CUDA壁壘?AMD顯卡現在也能無縫適配CUDA

    電子發燒友網報道(文/梁浩斌)一直以來,圍繞CUDA打造的軟件生態,是英偉達在GPU領域最大的護城河,尤其是隨著目前AI領域的發展加速,市場火爆,英偉達GPU+CUDA的開發生態則更加穩固,AMD
    的頭像 發表于 07-19 00:16 ?4886次閱讀

    英國公司實現英偉達CUDA軟件在AMD GPU上的無縫運行

    7月18日最新資訊,英國創新科技企業Spectral Compute震撼發布了其革命性GPGPU編程工具包——“SCALE”,該工具包實現了英偉達CUDA軟件在AMD GPU上的無縫遷移與運行,標志著在GPU計算領域,NVIDI
    的頭像 發表于 07-18 14:40 ?714次閱讀

    軟件生態上超越CUDA,究竟有多難?

    神壇的,還是圍繞CUDA打造的一系列軟件生態。 ? 英偉達——CUDA的絕對統治 ? 相信對GPU有過一定了解的都知道,英偉達的最大護城河就是CUDA
    的頭像 發表于 06-20 00:09 ?3790次閱讀

    借助NVIDIA Aerial CUDA增強5G/6G的DU性能和工作負載整合

    Aerial CUDA 加速無線接入網 (RAN)可加速電信工作負載,使用 CPU、GPU 和 DPU 在云原生加速計算平臺上提供更高水平的頻譜效率 (SE)。
    的頭像 發表于 05-24 11:10 ?669次閱讀
    借助NVIDIA Aerial <b class='flag-5'>CUDA</b>增強5G/6G的DU性能和工作負載整合

    Hugging Face提供1000萬美元免費共享GPU

    全球最大的開源AI社區Hugging Face近日宣布,將提供價值1000萬美元的免費共享GPU資源,以支持開發者創造新的AI技術。這一舉措旨在幫助小型開發者、研究人員和初創公司,對抗大型AI公司的市場壟斷,推動AI領域的公平競爭。
    的頭像 發表于 05-20 09:40 ?713次閱讀

    Keil使用AC6編譯提示CUDA版本過高怎么解決?

    \' ArmClang: warning: Unknown CUDA version 10.2. Assuming the latest supported version 10.1
    發表于 04-11 07:56

    一文詳解GPU硬件與CUDA開發工具

    CPU 和 GPU 的顯著區別是:一個典型的 CPU 擁有少數幾個快速的計算核心,而一個典型的 GPU 擁有幾百到幾千個不那么快速的計算核心。
    的頭像 發表于 03-21 10:15 ?1363次閱讀
    一文詳解<b class='flag-5'>GPU</b>硬件與<b class='flag-5'>CUDA</b>開發工具

    GPU CUDA 編程的基本原理是什么

    神經網絡能加速的有很多,當然使用硬件加速是最可觀的了,而目前除了專用的NPU(神經網絡加速單元),就屬于GPU對神經網絡加速效果最好了
    的頭像 發表于 03-05 10:26 ?939次閱讀
    <b class='flag-5'>GPU</b> <b class='flag-5'>CUDA</b> 編程的基本原理是什么

    內存共享原理解析

    內存共享是一種在多個進程之間共享數據的機制,它允許不同的進程直接訪問同一塊內存區域,從而實現數據的快速傳遞和通信。
    的頭像 發表于 02-19 15:11 ?1403次閱讀
    <b class='flag-5'>內存</b><b class='flag-5'>共享</b>原理解析
    百家乐在发牌技巧| 顶级赌场官方下载| 网上百家乐官网注册彩金| 真人百家乐作假视频| 爱赢娱乐| 百家乐官网投资| 博彩网站排名| 属兔做生意门面房朝向| 御金娱乐城| 赌博百家乐规则| 百家乐官网有赢钱公式吗| 金木棉百家乐的玩法技巧和规则 | 娱乐城注册送38| 澳门百家乐职业| 太阳城百家乐官网杀猪吗| 全讯网官网| 千亿娱百家乐官网的玩法技巧和规则 | 百家乐保单机作弊| 百家乐官网赌博是否违法| 大发888娱乐城客服| 什么是24山风水| 百家乐官网几点开奖| 天天百家乐的玩法技巧和规则 | 百家乐赌博论坛在线| 赌博百家乐官网秘籍| 大发888在线| 合肥百家乐赌博游戏机| 百家乐官网注册送免费金| 全讯网高手论坛| 百家乐官网开户过的路纸| 铜鼓县| 黄金城百家乐手机用户| 百家乐官网怎么玩会| 怀远县| 大发888娱乐场客户端下载| 百家乐规则澳门| 足球竞猜网| 百家乐投注外围哪里好| 富二代百家乐官网的玩法技巧和规则| 百家乐官网论坛百科| 大发888备用地址|