DeepDriving | CUDA編程-02: 初識CUDA編程

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

原文鏈接:CUDA編程-02: 初識CUDA編程

上一篇文章DeepDriving | CUDA編程-01: 搭建CUDA編程環境-CSDN博客介紹了如何搭建CUDA編程環境,從這篇文章開始正式開始介紹如何使用CUDA進行編程。

1 異構計算架構

如下圖所示,一個典型的異構計算架構節點由一個多核CPU和一個或多個GPU組成,每個CPUGPU都是獨立的設備,它們之間通過PCIe總線連接。GPU作為CPU的協處理器用于執行一些并行計算任務。CPU適合用于做一些邏輯控制任務,而GPU則適合作為CPU的協處理器用于做一些大計算量的數據并行計算任務。

heterogeneous_architecture

一般我們稱CPUhost,稱GPUdevice,相應地,一個異構計算的應用程序代碼也被分為兩部分:運行在CPU上的程序被稱為host代碼,運行在GPU上的程序被稱為device代碼。

heterogeneous_architecture

2 一個Hello World

在我們學習一種新的編程語言的時候,一般都是先從打印一句"Hello World"開始,今天開始學習CUDA編程,按照國際慣例,也從打印"Hello World"開始。

首先新建一個CUDA C源文件hello_world.cu,然后輸入下面的內容:

#include <stdio.h>int main(void)
{printf("Hello World from CPU\n");return 0;
}

nvcc進行編譯生成可執行文件:

nvcc?hello_world.cu?-o?hello_world

運行可執行文件hello_world,沒有意外的話就能在終端看到打印出的"Hello World from CPU"了。這是在CPU上運行代碼打印了這句話,那么怎么在GPU上打印呢?

要在GPU上運行程序,我們需要寫一個能在GPU上執行的函數,然后在CPU上調用這個函數。來看一個例子:

#include <stdio.h>__global__ void HelloFromGPU(void)
{printf("Hello from GPU\n");
}int main(void)
{printf("Hello from CPU\n");HelloFromGPU<<<1, 5>>>();cudaDeviceReset();return 0;
}

在上面的這段代碼里,我添加了一個用__global__函數類型限定符修飾的函數HelloFromGPU,然后在main函數中去調用它。在CUDA中,有以下3種函數類型限定符:

_global_

__global__函數類型限定符修飾的函數被稱為內核函數,該函數在host上被調用,在device上執行,只能返回void類型,不能作為類的成員函數。調用__global__修飾的函數是異步的,也就是說它未執行完就會返回。

_device_

__device__函數類型限定符修飾的函數只能在device上被調用,在device上執行,用于在device代碼中內部調用。

_host_

__host__函數類型限定符修飾的函數只能在host上被調用,在host上執行,也就是host上的函數,__host__函數類型限定符可以省略。

與調用一般CPU函數不同的是,調用HelloFromGPU函數的時候會在后面寫上<<< >>>,意思這是從host端到device端的內核函數調用,里面的參數是執行配置,用來說明使用多少線程來執行內核函數。一個內核函數是通過一組線程來執行的,所有線程執行的同樣的代碼,我這里設置的是用了5個線程來執行。程序編譯成功后運行得到如下結果:

Hello from CPU
Hello from GPU
Hello from GPU
Hello from GPU
Hello from GPU
Hello from GPU

可以看到,內核函數里的打印語句被執行了5次。

3 用CUDA實現數組相加

一個典型的CUDA程序結構一般由以下5個步驟組成:

  1. 分配GPU內存;

  2. CPU內存中拷貝數據到GPU內存中;

  3. 調用CUDA內核函數執行程序指定的計算任務;

  4. GPU內存中把數據拷貝回CPU內存中;

  5. 釋放GPU內存;

下面以一個數組相加的例子來展示以上過程,數組相加的計算過程比較簡單:數組a和數組b中對應下標的元素相加然后存入數組c中。

vector_add

首先來看一下在CPU上實現數組相加的代碼:

#include?<iostream>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];}
}int?main(void)?{//?alloc?memory?for?hostconst?size_t?size?=?1024;float?*ha?=?new?float[size]();float?*hb?=?new?float[size]();float?*hc?=?new?float[size]();for?(int?i?=?0;?i?<?size;?++i)?{ha[i]?=?i;hb[i]?=?size?-?i;}VectorAddCPU(ha,?hb,?hc,?size);delete[]?ha;delete[]?hb;delete[]?hc;return?0;
}

函數VectorAddCPU用了一個for循環在CPU上實現數組相加的過程。如果要用GPU來實現該過程,則調用CUDAAPI按照前面說的5個步驟編寫代碼:

#include?<cuda_runtime.h>
#include?<iostream>__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;if?(i?<?n)?{c[i]?=?a[i]?+?b[i];}
}int?main(void)?{//?分配CPU內存const?size_t?size?=?1024;float?*ha?=?new?float[size]();float?*hb?=?new?float[size]();float?*hc?=?new?float[size]();for?(int?i?=?0;?i?<?size;?++i)?{ha[i]?=?i;hb[i]?=?size?-?i;}//?分配GPU內存float?*da?=?nullptr;float?*db?=?nullptr;float?*dc?=?nullptr;cudaMalloc((void?**)&da,?size);cudaMalloc((void?**)&db,?size);cudaMalloc((void?**)&dc,?size);//?把數據從CPU拷貝到GPUcudaMemcpy(da,?ha,?size,?cudaMemcpyHostToDevice);cudaMemcpy(db,?hb,?size,?cudaMemcpyHostToDevice);cudaMemcpy(dc,?hc,?size,?cudaMemcpyHostToDevice);const?int?thread_per_block?=?256;const?int?block_per_grid?=?(size?+?thread_per_block?-?1)?/?thread_per_block;//?調用核函數VectorAddGPU<<<block_per_grid,?thread_per_block>>>(da,?db,?dc,?size);//?把數據從GPU拷貝回CPUcudaMemcpy(hc,?dc,?size,?cudaMemcpyDeviceToHost);//?釋放GPU內存cudaFree(da);cudaFree(db);cudaFree(dc);//?釋放CPU內存delete[]?ha;delete[]?hb;delete[]?hc;return?0;
}

在這段代碼里,用到了幾個CUDA的內存管理函數:

cudaMalloc

函數原型:

cudaError_t cudaMalloc(void** devPtr, size_t size)

該函數用于在GPU上分配指定大小的內存空間,類似于標準C語言中的malloc函數。

cudaMemcpy

函數原型:

cudaError_t cudaMemcpy(void* dst, const void* src, size_t count, cudaMemcpyKind kind)

該函數用于內存拷貝,類似于標準C語言中的memcpy函數。數據拷貝的流向由參數kind指定,分為以下4種方式:

  • cudaMemcpyHostToHost

  • cudaMemcpyHostToDevice

  • cudaMemcpyDeviceToHost

  • cudaMemcpyDeviceToDevice

從它們的字面意思就能知道,如果是從host拷貝數據到device,那么kind參數應該設置為cudaMemcpyHostToDevice;如果是從device拷貝數據到host,那么kind參數應該設置為cudaMemcpyDeviceToHost。上面的代碼體現了這一點。

cudaFree

udaError_t cudaFree(void* devPtr)

該函數用于釋放已分配的GPU內存空間,類似于標準C語言中的free函數。

在上面的代碼中,內核函數VectorAddGPU用于實現數組相加,與前面的代碼中VectorAddCPU函數不同的是,VectorAddGPU函數里面并沒有用for循環,因為在GPU中是將數據進行并行化劃分,然后通過線程組去實現計算過程的,線程組中的每個線程都是執行c[i] = a[i] + b[i]這個計算過程。在這個程序里,數組的長度為1024,我設置了每個線程組中的線程數量為256,總共用了4個線程組去進行計算。

關于GPU中線程和線程組的相關知識,我將在下一篇文章中進行闡述。

4 參考資料

  • Professional CUDA C Programming

  • CUDA C Programming_Guide

THE END !

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

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

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

相關文章

選擇、快排、堆排序、歸并

選擇排序 排序的核心是&#xff1a;在未排序的序列中&#xff0c;把未排序第一個元素和未排序的最小元素交換位置。 因此&#xff0c;設計時&#xff0c;顯然要設置兩重 for 循環 假設未排序的第一個元素稱為 a &#xff0c; 未排序的最小元素稱為 b 第一重 for 循環控制總…

web壓力測試,要不要過濾掉JS,CSS等請求?

在進行性能測試&#xff08;壓測&#xff09;時&#xff0c;是否過濾掉對JavaScript、CSS等靜態資源的請求&#xff0c;取決于你測試的目標和目的。 是測試服務端的性能還是前端的性能。這兩種目的所涉及到的測試場景和工具等方法是不一樣的。 一般的web產品&#xff0c;像cs…

java 8--Lambda表達式,Stream流

目錄 Lambda表達式 Lambda表達式的由來 Lambda表達式簡介 Lambda表達式的結構 Stream流 什么是Stream流&#xff1f; 什么是流呢&#xff1f; Stream流操作 中間操作 終端操作 Lambda表達式 Lambda表達式的由來 Java是面向對象語言&#xff0c;除了部分簡單數據類型…

利用kubeadm安裝k8s集群 以及跟harbor私有倉庫下載鏡像

目錄 環境準備 master&#xff08;2C/4G&#xff09; 192.168.88.3 docker、kubeadm、kubelet、kubectl、flannel node01&#xff08;2C/2G&#xff09; 192.168.88.4 docker、kubeadm、kubelet、kubectl、flannel node02&#xff08;…

2024中青杯數學建模競賽B題藥物屬性預測思路代碼論文分享

2024年中青杯數學建模競賽B題論文和代碼已完成&#xff0c;代碼為B題全部問題的代碼&#xff0c;論文包括摘要、問題重述、問題分析、模型假設、符號說明、模型的建立和求解&#xff08;問題1模型的建立和求解、問題2模型的建立和求解、問題3模型的建立和求解&#xff09;、模型…

QT調用Tinyxml2庫解析XML結構文件

在學習SVG結構的時候&#xff0c;發現SVG結構可以通過以XML文件直接解析&#xff0c;所以就去了解了Tinyxml2庫的使用&#xff0c;相關教程也比較多。 個人感覺Tinyxml2庫比官方的XML解析庫更好用&#xff0c;這里做個技術總結&#xff0c;記錄Tinyxml2庫解析XML文件結構的簡單…

【Linux取經路】一個簡單的日志模塊

文章目錄 一、可變參數的使用二、Log2.1 日志打印2.1.1 時間獲取2.1.2 日志分塊打印 2.2 打印模式選擇2.3 Log 使用樣例2.4 Log 完整源碼 三、結語 一、可變參數的使用 int sum(int n, ...) {va_list s; // va_list 本質上就是一個指針va_start(s, n); int sum 0;while(n){su…

為什么以太網適配器不是192.168而是196.254【筆記】

為什么以太網適配器不是192.168而是196.254【筆記】 前言版權為什么以太網適配器不是192.168而是196.254最后 前言 2024-03-12 22:55:34 公開發布于 2024-5-22 00:20:35 以下內容源自《【筆記】》 僅供學習交流使用 版權 禁止其他平臺發布時刪除以下此話 本文首次發布于CS…

Linux: tools: crash: not a supported file format

這個原因是,通過比對每個format的magic數值,或者其他的信息,來看是否屬于某個format,如果都不符合,就會出現這個錯誤。說明kernel的coredump文件,dump的有些問題。 main (argc=3, argv=0x7fffffffda88) at main.c:496 496 } else if (is_compressed…

Java高級面試精粹:問題與解答集錦(一)

Java 面試問題及答案 1. 什么是Java中的多態&#xff0c;它是如何實現的&#xff1f; 答案&#xff1a; 多態是Java中的一個核心概念&#xff0c;它允許不同類的對象對同一消息做出響應&#xff0c;但具體的行為會根據對象的實際類型而有所不同。多態主要通過以下兩種方式實現…

git命令行指引

命令行指引 您還可以按照以下說明從計算機中上傳現有文件。 Git 全局設置 git config --global user.name "lizhijun" git config --global user.email "oldgunqfhotmail.com"創建一個新倉庫 git clone gitfiles.tfedu.net:aigk985-gaokao/Folder-watc…

計算機畢業設計 | node.js(Express)+vue影院售票商城 電影放映購物系統(附源碼+論文)

1&#xff0c;緒論 1.1 項目背景 最近幾年&#xff0c;我國影院企業發展迅猛&#xff0c;各大電影院不斷建設新的院線&#xff0c;每年新投入使用的熒幕數目逐年顯著上升。這離不開人們的觀影需求及對觀影的過程要求的不斷進步。廣大觀影消費者需要知道自己的空閑時間&#x…

Django中使用Celery(通用方案、官方方案)

Django中使用Celery&#xff08;通用方案、官方方案&#xff09; 目錄 Django中使用Celery&#xff08;通用方案、官方方案&#xff09;通用方案場景前置準備完整代碼 Celery官方方案【1】注冊celery配置【2】創建celery文件【3】init注冊【4】添加任務【5】啟動worker異步任務…

設計模式六大原則之依賴倒置原則

文章目錄 概念邏輯關系 小結 概念 依賴倒置原則指在設計代碼架構時&#xff0c;高層模塊不應該依賴底層模塊&#xff0c;二者都應該依賴抽象。抽象不應該依賴于細節&#xff0c;細節應該依賴于抽象。 邏輯關系 如上圖所示&#xff0c;邏輯應該就是這樣&#xff0c;高層依賴于…

解決Wordpress中Cravatar頭像無法訪問問題

一、什么是Cravatar Gravatar是WordPress母公司Automattic推出的一個公共頭像服務&#xff0c;也是WordPress默認的頭像服務。但因為長城防火墻的存在&#xff0c;Gravatar在中國時不時就會被墻一下&#xff0c;比如本次從2021年2月一直到8月都是不可訪問狀態。 在以往的時候&…

Java_IO流學習

IO流 概念 I – in – 輸入(讀) O – out – 輸出(寫) 流 – 一點一點的像水流一樣去傳輸數據 注意&#xff1a;站在程序的角度去看待輸入還是輸出 分類 按照方向分流&#xff1a;輸入流、輸出流 按照單位分流&#xff1a;字節流、字符流 按照功能分流&#xff1a;基礎流/節點…

R語言:單細胞pcoa降維和去批次

#生成隨機顏色 > randomColor <- function() { paste0("#",paste0(sample(c(0:9, letters[1:6]), 6, replace TRUE),collapse "")) } # 生成100個隨機顏色 > randomColors <- replicate(100,randomColor()) > seuratreadRDS("seu…

RAG系統(四)手撕基于向量檢索的 RAG

RAG系統&#xff08;一&#xff09;系統介紹與向量檢索 RAG系統&#xff08;二&#xff09;文檔的加載與分段 RAG系統&#xff08;三&#xff09;向量數據庫 完整代碼需要依賴前邊三章中的代碼&#xff0c;本節主要展示整合后的RAG系統&#xff0c;及運行示例。 from MyVec…

前端javascript包管理,npm升級用pnpm

一 pnpm 介紹 pnpm&#xff08;Package Manager&#xff09;是一個快速、節省磁盤空間的 JavaScript 包管理器&#xff0c;它是 Node.js 生態系統中 npm 的一個替代品。pnpm 解決了傳統包管理工具在處理依賴時的一些痛點&#xff0c;特別是關于存儲空間使用和依賴地獄的問題。…

如何將Google Search Console添加到WordPress和GA4

您想知道如何將 Google Search Console 添加到您的 Google Analytics 帳戶和 WordPress 網站嗎&#xff1f; 作為網站主&#xff0c;Google Search Console 是一款不能不使用的工具。對于任何想要確保其網站在 Google 搜索結果中表現良好的人來說&#xff0c;這絕對是一個必不…