《基於CUDA的並行程式設計》學習筆記(三)——下
第3章 CUDA程式設計基礎
3.5 “HelloWorld”CUDA 程式設計例項
安裝完Visual Studio 2013軟體並配置好CUDA開發環境。本節我們正式CUDA程式設計。
首先我們開啟vs2013,點選新建工程,選中CUDA 7.5的模板。輸入工程名等資訊,完成工程的建立。
建立完工程後,會預設有一個kernel.cu的檔案,其實現的是矩陣相加,這時候可以直接除錯執行,如果沒有報錯則證明可以進行CUDA程式設計了。
可能會出現,下面這種無法識別<<<
符號的情況,但是目測不影響結果的輸出。
執行結果如下:
如果想編寫執行自己的程式,則需要先移除kernel.cu檔案,不然會出現main函式重複宣告。
我們現在開始編寫自己的CUDA程式,首先移除原來的kernel.cu檔案,然後右鍵工程名,選擇新增新item。
選擇CUDA C/C++ File
,然後輸入檔名為main,就可以得到一個空的main.cu檔案。在檔案中輸入如下程式碼:
#include <stdio.h>
#include <cuda_runtime.h>
#include <string>
// 要使用 runtime API 的時候,需要 include cuda_runtime.h
bool InitCUDA()
{
int count;
// 獲取計算能力>=1.0的裝置數量
cudaGetDeviceCount(&count);
printf("%d\n", count);
if (count == 0) {
fprintf(stderr, "There is no device./n");
return false;
}
int i;
for (i = 0; i < count; i++)
{
// 指定裝置的屬性
cudaDeviceProp prop;
if (cudaGetDeviceProperties(&prop, i) == cudaSuccess)
{
// 輸出裝置名稱
char *c = prop.name;
printf("%s\n",c);
// 定義裝置計算能力的主要修訂編號
printf("%d",prop.major);
// 定義裝置計算能力的次要修訂編號
printf("%d\n", prop.minor);
if (prop.major >= 1) {
break;
}
}
}
if (i == count) {
fprintf(stderr, "There is no device supporting CUDA 1.x./n");
return false;
}
// 把裝置設定為呼叫主執行緒的當前裝置
cudaSetDevice(i);
return true;
}
__global__ void kernel(char *str1, char *str2)
{
while ((*str1) != '\0')
*str2++ = *str1++;
*str2 = '\0';
}
int main()
{
if (!InitCUDA()) {
return 0;
}
char* host_str1;
host_str1 = "HelloWorld";
char* host_str2 = new char[11];
int size = strlen(host_str1) + 1;
char* dev_str1;
char* dev_str2;
cudaMalloc((void**)&dev_str1, size);
cudaMalloc((void**)&dev_str2, size);
cudaMemcpy(dev_str1, host_str1, size, cudaMemcpyHostToDevice);
kernel <<<1, 1 >>>(dev_str1, dev_str2);
cudaMemcpy(host_str2, dev_str2, size, cudaMemcpyDeviceToHost);
printf("%s\n",host_str2);
cudaFree(dev_str1);
cudaFree(dev_str2);
getchar();
return 0;
}
InitCUDA()
中會先呼叫 cudaGetDeviceCount 函式,取得支援 CUDA 的裝置的數目。如果系統上沒有支援 CUDA 的裝置,則它會傳回 1,而 device 0 會是一個模擬的裝置,但不支援 CUDA 1.0 以上的功能。所以,要確定系統上是否有支援 CUDA 的裝置,需要對每個 device 呼叫 cudaGetDeviceProperties 函式,取得裝置的各項資料,並判斷裝置支援的 CUDA 版本(prop.major 和 prop.minor 分別代表裝置支援的版本號碼,例如 1.0 則 prop.major 為 1 而 prop.minor 為 0)。
透過 cudaGetDeviceProperties 函式可以取得許多資料,除了裝置支援的 CUDA 版本之外,還有裝置的名稱、記憶體的大小、最大的 thread 數目、執行單元的頻率等等。詳情可參考 NVIDIA 的 CUDA Programming Guide。
在找到支援 CUDA 1.0 以上的裝置之後,就可以呼叫 cudaSetDevice 函式,把它設為目前要使用的裝置。
編譯後輸出如下:
能正確輸出上述結果,則代表第一個完整的CUDA程式到此結束了。但是這個專案的結構看上去一點也不規範化,所有的程式碼都寫到一個原始檔裡,這樣的程式碼不易讀懂,也不好定位程式的錯誤。CUDA程式一般可以進行如下圖所示的檔案結構管理。
如上圖所示,編寫CUDA程式過程中,若某個功能適合序列,則編寫序列程式碼在CPU端執行,若適合並行,則編寫CUDA並行程式碼在GPU端執行。CUDA並行程式碼的機構應進行如下分離:
(1) CUDA程式呼叫介面封裝了和CUDA相關的函式,一般位於.cpp檔案中。
(2) CUDA主機端程式碼的主要功能包括選擇計算裝置,進行GPU端儲存器的分配、主機端與裝置端直接的資料複製及呼叫kernel函式等準備工作,一般位於一個獨立的.cu檔案中。
(3) CUDA裝置端程式碼主要指GPU端執行的核心程式碼kernel函式。kernel函式可能有多個,各個kernel函式各自放在一個單獨的以kernel函式功能命名的.cu檔案下,因此kernel函式相關的檔案可能有多個。
根據以上程式碼管理的方法,對HelloWordl程式改寫如下:在原始檔建立3個檔案(main.cpp、kernel.cu和GPU_HelloWorld.cu)。main.cpp中呼叫核函式GPU_HelloWorld(host_str1, host_str2)
相當於上述結構中的“GPU函式”,kernel.cu檔案相當於上述結構中的“kernel函式”。
main.cpp檔案主要包含了一個完整程式執行的主框架,其程式碼改寫如下:
#include<stdio.h>
extern bool GPU_HelloWorld(char* host_str1, char* host_str2);
int main()
{
char* host_str1;
host_str1 = "HelloWorld";
char* host_str2 = new char[11];
if(!GPU_HelloWorld(host_str1, host_str2))
return 0;
printf("%s\n",host_str2);
return 0;
}
kernel.cu檔案為GPU端執行函式的核心程式碼,其程式碼如下:
#include <cuda_runtime.h>
__global__ void kernel(char *str1, char *str2)
{
while ((*str1) != '\0')
*str2++ = *str1++;
*str2 = '\0';
}
GPU_HelloWorld.cu檔案作用是為kernel函式選擇可用的計算裝置並進行呼叫環境準備,其程式碼如下:
#include "kernel.cu"
#include <cuda_runtime.h>
#include <string>
bool InitCUDA()
{
int count;
// 獲取計算能力>=1.0的裝置數量
cudaGetDeviceCount(&count);
printf("%d\n", count);
if (count == 0) {
fprintf(stderr, "There is no device./n");
return false;
}
int i;
for (i = 0; i < count; i++)
{
// 指定裝置的屬性
cudaDeviceProp prop;
if (cudaGetDeviceProperties(&prop, i) == cudaSuccess)
{
// 輸出裝置名稱
char *c = prop.name;
printf("%s\n",c);
// 定義裝置計算能力的主要修訂編號
printf("%d",prop.major);
// 定義裝置計算能力的次要修訂編號
printf("%d\n", prop.minor);
if (prop.major >= 1) {
break;
}
}
}
if (i == count) {
fprintf(stderr, "There is no device supporting CUDA 1.x./n");
return false;
}
// 把裝置設定為呼叫主執行緒的當前裝置
cudaSetDevice(i);
return true;
}
bool GPU_HelloWorld(char* host_str1, char* host_str2)
{
if(!InitCUDA())
{
return 0;
}
int size = strlen(host_str1)+1;
char* dev_str1;
char* dev_str2;
cudaMalloc((void**)&dev_str1, size);
cudaMalloc((void**)&dev_str2, size);
cudaMemcpy(dev_str1, host_str1, size, cudaMemcpyHostToDevice);
kernel <<<1, 1 >>>(dev_str1, dev_str2);
cudaMemcpy(host_str2, dev_str2, size, cudaMemcpyDeviceToHost);
cudaFree(dev_str1);
cudaFree(dev_str2);
return 1;
}
編譯執行結構修改後的專案,將得到和修改前一樣的執行結果。編譯時需要將kernel.cu檔案從專案生成中排除,否則編譯連線時會出錯。
注:如何從生成專案中移除
(1) 右鍵想要移除的檔案,選擇屬性
(2) 點選右側的從從生成中移除的yes
,然後點選確定
(3) 弄好了後看到之前選擇檔案上有一個紅色提示符
相關推薦
《基於CUDA的並行程式設計》學習筆記(三)——下
第3章 CUDA程式設計基礎 3.5 “HelloWorld”CUDA 程式設計例項 安裝完Visual Studio 2013軟體並配置好CUDA開發環境。本節我們正式CUDA程式設計。 首先我們開啟vs2013,點選新建工程,選中CUD
CUDA 學習筆記 (二) 【Chapter4 CUDA並行程式設計】
Capter4 CUDA並行程式設計 前面我們看到將一個標準C函式放到GPU裝置上執行是很容易的。只需要在函式定義前面加上 __globle__ 修飾符,並通過一種特殊的尖括號語法來呼叫它,就可以在GPU上執行這個函式。然而,前面的示例只調用了一個和函式,並且該函式在GPU上以序列方
ROS學習筆記(三):自定義話題的程式設計
前言:ros給我們提供了眾多的訊息結構,但是更多時候我們需要根據自己的研發需求定義自己的訊息結構。 一、檢視ros自帶的訊息結構 我們最常用的一個訊息結構就是std_msgs,那麼怎麼檢視這個訊息結構支援可以定義哪些資料型別呢? 我們使用roscd std_msgs/這個命令開啟該訊息結
Excel中VBA程式設計學習筆記(三)
12、使用InputBox函式進行輸入 語法如下: InputBox(prompt [,title] [,default] [,xpos] [,ypos] [,helpfile,context]) 引數說明: prompt為提示內容,必選; title對
Linux學習筆記 三 linux下的連結庫以及實現
1、連結庫概述 Linux下得庫有動態與靜態兩種,動態通常用.so為字尾,靜態用.a為字尾。面對比一下兩者: 靜態連結庫:當要使用時,聯結器會找出程式所需的函式,然後將它們拷貝到執行檔案,由於這種拷貝是完整的,所以一旦連線成功,靜態程式庫也就不再需要了。
Javascript高階程式設計學習筆記(三)—— JS中的資料型別(1)
前一段時間由於事情比較多,所以筆記耽擱了一段時間,從這一篇開始我會盡快寫完這個系列。 文章中有什麼不足之處,還望各位大佬指出。 JS中的資料型別 上一篇中我寫了有關JS引入的Script標籤相關的東西。 那麼這一篇,我們可以正式進入JS的世界了,emmm 前面的東西應該比較基礎,大佬們不
《CUDA並行程式設計:GPU程式設計指南》筆記 Chaper 4 環境搭建
關於平臺:Windows、Linux、Mac OS都支援 Windows下環境搭建順序: (1) VS 2010 (必須先安裝VS!,然後再安裝其他) (2) CUDA ToolKit (在9.0版本中
《C語言程式設計:現代方法(第2版)(K.N.King 著)》學習筆記三:C語言基本概念(2)
2.3 註釋 每一個程式都應該包含識別資訊,即程式名、編寫日期、作者、程式的用途以及其他相關資訊。C語言把這類資訊放在註釋(comment)中。 符號 /* 標記註釋的開始,而符號 */ 則標記註釋
Python程式設計入門學習筆記(三)
### 切片 ```python line = 'Welcome to Beijing,welcome to China!' #取字串的前10個字元,line[0:10],預設是0 line[:10] ``` 'Welcome to' ```pyt
Android學習筆記三十六:android之socket程式設計例項
注意點:註冊訪問的網路許可權;android中UI執行緒不能有訪問網路的操作,否則會報android.os.NetworkOnMainThreadException的異常 <uses-permission android:name="
Java高併發程式設計學習筆記(三):Java記憶體模型和執行緒安全
文章目錄 原子性 有序性 可見性 – 編譯器優化 – 硬體優化(如寫吸收,批操作) Java虛擬機器層面的可見性 Happen-Before規則(先行發生) 程式順序原則: volat
評估深度學習模型-在keras中使用scikit-learn-基於keras的python學習筆記(三)
版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/weixin_44474718/article/details/86249827 使用交叉驗證評估模型 KerasClassifier和kerasregressor 類使用引數build
《GPU高效能程式設計CUDA實戰》學習筆記(一)
第一天讀這本書,先將封面讓大家看看吧 這兩張圖好大啊,不過讀者會更加清晰的看到作者資訊,這樣也不錯。 近年來英偉達在CUDA上float運算效能基本上已經超過了cpu,並且gpu(Graphics Procdss Unit)程式設計難度也接近cpu程式設計。NVID
《GPU高效能程式設計CUDA實戰》學習筆記(九)
第9章 原子性 在某些情況下,對於單執行緒應用程式來說非常簡單的任務,或許使用大規模的並行架構實現卻會變成一個複雜的問題。這裡我們將在這些情況中使用特殊的原語從而確保安全地完成傳統單執行緒應用程式中的簡單任務。 9.1 本章目標 瞭解不同NVIDIA GPU的計算功能集。
《GPU高效能程式設計CUDA實戰》學習筆記(四)
#ifndef __BOOK_H__ #define __BOOK_H__ #include <stdio.h> static void HandleError( cudaError_t err, const char *file,
Spring學習筆記(三)基於XML Schema的配置方式
前言:Spring2.0開始,Spring允許使用基於XML Schema的配置方式來簡化Spring配置檔案,這種方式更加簡潔,可以對Spring配置檔案進行“減肥”。 Spring配置檔案的基本配置的<beans>標籤包含如下配置: <?xml v
多執行緒程式設計學習筆記——執行緒同步(三)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; //引入執行緒 using System.Diagnostics; namesp
cuda 程式設計學習筆記
programming model kernels 類似於c函式,函式定義使用global宣告,使用<<<…>>>形式的execution configuration決定kernal執行的執行緒數,使用threadId
Windows程式設計學習筆記(三)——視窗和訊息
MessageBox函式會建立一個‘視窗’。在Windows中,一個視窗就是螢幕上一個矩形區域,它接收使用者的輸入並以文字或圖形的格式顯示輸出內容。MessageBox函式建立一個視窗,但只是一個功能有
Android學習筆記三十八:Android4.0 Socket異常,需要另外開闢執行緒進行Socket程式設計
Socket socket = new Socket(); socket.connect(new InetSocketAddress(ConstData.TCP_IP, ConstData.TCP_PORT), 2000); 通