0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

NIVDIA的reduce優(yōu)化筆記

jf_pmFSk4VX ? 來源:GiantPandaCV ? 2023-01-12 15:05 ? 次閱讀

問題介紹

通俗的來說,Reduce就是要對一個數(shù)組求 sum,min,max,avg 等等。Reduce又被叫作規(guī)約,意思就是遞歸約減,最后獲得的輸出相比于輸入一般維度上會遞減。

比如 nvidia 博客上這個 Reduce Sum 問題,一個長度為 8 的數(shù)組求和之后得到的輸出只有一個數(shù),從 1 維數(shù)組變成一個標(biāo)量。本文就以 Reduce Sum 為例來記錄 Reduce 優(yōu)化。

5bb710aa-8be4-11ed-bfe3-dac502259ad0.png

硬件環(huán)境

NVIDIA A100-PCIE-40GB , 峰值帶寬在 1555 GB/s , CUDA版本為11.8.

構(gòu)建BaseLine

在問題介紹一節(jié)中的 Reduce 求和圖實際上就指出了 BaseLine 的執(zhí)行方式,我們將以樹形圖的方式去執(zhí)行數(shù)據(jù)累加,最終得到總和。但由于GPU沒有針對 global memory 的同步操作,所以博客指出我們可以通過將計算分成多個階段的方式來避免 global memrory 的操作。如下圖所示:

5bd8e1da-8be4-11ed-bfe3-dac502259ad0.png

接著 NVIDIA 博客給出了 BaseLine 算法的實現(xiàn):

5bf0645e-8be4-11ed-bfe3-dac502259ad0.png在這里插入圖片描述

這里的 g_idata 表示的是輸入數(shù)據(jù)的指針,而 g_odata 則表示輸出數(shù)據(jù)的指針。然后首先把 global memory 數(shù)據(jù) load 到 shared memory 中,接著在 shared memory 中對數(shù)據(jù)進(jìn)行 Reduce Sum 操作,最后將 Reduce Sum 的結(jié)果寫會 global memory 中。

但接下來的這頁 PPT 指出了 Baseine 實現(xiàn)的低效之處:

5c0a9298-8be4-11ed-bfe3-dac502259ad0.png

這里指出了2個問題,一個是warp divergent,另一個是取模這個操作很昂貴。這里的warp divergent 指的是對于啟動 BaseLine Kernel 的一個 block 的 warp 來說,它所有的 thread 執(zhí)行的指令都是一樣的,而 BaseLine Kernel 里面存在 if 分支語句,一個 warp 的32個 thread 都會執(zhí)行存在的所有分支,但只會保留滿足條件的分支產(chǎn)生的結(jié)果。

5c28531e-8be4-11ed-bfe3-dac502259ad0.png

我們可以在第8頁PPT里面看到,對于每一次迭代都會有兩個分支,分別是有豎直的黑色箭頭指向的小方塊(有效計算的線程)以及其它沒有箭頭指向的方塊,所以每一輪迭代實際上都有大量線程是空閑的,無法最大程度的利用GPU硬件。

從這個PPT我們可以計算出,對于一個 Block 來說要完成Reduce Sum,一共有8次迭代,并且每次迭代都會產(chǎn)生warp divergent。

接下來我們先把 BaseLine 的代碼抄一下,然后我們設(shè)定好一個 GridSize 和 BlockSize 啟動 Kernel 測試下性能。在PPT的代碼基礎(chǔ)上,我么補充一下內(nèi)存申請以及啟動 Kernel 的代碼。

#include
#include
#include

#defineN32*1024*1024
#defineBLOCK_SIZE256

__global__voidreduce_v0(float*g_idata,float*g_odata){
__shared__floatsdata[BLOCK_SIZE];

//eachthreadloadsoneelementfromglobaltosharedmem
unsignedinttid=threadIdx.x;
unsignedinti=blockIdx.x*blockDim.x+threadIdx.x;
sdata[tid]=g_idata[i];
__syncthreads();

//doreductioninsharedmem
for(unsignedints=1;s>>(input_device,output_device);
cudaMemcpy(output_device,output_host,block_num*sizeof(float),cudaMemcpyDeviceToHost);
return0;
}

我們這里設(shè)定輸入數(shù)據(jù)的長度是 32*1024*1024 個float數(shù) (也就是PPT中的4M數(shù)據(jù)),然后每個 block 的線程數(shù)我們設(shè)定為 256 (BLOCK_SIZE = 256, 也就是一個 Block 有 8 個 warp)并且每個 Block 要計算的元素個數(shù)也是 256 個,然后當(dāng)一個 Block 的計算完成后對應(yīng)一個輸出元素。

所以對于輸出數(shù)據(jù)來說,它的長度是輸入數(shù)據(jù)長度處以 256 。我們代入 kernel 加載需要的 GridSize(N / 256) 和 BlockSize(256) 再來理解一下 BaseLine 的 Reduce kernel。

首先,第 tid 號線程會把 global memroy 的第 i 號數(shù)據(jù)取出來,然后塞到 shared memroy 中。接下來針對已經(jīng)存儲到 shared memroy 中的 256 個元素展開多輪迭代,迭代的過程如 PPT 的第8頁所示。完成迭代過程之后,這個 block 負(fù)責(zé)的256個元素的和都放到了 shared memrory 的0號位置,我們只需要將這個元素寫回global memory就做完了。

接下來我們使用 nvcc -o bin/reduce_v0 reduce_v0_baseline.cu 編譯一下這個源文件,并且使用nsight compute去profile一下。

性能和帶寬的測試情況如下:

優(yōu)化手段 耗時(us) 帶寬利用率 加速比
reduce_baseline 990.66us 39.57% ~

優(yōu)化手段1: 交錯尋址(Interleaved Addressing)

接下來直接NVIDIA的PPT給出了優(yōu)化手段1:

5c3fda0c-8be4-11ed-bfe3-dac502259ad0.png

這里是直接針對 BaseLine 中的 warp divergent 問題進(jìn)行優(yōu)化,通過調(diào)整BaseLine中的分支判斷代碼使得更多的線程可以走到同一個分支里面,降低迭代過程中的線程資源浪費。具體做法就是把 if (tid % (2*s) == 0) 替換成 strided index的方式也就是int index = 2 * s * tid,然后判斷 index 是否在當(dāng)前的 block 內(nèi)。

雖然這份優(yōu)化后的代碼沒有完全消除if語句,但是我們可以來計算一下這個版本的代碼在8次迭代中產(chǎn)生 warp divergent 的次數(shù)。對于第一次迭代,0-3號warp的index都是滿足=blockDim.x的,也就是說這次迭代根本不會出現(xiàn)warp divergent的問題,因為每個warp的32個線程執(zhí)行的都是相同的分支。

接下來對于第二代迭代,0,1兩個warp是滿足=blockDim.x,依然不會出現(xiàn)warp divergent,以此類推直到第4次迭代時0號warp的前16個線程和后16線程會進(jìn)入不同的分支,會產(chǎn)生一次warp divergent,接下來的迭代都分別會產(chǎn)生一次warp divergent。

但從整體上看,這個版本的代碼相比于BaseLine的代碼產(chǎn)生的warp divergent次數(shù)會少得多。

我們繼續(xù)抄一下這個代碼然后進(jìn)行profile一下。

#defineN32*1024*1024
#defineBLOCK_SIZE256

__global__voidreduce_v1(float*g_idata,float*g_odata){
__shared__floatsdata[BLOCK_SIZE];

//eachthreadloadsoneelementfromglobaltosharedmem
unsignedinttid=threadIdx.x;
unsignedinti=blockIdx.x*blockDim.x+threadIdx.x;
sdata[tid]=g_idata[i];
__syncthreads();

//doreductioninsharedmem
for(unsignedints=1;s

性能和帶寬的測試情況如下:

優(yōu)化手段 耗時(us) 帶寬利用率 加速比
reduce_baseline 990.66us 39.57% ~
reduce_v1_interleaved_addressing 479.58us 81.74% 2.06

可以看到這個優(yōu)化還是很有效的,相比于BaseLine的性能有2倍的提升。

優(yōu)化手段2: 解決Bank Conflict

對于 reduce_v1_interleaved_addressing 來說,它最大的問題時產(chǎn)生了 Bank Conflict。使用 shared memory 有以下幾個好處:

更低的延遲(20-40倍)

更高的帶寬(約為15倍)

更細(xì)的訪問粒度,shared memory是4byte,global memory是32byte

但是 shared memrory 的使用量存在一定上限,而使用 shared memory 要特別小心 bank conflict 。實際上,shared memory 是由 32 個 bank 組成的,如下面這張 PPT 所示:

5c606a06-8be4-11ed-bfe3-dac502259ad0.png

而 bank conflict 指的就是在一個 warp 內(nèi),有2個或者以上的線程訪問了同一個 bank 上不同地址的內(nèi)存。比如:

5c7ce870-8be4-11ed-bfe3-dac502259ad0.png

在 reduce_v1_interleaved_addressing 的 Kernel 中,我們以0號warp為例。在第一次迭代中,0號線程需要去加載 shared memory 的0號和1號地址,然后寫回0號地址。此時,0號 warp 的16號線程需要加載 shared memory 的32和33號地址并且寫回32號地址。

所以,我們在一個warp內(nèi)同時訪問了一個bank的不同內(nèi)存地址,發(fā)生了2路的 Bank Conflict,如上圖所示。類似地,在第二次迭代過程中,0號warp的0號線程會加載0號和2號地址并寫回0號地址,然后0號warp的8號線程需要加載 shared memory 的32號和34號地址(2*2*8=32, 32+2=34)并寫回32號線程,16號線程會加載64號和68號地址,24號線程會加載96號和100號地址。

然后0,32,64,96號地址都在一個bank中,所以這里產(chǎn)生了4路的 Bank Conflict 。以此類推,下一次迭代會產(chǎn)生8路的 Bank Conflict,使得整個 Kernel 一直受到 Bank Conflict 的影響。

接下來PPT為我們指出了避免Bank Conflict的方案,那就是把循環(huán)迭代的順序修改一下:

5c993b74-8be4-11ed-bfe3-dac502259ad0.png

為啥這樣就可以避免Bank Conflict呢?我們繼續(xù)分析一下0號wap的線程,首先在第一輪迭代中,0號線程現(xiàn)在需要加載0號以及128號地址,并且寫回0號地址。而1號線程需要加載1號和129號地址并寫回1號地址。2號線程需要加載2號和130號地址并寫回2號地址。

我們可以發(fā)現(xiàn)第0個warp的線程在第一輪迭代中剛好加載shared memory的一行數(shù)據(jù),不會產(chǎn)生 bank conflict。接下來對于第2次迭代,0號warp仍然也是剛好加載shared memory的一行數(shù)據(jù),不會產(chǎn)生 bank conflict 。對于第三次迭代,也是這樣。

而對于第4次迭代,0號線程load shared memory 0號和16號地址,而這個時候16號線程什么都不干被跳過了,因為s=16,16-31號線程不滿足if的條件。整體過程如PPT的14頁:

5cb2c22e-8be4-11ed-bfe3-dac502259ad0.png

接下來我們修改下代碼再profile一下:

#defineN32*1024*1024
#defineBLOCK_SIZE256

__global__voidreduce_v2(float*g_idata,float*g_odata){
__shared__floatsdata[BLOCK_SIZE];

//eachthreadloadsoneelementfromglobaltosharedmem
unsignedinttid=threadIdx.x;
unsignedinti=blockIdx.x*blockDim.x+threadIdx.x;
sdata[tid]=g_idata[i];
__syncthreads();

//doreductioninsharedmem
for(unsignedints=blockDim.x/2;s>0;s>>=1){
if(tid

性能和帶寬的測試情況如下:

優(yōu)化手段 耗時(us) 帶寬利用率 加速比
reduce_baseline 990.66us 39.57% ~
reduce_v1_interleaved_addressing 479.58us 81.74% 2.06
reduce_v2_bank_conflict_free 462.02us 84.81% 2.144

可以看到相比于優(yōu)化版本1性能和帶寬又提升了一些。

優(yōu)化手段3: 解決 Idle 線程

接下來PPT 17指出,reduce_v2_bank_conflict_free 的 kernel 浪費了大量的線程。對于第一輪迭代只有128個線程在工作,而第二輪迭代只有64個線程工作,第三輪迭代只有32和線程工作,以此類推,在每一輪迭代中都有大量的線程是空閑的。

5cc2217e-8be4-11ed-bfe3-dac502259ad0.png

那么可以如何避免這種情況呢?PPT 18給了一個解決方法:

5cd37f3c-8be4-11ed-bfe3-dac502259ad0.png

這里的意思就是我們讓每一輪迭代的空閑的線程也強行做一點工作,除了從global memory中取數(shù)之外再額外做一次加法。但需要注意的是,為了實現(xiàn)這個我們需要把block的數(shù)量調(diào)成之前的一半,因為這個Kernel現(xiàn)在每次需要管512個元素了。我們繼續(xù)組織下代碼并profile一下:

#defineN32*1024*1024
#defineBLOCK_SIZE256

__global__voidreduce_v3(float*g_idata,float*g_odata){
__shared__floatsdata[BLOCK_SIZE];

//eachthreadloadsoneelementfromglobaltosharedmem
unsignedinttid=threadIdx.x;
unsignedinti=blockIdx.x*(blockDim.x*2)+threadIdx.x;
sdata[tid]=g_idata[i]+g_idata[i+blockDim.x];
__syncthreads();

//doreductioninsharedmem
for(unsignedints=blockDim.x/2;s>0;s>>=1){
if(tid

性能和帶寬的測試情況如下:

優(yōu)化手段 耗時(us) 帶寬利用率 加速比
reduce_baseline 990.66us 39.57% ~
reduce_v1_interleaved_addressing 479.58us 81.74% 2.06
reduce_v2_bank_conflict_free 462.02us 84.81% 2.144
reduce_v3_idle_threads_free 244.16us 83.16% 4.057

優(yōu)化手段4: 展開最后一個warp

首先來看 PPT 的第20頁:

5cea4636-8be4-11ed-bfe3-dac502259ad0.png

這里的意思是,對于 reduce_v3_idle_threads_free 這個 kernel 來說,它的帶寬相比于理論帶寬還差得比較遠(yuǎn),因為 Reduce 操作并不是算術(shù)密集型的算子。

因此,一個可能的瓶頸是指令的開銷。這里說的指令不是加載,存儲或者給計算核心用的輔算術(shù)指令。換句話說,這里的指令就是指地址算術(shù)指令和循環(huán)的開銷。

接下來 PPT 21指出了減少指令開銷的優(yōu)化方法:

5d14e2a6-8be4-11ed-bfe3-dac502259ad0.png

這里的意思是當(dāng)reduce_v3_idle_threads_free kernel里面的s<=32時,此時的block中只有一個warp0在干活時,但線程還在進(jìn)行同步操作。這一條語句造成了極大的指令浪費。

由于一個warp的32個線程都是在同一個simd單元上,天然保持了同步的狀態(tài),所以當(dāng)s<=32時,也即只有一個warp在工作時,完全可以把__syncthreads()這條同步語句去掉,使用手動展開的方式來代替。具體做法就是:

5d34a7a8-8be4-11ed-bfe3-dac502259ad0.png

注意

這里的warpReduce函數(shù)的參數(shù)使用了一個volatile修飾符號,volatile的中文意思是“易變的,不穩(wěn)定的”,對于用volatile修飾的變量,編譯器對訪問該變量的代碼不再優(yōu)化,總是從它所在的內(nèi)存讀取數(shù)據(jù)。

對于這個例子,如果不使用volatile,對于一個線程來說(假設(shè)線程ID就是tid),它的s_data[tid]可能會被緩存在寄存器里面,且在某個時刻寄存器和shared memory里面s_data[tid]的數(shù)值還是不同的。

當(dāng)另外一個線程讀取s_data[tid]做加法的時候,也許直接就從shared memory里面讀取了舊的數(shù)值,從而導(dǎo)致了錯誤的結(jié)果。

__device__voidwarpReduce(volatilefloat*cache,unsignedinttid){
cache[tid]+=cache[tid+32];
cache[tid]+=cache[tid+16];
cache[tid]+=cache[tid+8];
cache[tid]+=cache[tid+4];
cache[tid]+=cache[tid+2];
cache[tid]+=cache[tid+1];
}

__global__voidreduce_v4(float*g_idata,float*g_odata){
__shared__floatsdata[BLOCK_SIZE];

//eachthreadloadsoneelementfromglobaltosharedmem
unsignedinttid=threadIdx.x;
unsignedinti=blockIdx.x*(blockDim.x*2)+threadIdx.x;
sdata[tid]=g_idata[i]+g_idata[i+blockDim.x];
__syncthreads();

//doreductioninsharedmem
for(unsignedints=blockDim.x/2;s>32;s>>=1){
if(tid

性能和帶寬的測試情況如下:

優(yōu)化手段 耗時(us) 帶寬利用率 加速比
reduce_baseline 990.66us 39.57% ~
reduce_v1_interleaved_addressing 479.58us 81.74% 2.06
reduce_v2_bank_conflict_free 462.02us 84.81% 2.144
reduce_v3_idle_threads_free 244.16us 83.16% 4.057
reduce_v4_unroll_last_warp 167.10us 54.10% 5.928

這個地方我目前是有疑問的,nvidia的ppt指出這個kernel會繼續(xù)提升性能和帶寬,但是在我實測的時候發(fā)現(xiàn)性能確實繼續(xù)提升了,但是帶寬的利用率卻下降了,目前想不清楚這個原因是什么?這里唯一的區(qū)別就是我使用的GPU是 A100-PCIE-40GB,而nvidia gpu上使用的gpu是 G80 GPU 。歡迎大佬評論區(qū)指點。

5d53518a-8be4-11ed-bfe3-dac502259ad0.png

優(yōu)化手段5: 完全展開循環(huán)

在 reduce_v4_unroll_last_warp kernel 的基礎(chǔ)上就很難再繼續(xù)優(yōu)化了,但為了極致的性能NVIDIA的PPT上給出了對for循環(huán)進(jìn)行完全展開的方案。

5d691a56-8be4-11ed-bfe3-dac502259ad0.png

這種方案的實現(xiàn)如下:

5d846982-8be4-11ed-bfe3-dac502259ad0.png

kernel的代碼實現(xiàn)如下:

template
__device__voidwarpReduce(volatilefloat*cache,inttid){
if(blockSize>=64)cache[tid]+=cache[tid+32];
if(blockSize>=32)cache[tid]+=cache[tid+16];
if(blockSize>=16)cache[tid]+=cache[tid+8];
if(blockSize>=8)cache[tid]+=cache[tid+4];
if(blockSize>=4)cache[tid]+=cache[tid+2];
if(blockSize>=2)cache[tid]+=cache[tid+1];
}

template
__global__voidreduce_v5(float*g_idata,float*g_odata){
__shared__floatsdata[BLOCK_SIZE];

//eachthreadloadsoneelementfromglobaltosharedmem
unsignedinttid=threadIdx.x;
unsignedinti=blockIdx.x*(blockDim.x*2)+threadIdx.x;
sdata[tid]=g_idata[i]+g_idata[i+blockDim.x];
__syncthreads();

//doreductioninsharedmem
if(blockSize>=512){
if(tid<256){
????????????sdata[tid]+=sdata[tid+256];
????????}
????????__syncthreads();
????}
????if(blockSize>=256){
if(tid<128){
????????????sdata[tid]+=sdata[tid+128];
????????}
????????__syncthreads();
????}
????if(blockSize>=128){
if(tid<64){
????????????sdata[tid]+=sdata[tid+64];
????????}
????????__syncthreads();
????}
????
????//?write?result?for?this?block?to?global?mem
????if(tid<32)warpReduce(sdata,tid);
if(tid==0)g_odata[blockIdx.x]=sdata[0];
}

性能和帶寬的測試情況如下:

優(yōu)化手段 耗時(us) 帶寬利用率 加速比
reduce_baseline 990.66us 39.57% ~
reduce_v1_interleaved_addressing 479.58us 81.74% 2.06
reduce_v2_bank_conflict_free 462.02us 84.81% 2.144
reduce_v3_idle_threads_free 244.16us 83.16% 4.057
reduce_v4_unroll_last_warp 167.10us 54.10% 5.928
reduce_v5_completely_unroll 158.78us 56.94% 6.239

優(yōu)化手段6: 調(diào)節(jié)BlockSize和GridSize

PPT的第31頁為我們展示了最后一個優(yōu)化技巧:

5df3efa0-8be4-11ed-bfe3-dac502259ad0.png

這里的意思就是我們還可以通過調(diào)整GridSize和BlockSize的方式獲得更好的性能收益,也就是說一個線程負(fù)責(zé)更多的元素計算。對應(yīng)到代碼的修改就是:

5e2163ae-8be4-11ed-bfe3-dac502259ad0.png

這里再貼一下kernel的代碼:

template
__device__voidwarpReduce(volatilefloat*cache,inttid){
if(blockSize>=64)cache[tid]+=cache[tid+32];
if(blockSize>=32)cache[tid]+=cache[tid+16];
if(blockSize>=16)cache[tid]+=cache[tid+8];
if(blockSize>=8)cache[tid]+=cache[tid+4];
if(blockSize>=4)cache[tid]+=cache[tid+2];
if(blockSize>=2)cache[tid]+=cache[tid+1];
}

template
__global__voidreduce_v6(float*g_idata,float*g_odata){
__shared__floatsdata[BLOCK_SIZE];

//eachthreadloadsoneelementfromglobaltosharedmem
unsignedinttid=threadIdx.x;
unsignedinti=blockIdx.x*(blockDim.x*NUM_PER_THREAD)+threadIdx.x;
sdata[tid]=0;
#pragmaunroll
for(intiter=0;iter=512){
if(tid<256){
????????????sdata[tid]+=sdata[tid+256];
????????}
????????__syncthreads();
????}
????if(blockSize>=256){
if(tid<128){
????????????sdata[tid]+=sdata[tid+128];
????????}
????????__syncthreads();
????}
????if(blockSize>=128){
if(tid<64){
????????????sdata[tid]+=sdata[tid+64];
????????}
????????__syncthreads();
????}
????
????//?write?result?for?this?block?to?global?mem
????if(tid<32)warpReduce(sdata,tid);
if(tid==0)g_odata[blockIdx.x]=sdata[0];
}

intmain(){
float*input_host=(float*)malloc(N*sizeof(float));
float*input_device;
cudaMalloc((void**)&input_device,N*sizeof(float));
for(inti=0;i<<>>(input_device,output_device);
cudaMemcpy(output_device,output_host,block_num*sizeof(float),cudaMemcpyDeviceToHost);
return0;
}

profile結(jié)果:

5e3ecc8c-8be4-11ed-bfe3-dac502259ad0.png

性能和帶寬的測試情況如下:

優(yōu)化手段 耗時(us) 帶寬利用率 加速比
reduce_baseline 990.66us 39.57% ~
reduce_v1_interleaved_addressing 479.58us 81.74% 2.06
reduce_v2_bank_conflict_free 462.02us 84.81% 2.144
reduce_v3_idle_threads_free 244.16us 83.16% 4.057
reduce_v4_unroll_last_warp 167.10us 54.10% 5.928
reduce_v5_completely_unroll 158.78us 56.94% 6.239
reduce_v6_multi_add 105.47us 85.75% 9.392

在把block_num從65536調(diào)整到1024之后,無論是性能還是帶寬都達(dá)到了最強,相比于最初的BaseLine加速了9.4倍。

總結(jié)

我這里的測試結(jié)果和nvidia ppt里提供的結(jié)果有一些出入,nvidia ppt的34頁展示的結(jié)果是對于每一種優(yōu)化相比于前一種無論是性能還是帶寬都是穩(wěn)步提升的。但我這里的測試結(jié)果不完全是這樣,對于 reduce_v4_unroll_last_warp 和 reduce_v5_completely_unroll 這兩個優(yōu)化,雖然耗時近一步減少但是帶寬卻降低了,我也還沒想清楚原因。

5e5077fc-8be4-11ed-bfe3-dac502259ad0.png

并且最終的Kernel帶寬利用率為 73 / 86.4 = 84.5% ,和我在A100上的reduce_v6_multi_add kernel的測試結(jié)果基本相當(dāng)。






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • gpu
    gpu
    +關(guān)注

    關(guān)注

    28

    文章

    4673

    瀏覽量

    128593
  • NIVDIA
    +關(guān)注

    關(guān)注

    0

    文章

    6

    瀏覽量

    7128

原文標(biāo)題:【BBuf的CUDA筆記】三,reduce優(yōu)化入門學(xué)習(xí)筆記

文章出處:【微信號:GiantPandaCV,微信公眾號:GiantPandaCV】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    PCB design for reduce EMI

    PCB design for reduce EMI
    發(fā)表于 08-20 15:55

    綠茶系統(tǒng) Ghost WinXP SP3 筆記優(yōu)化版 V2013.03

    綠茶系統(tǒng) Ghost WinXP SP3 筆記優(yōu)化版 V2013.03右鍵使用迅雷高速下載地址:thunder://QUFodHRwOi8vZDEzMDIueHk1OC5uZXQvMjAxMzAzL0xDWFRfR2hvc3RfV2luWFBfU1AzX1YyMDEzL
    發(fā)表于 03-27 09:05

    成功解決pyinstaller打包AttributeErrortype object pandas_TSObject has no attribute _reduce_cython_

    成功解決pyinstaller打包AttributeErrortype object pandas_TSObject has no attribute _reduce_cython_
    發(fā)表于 12-19 17:04

    成功解決AttributeError type object 'h5pyh5rReference' has no attribute '__reduce_cython__'

    成功解決AttributeError type object 'h5pyh5rReference' has no attribute '__reduce_cython__'
    發(fā)表于 12-20 10:32

    How to Reduce Reference Noise

    How to Reduce Reference Noise by Half Abstract: A low-noise, 2.5V reference is constructed
    發(fā)表于 01-23 22:39 ?1641次閱讀
    How to <b class='flag-5'>Reduce</b> Reference Noise

    Reduce Standby Power Drains wi

    Reduce Standby Power Drains with Ultra-Low-Current, Isolated, Pulse-Frequency-Modulated (PFM) DC-DC
    發(fā)表于 03-23 21:05 ?2653次閱讀
    <b class='flag-5'>Reduce</b> Standby Power Drains wi

    Reduce System Cost for Advance

    Reduce System Cost for Advanced Powerline Monitoring by Leveraging High-Performance
    發(fā)表于 10-03 08:43 ?1809次閱讀
    <b class='flag-5'>Reduce</b> System Cost for Advance

    筆記本音頻系統(tǒng)的優(yōu)化與保養(yǎng)

    筆記本音頻系統(tǒng)的優(yōu)化與保養(yǎng) 隨著筆記本硬件水平的提升,筆記本在各個方面已經(jīng)逐漸達(dá)到了主流臺式機的水平,開始具備了替代臺
    發(fā)表于 10-15 23:12 ?1297次閱讀

    筆記本音響系統(tǒng)分析及其優(yōu)化

    筆記本音響系統(tǒng)分析及其優(yōu)化 前言:   隨著筆記本硬件水平的提升,筆記本在各個方面已經(jīng)逐漸達(dá)到了主流臺式機的水平,
    發(fā)表于 01-23 08:48 ?564次閱讀

    Reduce階段values中的每個值都共享一個對象

    Hadoop備忘:Reduce階段IterableVALUEIN values中的每個都共享一個對象。在Reduce階段,具有相同key的的所有的value都會被組織到一起,形成一種key:values的形式。
    發(fā)表于 11-28 11:00 ?1335次閱讀

    mapreduce工作原理圖文詳解_Map、Reduce任務(wù)中Shuffle和排序

    本文主要分析以下兩點內(nèi)容:1.MapReduce作業(yè)運行流程原理2.Map、Reduce任務(wù)中Shuffle和排序的過程。分析如下文
    發(fā)表于 01-02 14:39 ?8474次閱讀
    mapreduce工作原理圖文詳解_Map、<b class='flag-5'>Reduce</b>任務(wù)中Shuffle和排序

    如何開始一個項目墊集成流:如何快速配置新筆記優(yōu)化設(shè)計庫

    學(xué)習(xí)如何快速配置新筆記優(yōu)化設(shè)計庫,所以你可以簡單地拖拽到方案設(shè)計部分。
    的頭像 發(fā)表于 10-10 07:10 ?2781次閱讀

    LTC2064 Demo Circuit - Example Use of Parallel Amplifiers to Reduce Noise by Square Root of 2

    LTC2064 Demo Circuit - Example Use of Parallel Amplifiers to Reduce Noise by Square Root of 2
    發(fā)表于 02-01 09:25 ?0次下載
    LTC2064 Demo Circuit - Example Use of Parallel Amplifiers to <b class='flag-5'>Reduce</b> Noise by Square Root of 2

    LTC2067 Demo Circuit - Example Use of Parallel Amplifiers to Reduce Noise by Square Root of 2

    LTC2067 Demo Circuit - Example Use of Parallel Amplifiers to Reduce Noise by Square Root of 2
    發(fā)表于 02-01 14:43 ?1次下載
    LTC2067 Demo Circuit - Example Use of Parallel Amplifiers to <b class='flag-5'>Reduce</b> Noise by Square Root of 2

    AD6633:采用VersaCREST?Crest Reduce Engine的多通道數(shù)字上變頻器數(shù)據(jù)表

    AD6633:采用VersaCREST?Crest Reduce Engine的多通道數(shù)字上變頻器數(shù)據(jù)表
    發(fā)表于 04-22 11:44 ?0次下載
    AD6633:采用VersaCREST?Crest <b class='flag-5'>Reduce</b> Engine的多通道數(shù)字上變頻器數(shù)據(jù)表