2016-07-20 93 views
1

我剛剛開始使用CUDA。現在我有一個問題。 我有N * N矩陣,窗口尺度是8x8。我想把這個矩陣細分成多個子矩陣並且找到它的最大值。 例如,如果我有64 * 64的矩陣,所以我將有8 * 8的小矩陣,並找出8個最大值。最後,我將所有最大值保存到新數組中,但其順序始終更改。我想找到解決方案,讓他們在正確的順序在CUDA中查找矩陣的最大值

__global__ void calculate_emax_kernel(float emap[],float emax[], int img_height, int img_width,int windows_size) 
{ 
    int x_index = blockIdx.x*blockDim.x+threadIdx.x; 
    int y_index = blockIdx.y*blockDim.y+threadIdx.y; 

    int num_row_block = img_height/windows_size; 
    int num_col_block = img_width/windows_size; 
    __shared__ float window_elements[256]; 
    __shared__ int counter; 
    __shared__ int emax_count; 

    if (threadIdx.x == 0) emax_count = 0; 
    __syncthreads(); 
    int index; 
    int emax_idx = 0; 


    if(y_index >= img_height|| x_index >= img_width) return; 
    for(int i = 0; i < num_row_block; i++) 
    { 
     for(int j = 0; j < num_col_block; j++) 
     { 
      counter = 0; 
      if(y_index >= i*windows_size && y_index < (i+1)*windows_size 
        && x_index >= j*windows_size && x_index < (j+1)*windows_size) 
      { 
       int idx = y_index*img_height + x_index; 
       index = atomicAdd(&counter, 1); 

       window_elements[index] = emap[idx]; 
       __syncthreads(); 


       // reduction 
       unsigned int k = (windows_size*windows_size)/2; 
       while(k != 0) 
       { 
        if(index < k) 
        { 
         window_elements[index] = fmaxf(window_elements[index], window_elements[index+k]); 

        } 
        k /= 2; 
       } 
       if(index == 0) 
       { 
        emax[i*num_row_block+j] = window_elements[index]; 
       } 
      } 
      __syncthreads(); 
     } 
     __syncthreads(); 
    } 
    __syncthreads(); 
} 

這是我的配置

void construct_emax(float *input,float *output, int img_height, int img_width) 
{ 
    int windows_size = 4; 
    float * d_input, * d_output; 
    cudaMalloc(&d_input, img_width*img_height*sizeof(float)); 
    cudaMalloc(&d_output, img_width*img_height*sizeof(float)); 

    cudaMemcpy(d_input, input, img_width*img_height*sizeof(float), cudaMemcpyHostToDevice); 
    dim3 blocksize(16,16); 
    dim3 gridsize; 

    gridsize.x=(img_width+blocksize.x-1)/blocksize.x; 
    gridsize.y=(img_height+blocksize.y-1)/blocksize.y; 

    calculate_emax_kernel<<<gridsize,blocksize>>>(d_input,d_output,img_height,img_width,windows_size); 

} 
+0

你的意思是「我將有一個8×8的8×8小矩陣,找出8×8的最大值」? – kangshiyin

+0

@ kangshiyin對不起,這很難解釋,它意味着我將輸入矩陣分成一些小矩陣,它取決於窗口的大小。 例如,如果我有16 * 16矩陣和8 * 8窗口大小,所以我將有4個小矩陣。並找出每個小矩陣的最大值。 –

+0

什麼是網格/塊配置? – kangshiyin

回答

2

有了CUDA,parallel reduction是棘手的; segmented parallel reduction更棘手。現在你正在以二維方式進行,而你的片段/窗口比線程塊小。

對於大窗口大小,我不認爲這是一個問題。您可以使用一個線程塊來減少一個窗口。例如,如果你有一個16x16的窗口,你可以簡單地使用16x16的線程塊。如果窗口尺寸更大,例如64x64,則仍然可以使用16x16線程塊。首先在數據加載期間將64x64窗口縮小爲16x16元素,然後在線程塊內將其縮小爲1標量。

對於小於塊大小的窗口大小,您必須減少每個線程塊的多個窗口以獲得更高的性能。您可以使用當前的塊/網格配置,其中每個256線程塊(16x16)負責16個4x4窗口。但是這不會是最佳的,因爲每個32線程的包裝分爲兩部分(2x16)。這對於coalesced global memory access並不合適,並且很難將2x16 warp映射到一個或多個4x4窗口以進行有效的並行縮減。

或者我建議你使用256線程的一維線程塊。每個線程減少一個m窗口。然後,您可以使用二維網格來覆蓋整個圖像。

const int m = window_size; 
dim3 blocksize(256); 
dim3 gridsize((img_width+255)/256, (img_height+m-1)/m); 

在內核的功能,你可以

  1. 減少每個m X m窗口全球數據加載時的1倍m向量;
  2. 使用樹簡化方法將1x m向量減少爲標量。

下面的代碼是一個概念性的演示,當m是2的冪和m <= 32的功能。你可以進一步修改它以獲得更好的邊界檢查m

#include <assert.h> 
#include <cuda.h> 
#include <thrust/device_vector.h> 

__global__ void calculate_emax_kernel(const float* input, float* output, 
             int height, int width, int win_size, 
             int out_width) { 
    const int tid = threadIdx.x; 
    const int i = blockIdx.y * win_size; 
    const int j = blockIdx.x * 256 + tid; 
    const int win_id = j % win_size; 

    __shared__ float smax[256]; 

    float tmax = -1e20; 
    if (j < width) { 
    for (int tile = 0; tile < win_size; tile++) { 
     if (i + tile < height) { 
     tmax = max(tmax, input[(i + tile) * width + j]); 
     } 
    } 
    } 
    smax[tid] = tmax; 
    for (int shift = win_size/2; shift > 0; shift /= 2) { 
    if (win_id < shift) { 
     smax[tid] = max(smax[tid], smax[tid + shift]); 
    } 
    } 
    if (win_id == 0 && j < width) { 
    output[blockIdx.y * out_width + (j/win_size)] = smax[tid]; 
    } 
} 

int main() { 
    const int height = 1024; 
    const int width = 1024; 
    const int m = 4; 
    thrust::device_vector<float> in(height * width); 
    thrust::device_vector<float> out(
     ((height + m - 1)/m) * ((width + m - 1)/m)); 

    dim3 blocksize(256); 
    dim3 gridsize((width + 255)/256, (height + m - 1)/m); 

    assert(m == 2 || m == 4 || m == 8 || m == 16 || m == 32); 
    calculate_emax_kernel<<<gridsize, blocksize>>>(
     thrust::raw_pointer_cast(in.data()), 
     thrust::raw_pointer_cast(out.data()), 
     height, width, m, (width + m - 1)/m); 

    return 0; 
}