1. 程式人生 > >[菜鳥每天來段CUDA_C]多GPU的使用

[菜鳥每天來段CUDA_C]多GPU的使用

單個GPU具有強大的平行計算的能力,當把多個GPU同時用來執行同一個任務的時候,計算的效能將會得到更大的提升。本文在兩塊GPU上實現大資料量的向量點積運算。主要步驟為:

1.      獲得裝置數量;

2.      任務分配;

3.      為每個任務建立一個執行緒;

4.      啟動每個執行緒進行運算;

5.      合併每個GPU得到的結果。

程式程式碼:

主程式:main.cpp

#include "main.h"
#include <stdio.h>

extern "C" void runDotProduct(float *dev_a, float *dev_b, float *dev_partial_c, int size);

void* worker(void *pvoidData)
{
	GPUPlan *plan = (GPUPlan*) pvoidData;
	HANDLE_ERROR(cudaSetDevice(plan->deviceID));

	int size = plan->size;
	float *a, *b, c, *partial_c;
	float *dev_a, *dev_b, *dev_partial_c;

	a = plan->a;
	b = plan->b;
	partial_c = (float*)malloc(blockPerGrid*sizeof(float));

	HANDLE_ERROR(cudaMalloc((void**)&dev_a, size*sizeof(float)));
	HANDLE_ERROR(cudaMalloc((void**)&dev_b, size*sizeof(float)));
	HANDLE_ERROR(cudaMalloc((void**)&dev_partial_c, blockPerGrid*sizeof(float)));

	HANDLE_ERROR(cudaMemcpy(dev_a, a, size*sizeof(float), cudaMemcpyHostToDevice));
	HANDLE_ERROR(cudaMemcpy(dev_b, b, size*sizeof(float), cudaMemcpyHostToDevice));

	runDotProduct(dev_a, dev_b, dev_partial_c, size);

	HANDLE_ERROR(cudaMemcpy(partial_c, dev_partial_c, blockPerGrid*sizeof(float), cudaMemcpyDeviceToHost));

	c = 0;
	for (int i=0; i<blockPerGrid; i++)
	{
		c += partial_c[i];
	}

	HANDLE_ERROR(cudaFree(dev_a));
	HANDLE_ERROR(cudaFree(dev_b));
	HANDLE_ERROR(cudaFree(dev_partial_c));

	free(partial_c);
	plan->returnValue = c;
	return 0;
}



int main()
{
	//on two GPUs
	int i;
	int deviceCount;
	HANDLE_ERROR(cudaGetDeviceCount(&deviceCount));

	if (deviceCount < 2)
	{
		printf("No more than 2 device with compute 1.0 or greater."
			"only %d devices found", deviceCount);
		return 0;
	}

	float *a = (float*)malloc(sizeof(float)*N);
	HANDLE_NULL(a);
	float *b = (float*)malloc(sizeof(float)*N);
	HANDLE_NULL(b);

	for (i=0; i<N; i++)
	{
		a[i] = i;
		b[i] = i * 2;
	}

	GPUPlan plan[2];
	plan[0].deviceID = 0;
	plan[0].size = N/2;
	plan[0].a = a;
	plan[0].b = b;

	plan[1].deviceID = 1;
	plan[1].size = N/2;
	plan[1].a = a + N/2;
	plan[1].b = b + N/2;

	cudaEvent_t start, stop;
	HANDLE_ERROR(cudaEventCreate(&start));
	HANDLE_ERROR(cudaEventCreate(&stop));
	float elapsedTime;

	HANDLE_ERROR(cudaEventRecord(start));
	
	CUTThread mythread1 = start_thread((CUT_THREADROUTINE)worker, &plan[0]);
	CUTThread mythread2 = start_thread((CUT_THREADROUTINE)worker, &plan[1]);
	//worker(&plan[1]);

	end_thread(mythread1);
	end_thread(mythread2);

	HANDLE_ERROR(cudaEventRecord(stop));
	HANDLE_ERROR(cudaEventSynchronize(stop));

	HANDLE_ERROR(cudaEventElapsedTime(&elapsedTime, start, stop));
		printf("Computing by 2 GPUs finished in %3.1f <ms>\n", elapsedTime);

	printf("value calculated: %f\n", plan[0].returnValue + plan[1].returnValue);

	HANDLE_ERROR(cudaEventDestroy(start));
	HANDLE_ERROR(cudaEventDestroy(stop));
	free(a);
	free(b);

	// on one GPU
	float *host_a;
	float *host_b;
	float *partial_c;
	host_a = (float*)malloc(N*sizeof(float));
	host_b = (float*)malloc(N*sizeof(float));
	partial_c = (float*)malloc(blockPerGrid*sizeof(float));

	for (int i=0; i<N; i++)
	{
		host_a[i] = i;
		host_b[i] = 2 * i;
	}

	float *dev_a, *dev_b, *dev_partial_c;

	HANDLE_ERROR(cudaMalloc((void**)&dev_a, N*sizeof(float)));
	HANDLE_ERROR(cudaMalloc((void**)&dev_b, N*sizeof(float)));
	HANDLE_ERROR(cudaMalloc((void**)&dev_partial_c, blockPerGrid*sizeof(float)));

	HANDLE_ERROR(cudaMemcpy(dev_a, host_a, N*sizeof(float), cudaMemcpyHostToDevice));
	HANDLE_ERROR(cudaMemcpy(dev_b, host_b, N*sizeof(float), cudaMemcpyHostToDevice));

	cudaEvent_t start1, stop1;
	HANDLE_ERROR(cudaEventCreate(&start1));
	HANDLE_ERROR(cudaEventCreate(&stop1));

	HANDLE_ERROR(cudaEventRecord(start1));
	runDotProduct(dev_a, dev_b, dev_partial_c, N);
	HANDLE_ERROR(cudaEventRecord(stop1));
	HANDLE_ERROR(cudaEventSynchronize(stop1));

	HANDLE_ERROR(cudaEventElapsedTime(&elapsedTime, start1, stop1));
	printf("Computing by one GPU finished in %3.1f <ms>\n", elapsedTime);

	HANDLE_ERROR(cudaMemcpy(partial_c, dev_partial_c, blockPerGrid*sizeof(float), cudaMemcpyDeviceToHost));

	float res = 0;
	for (int i=0; i<blockPerGrid; i++)
	{
		res += partial_c[i];
	}

	printf("value calculated: %f\n", res);

	HANDLE_ERROR(cudaEventDestroy(start1));
	HANDLE_ERROR(cudaEventDestroy(stop1));

	HANDLE_ERROR(cudaFree(dev_a));
	HANDLE_ERROR(cudaFree(dev_b));
	HANDLE_ERROR(cudaFree(dev_partial_c));

	free(host_a);
	free(host_b);
	free(partial_c);

	return 0;
}

核函式:kernel.cu
#define imin(a,b) (a<b?a:b)
extern const int N = 33 * 1024 * 1024;
extern const int threadsPerBlock = 256;
extern const int blockPerGrid = imin(32, (N+threadsPerBlock-1)/threadsPerBlock);

__global__ void dotProduct(float *a, float *b, float *c, int N)
{
	__shared__ float cache[threadsPerBlock];
	int tid = blockDim.x * blockIdx.x + threadIdx.x;
	int cacheIdx = threadIdx.x;

	float temp = 0;
	while (tid < N)
	{
		temp += a[tid] * b[tid];
		tid += blockDim.x * gridDim.x;
	}
	cache[cacheIdx] = temp;

	__syncthreads();

	int i = blockDim.x /2;
	while (i != 0)
	{
		if (cacheIdx < i)
		{
			cache[cacheIdx] += cache[cacheIdx+i];
		}
		__syncthreads();
		i /= 2;
	}

	if (cacheIdx == 0)
	{
		c[blockIdx.x] = cache[0];
	}

}

extern "C" void runDotProduct(float *dev_a, float *dev_b, float *dev_partial_c, int size)
{
	dotProduct<<<blockPerGrid, threadsPerBlock>>>(dev_a, dev_b, dev_partial_c, size);
}

本文試圖將同樣的資料在單個GPU上計算,比較計算時間來突出多GPU在計算效能上的提升。但實際情況是多GPU的計算時間卻比單GPU更長。初步考慮是覺得核函式太簡單,使得GPU執行的效能提升不足以彌補裝置分配以及執行緒排程等帶來的開銷。所以多GPU也許更適合在大量複雜計算的場景下使用~

相關推薦

[每天CUDA_C]GPU的使用

單個GPU具有強大的平行計算的能力,當把多個GPU同時用來執行同一個任務的時候,計算的效能將會得到更大的提升。本文在兩塊GPU上實現大資料量的向量點積運算。主要步驟為: 1.      獲得裝置數量; 2.      任務分配; 3.      為每個任務建立一個執行緒;

每天CUDA_C]GPU的使用

單個GPU具有強大的平行計算的能力,當把多個GPU同時用來執行同一個任務的時候,計算的效能將會得到更大的提升。本文在兩塊GPU上實現大資料量的向量點積運算。主要步驟為:1. 獲得裝置數量;2. 任務分配;3. 為每個任

[每天CUDA_C]GPU實現水波動畫效果

本文利用GPU強大的計算能力生成不同時刻的水波圖片,並使用OpenGL繪製,實現水波動畫效果。其中幾個時刻的截圖如下: 生成一幀動畫的程式碼為: void generateFrame(DataBlock *d, int ticks) { dim3 blocks(DIM

[每天CUDA_C] 利用頁鎖定記憶體提高運算效率

本文通過使用malloc分配記憶體和cudaHostAlloc分配頁鎖定記憶體,說明使用頁鎖定記憶體可提高運算效率,並指出哪些場合適合使用頁鎖定記憶體。 malloc分配的是標準的可分頁的(pagable)的主機記憶體,作業系統在對記憶體進行排程的時候可能會將這種記憶體分頁

學Python之Python版本共存安裝

Python的安裝 進入Python官方網站:www.python.org下載系統對應的Python版本 按照提示步奏安裝,安裝路徑選擇自定義,方便查詢 安裝完成後,按win+R鍵,輸入cmd進入cmd程式,輸入Python,如果能夠進入互動環境,則表示安裝完成 Python多版本共存

先飛之JAVA_執行緒

多執行緒的概述 執行緒是程式執行的一條路徑, 一個程序中可以包含多條執行緒,多執行緒併發執行可以提高程式的效率, 可以同時完成多項工作。 併發和並行的區別,並行就是兩個任務同時執行,就是甲任務進行的同時,乙任務也在進行。(需要多核CPU);併發是指兩個任務都

攻略——C語言檔案程式設計初探(一)

      發現有很多童鞋學了一年半載的C語言還不會多檔案程式設計。很多人到現在一個程式都只有一個原始檔(main.cpp或mian.c)。甚至連我的室友大飛哥(我們都大二下學期了)昨天也問我怎麼做。

Python晉級12----線程

span not 可選 正在 元素 等待 run gin cti Python 多線程 多線程類似於同一時候執行多個不同程序,多線程執行有例如以下長處: 使用線程能夠把占領長時間的程序中的任務放到後臺去處理。用戶界面能夠更加吸引人。這樣比方用戶點擊了一個butto

圖慎入)圖解到大拿實現財務自由走向人生巔峰之路

情況下 項目管理 備註 互聯 職業 mark jpg min 產品經理 整體發展線路 生命就是一場永不停止的修煉過程 也是一場折騰的過程 生命不息 折騰不止 愛折騰是技術人員必備的“功夫”,沒有之一 職業發展線路圖 職業發展對運維人員的要求特別嚴苛,因為運維人員針對不同的問

牛客網暑期ACM校訓練營(第二場)補題QAQ

warn 分享圖片 ini lin int 技術分享 ace main bre   G transform   題目大意: 數軸上有n個集裝箱,第i個集裝箱位於坐標x[i],有a[i]件貨物。現在要把集裝箱進行一些移動,求在所有貨物移動總距離不超過T的情況下,最多能把多少

程式設計師想趁頭髮還在找個女友!原因:陪機器人比陪女生時間

近日,一位程式設計師相親圖引起了網友們的圍觀。這位"天然呆"的程式設計師表示自己現在年紀不小了,想趁頭髮還沒掉光時找個女朋友。 而關於擇偶標準,他表示是女孩就行,不是產品經理的話條件還能再放寬。這位天然呆的程式設計師小哥哥是負責快遞倉的倉儲自動化方面的,小哥哥表示,工作就是通過系統排程完成

如何簡單高效地部署和監控分散式爬蟲專案?教你!

初級使用者: 只有一臺開發主機 能夠通過 Scrapyd-client 打包和部署 Scrapy 爬蟲專案,以及通過 Scrapyd JSON API 來控制爬蟲,感覺 命令列操作太麻煩 ,希望能夠通過瀏覽器直接部署和執行專案 專業使用者: 有

面試被當成,程式設計師:當場摘帽子,面試官:明天上班!

很多求職者,都有過面試的經歷,這個過程很讓人煎熬,因為面試前需要做很多準備,比如修改簡歷、準備面試內容,甚至還要思考面試時要怎麼穿著才得體。 雖然說光看外表並不能客觀的反映一個人的真實能力,但是面試官也會通過求職者的外形和裝扮來判斷他們的經驗和閱歷。 學習web前端找工作這裡推

Java執行緒入門&執行緒安全

什麼是程序 電腦中時會有很多單獨執行的程式,每個程式有一個獨立的程序,而程序之間是相互獨立存在的。比如下圖中的QQ、酷狗播放器、電腦管家等等。 什麼是執行緒 程序想要執行任務就需要依賴執行緒。換句話說,就是程序中的最小執行單位就是執行緒,並且一個程序中至少有一個

學python第二十四天(面向物件三大特性之型)

面向物件三大特性之多型 什麼是多型 不同物件響應同一種方法的表現出不同的行為,產生不同的結果 用基類建立一套統一的規則,強制子類去遵循(使用抽象類實現),這樣便可以 在不用考慮物件具體型別的前提下而直接使用物件下的方法 為什麼要有多型 增加了程式的靈活性 以不變應萬變,不論

Java程式設計師從笨之(三)面向物件之封裝,繼承,型(下)

五:再談繼承   繼承是一種聯結類的層次模型,並且允許和鼓勵類的重用,它提供了一種明確表述共性的方法。物件的一個新類可以從現有的類中派生,這個過程稱為類繼承。新類繼承了原始類的特性,新類稱為原始類的派生類(子類),而原始類稱為新類的基類(父類)。派生類可以從它的基類那裡繼承方法和例項變數,並且類可以修改或增加

Java程式設計師從笨之(五十四)細談Hibernate(五)Hibernate一對關係對映

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!                       前幾篇系列部落格:           在前幾篇部落格,我們初步對Hibernate有了一定的基礎性的認知了,也能夠簡單的用hibernate進行

# Mybatis(四)表間關係分析,高階對映(一對一,一對) 日記--day05(下_02)

Mybatis(四)表間關係分析,高階對映(一對一,一對多,多對多) 菜鳥日記–day05(下_02) 花了很多時間,去看sql複雜查詢,更新慢了 電腦斷電寫的太急,綠色補更 一、表間關係分析 1.分析資料庫表的方法 思路: 需要分模組的對多張表進行邏輯分析 表記錄

們注意了,看看大神們推薦的運維資料和書籍

對於有志成為運維工程師的在校學生,有哪些好的運維方面的好書或者資料推薦? 韓海剛,運維 苦練基本功 1. 熟悉windows、linux作業系統;作業系統原理是精髓。 2. sed、awk、grep等指令碼用法熟悉;正則表示式熟悉。 3. 常見系統在windows,linux下部署要熟悉。比如dns

的C#學習之旅——型的實現途徑

目錄 一、定義 二、虛方法: 三、抽象類與抽象方法: 四、介面實現: 五、總結: 一、定義 多型:在面嚮物件語言中,介面的多種不同實現方式即為多型 多型性就是指在程式執行時,執行的雖然是一個呼叫方法的語句,卻可以根據派生類物件的型別的不同完成方法不同的具體實現