CUDA零復(fù)制(Zero Copy)(零拷貝內(nèi)存)

零復(fù)制(Zero Copy)(零拷貝內(nèi)存)

零復(fù)制是一種特殊形式的內(nèi)存映射,它允許你將主機內(nèi)存直接映射到GPU內(nèi)存空間上回怜。因此距辆,當你對GPU上的內(nèi)存解引用時,如果它是基于GPU的蔽氨,那么你就獲得了全局內(nèi)存的高速帶寬(180GB/s)藐唠。如果GPU代碼讀取一個主機映射變量,它會提交一個PCI-E讀取事務(wù)鹉究,很長時間之后宇立,主機會通過PCI-E總線返回數(shù)據(jù)。
如果程序是計算密集型自赔,那么零復(fù)制可能是一項非常有用的技術(shù)妈嘹。它節(jié)省了設(shè)備顯示傳輸?shù)臅r間。事實上绍妨,是將計算和數(shù)據(jù)傳輸操作重疊了润脸,而且無需執(zhí)行顯式的內(nèi)存管理。
實際上他去,使用零復(fù)制內(nèi)存津函,將傳輸和內(nèi)核操作分成更小的塊,然后以流水線的方式執(zhí)行它們孤页。
然而尔苦,采用零復(fù)制內(nèi)存的傳輸實際上是相當小的。PCI-E總線在兩個方向上的帶寬的總是相同的行施。由于基于PCI-E的內(nèi)存讀取存在高延遲允坚,因此實際上大多數(shù)讀操作應(yīng)該在所有寫操作之前被放入讀隊列。我們可能獲得比顯式內(nèi)存復(fù)制版本節(jié)省大量執(zhí)行時間的效果蛾号。

在論壇上的問答:
Q:我在程序中使用了零拷貝內(nèi)存稠项,程序反而變得更慢了,看書上說
“當輸入內(nèi)存和輸出內(nèi)存都只能使用一次時鲜结,那么在獨立GPU上使用零拷貝內(nèi)存將帶來性能提升”展运。
請問 “當輸入內(nèi)存和輸出內(nèi)存都只能使用一次時” 具體是什么意思活逆?我下面的做法有什么問題?
程序情況是這樣的:
在主機申請了3個 零拷貝內(nèi)存 buf1,buf2,buf3拗胜,然后通過cudaHostGetDevicePointer() 獲得這塊內(nèi)存在GPU上的有效指針dev1,dev2,dev3蔗候。其中 buf1,buf2,buf3 是從影像中讀取的數(shù)據(jù),然后在核函數(shù)中對dev1,dev2,dev3進行處理埂软,值依然存放在dev1,dev2,dev3中锈遥,然后調(diào)用GDAL將buf1,buf2,buf3 寫出。
A:
LZ您好:
zero copy和普通的cudaMemcpy一樣也是要走pci-e總線的勘畔,只不過cudaMemcpy是一次性全部copy過去所灸,而zero copy是用的時候自動在后臺通過pci-e總線傳輸。
zero copy這樣的機制多少可以利用計算來掩蓋一些copy的時間炫七,而如果使用cudaMemcpy要實現(xiàn)類似的計算和傳輸互相掩蓋的話爬立,需要使用異步版本的cudaMemcpy函數(shù),并使用頁鎖定內(nèi)存以及多個stream万哪。
zero copy的讀入信息是不在device端緩沖的懦尝,也就是說device端使用幾次就需要從host端走較慢的pci-e 總線讀入幾次。所以壤圃,一般建議只使用一次的數(shù)據(jù)以及少量的返回數(shù)據(jù)可以使用zero copy陵霉,其他情況建議copy到顯存使用,顯存DRAM的帶寬要比pci-e的帶寬高出一個量級伍绳。
以上是對zero copy的簡要介紹踊挠。

使用零復(fù)制的三步

1、啟用零復(fù)制

需要在任何CUDA上下文創(chuàng)建之前進行下面的調(diào)用:

//Enable host mapping to device
 memoryCUDA_CALL(cudaSetDeviceFlags(cudaDeviceMapHost));

當CUDA上下文被創(chuàng)建時冲杀,驅(qū)動程序會知道它需要支持主機內(nèi)存映射效床,沒有驅(qū)動程序的支持,零復(fù)制將無法工作权谁。如果該支持在CUDA上下文創(chuàng)建之后完成剩檀,內(nèi)存也無法工作。請注意對cudaHostAlloc
這樣的函數(shù)調(diào)用旺芽,盡管在主機內(nèi)存上執(zhí)行沪猴,也仍然創(chuàng)建一個GPU上下文。
雖然大多數(shù)設(shè)備支持零復(fù)制內(nèi)存采章,但是一些早期的設(shè)備卻不支持运嗜。顯式檢查:

struct cudaDeviceProp device_prop
cudaGetDeviceProperties(&device_prop,device_num);
zero_copy_supported=device_prop.canMapHostMemory;

2、分配主機內(nèi)存

分配了主機內(nèi)存悯舟,這樣它就可以被映射到設(shè)備內(nèi)存担租。我們對cudaHostAlloc
函數(shù)使用額外的標志cudaHostAllocMapped
就可以實現(xiàn)。

//Allocate zero copy pinned 
cudaHostAlloc((void**)&host_data_to_device,size_in_bytes,cudaHostAllocWriteCombined|cudaHostAllocMapped);

3抵怎、將常規(guī)的主機指針轉(zhuǎn)換成指向設(shè)備內(nèi)存空間的指針

通過cudaHostGetDevicePointer
函數(shù):

//Conver to a GPU host 
cudaHostGetDevicePointer(&dev_host_data_to_device,host_data_to_device,0);

在這個調(diào)用中奋救,我們將之前在主機內(nèi)存空間分配的host_data_to_device
轉(zhuǎn)換成GPU內(nèi)存空間的指針岭参。在GPU內(nèi)核中,只使用轉(zhuǎn)換后的指針尝艘,原始的指針只出現(xiàn)在主機執(zhí)行的代碼中演侯。因此,為了之后釋放內(nèi)存利耍,需要在主機上執(zhí)行一個操作,其他的調(diào)用保持不變:

//Free pinned 
cudaFreeHost(host_data_to_device);

代碼參考

簡單的代碼盔粹,進行兩個數(shù)組之間數(shù)據(jù)的拷貝隘梨,這里只是為了說明零復(fù)制的使用,并無實際意義舷嗡。

#include <numeric>
#include <stdio.h>
#include <stdlib.h>
void checkCUDAError(const char *msg) {
    cudaError_t err = cudaGetLastError();
    if (cudaSuccess != err) {
        fprintf(stderr, "Cuda error: %s: %s.\n", msg, cudaGetErrorString(err));
        exit(EXIT_FAILURE);
    }
}

__global__ void sumNum( int *data) {

    int i = threadIdx.x + blockIdx.x * blockDim.x;
    if(i<1000000000){
     data[i]=10;
    }
}
int main(void) {
    size_t size = 1*1000000000 * sizeof(int);//4G
    //1.啟用零復(fù)制
    cudaSetDeviceFlags (cudaDeviceMapHost);
    int* data;
    //2.分配主機內(nèi)存
    cudaHostAlloc((void**) &data, size,
            cudaHostAllocWriteCombined | cudaHostAllocMapped);
     checkCUDAError("cudaHostAlloc data");
    
     memset(data, 0, 1*1000000000 * sizeof(int));
    int *gpudata;
    //3.將常規(guī)的主機指針轉(zhuǎn)換成指向設(shè)備內(nèi)存空間的指針
    cudaHostGetDevicePointer(&gpudata, data, 0);
    checkCUDAError("cudaHostGetDevicePointer");
    //sumNum<<<1000000000/1024+1023, 1024>>>(gpudata);
    //注意V崃浴!因為下面要打印出來測試进萄,所以要先同步數(shù)據(jù)捻脖,這個函數(shù)可以保證cpu等待gpu的kernel函數(shù)結(jié)束才往下運行。如果數(shù)據(jù)暫時用不到中鼠,可以在整體結(jié)束以后再加這句話可婶。明顯等待kernel函數(shù)結(jié)束會占用程序進行的時間。
    cudaDeviceSynchronize();
    for (int i = 99999999; i < 1000000000; i=i+100000000) {
        printf("%d \n", data[i]);
    }
    //記得零拷貝的free是這個函數(shù)
    cudaFreeHost(data);
    return 0;
}

參考:https://www.findhao.net/easycoding/1448

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末援雇,一起剝皮案震驚了整個濱河市矛渴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惫搏,老刑警劉巖具温,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異筐赔,居然都是意外死亡铣猩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門茴丰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來达皿,“玉大人,你說我怎么就攤上這事贿肩×廴疲” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵尸曼,是天一觀的道長们何。 經(jīng)常有香客問我,道長控轿,這世上最難降的妖魔是什么冤竹? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任拂封,我火速辦了婚禮,結(jié)果婚禮上鹦蠕,老公的妹妹穿的比我還像新娘冒签。我一直安慰自己,他們只是感情好钟病,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布萧恕。 她就那樣靜靜地躺著,像睡著了一般肠阱。 火紅的嫁衣襯著肌膚如雪票唆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天屹徘,我揣著相機與錄音走趋,去河邊找鬼。 笑死噪伊,一個胖子當著我的面吹牛簿煌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鉴吹,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼姨伟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了豆励?” 一聲冷哼從身側(cè)響起授滓,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肆糕,沒想到半個月后般堆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡诚啃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年淮摔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片始赎。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡和橙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出造垛,到底是詐尸還是另有隱情魔招,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布五辽,位于F島的核電站办斑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乡翅,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一鳞疲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蠕蚜,春花似錦尚洽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至挣柬,卻和暖如春潮酒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凛忿。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工澈灼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留竞川,地道東北人店溢。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像委乌,于是被迫代替她去往敵國和親床牧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容