2011-09-25 38 views
0

前一個問題問如何找到找到CUDA數組的最大值有效:Finding max value in CUDA,頂響應優化減少內核提供了一個鏈接到NVIDIA演示。CUDA最大抑制算法不工作

如果您使用Visual Studio,只需刪除標題引用以及CPU執行之間的所有內容。

我設置,其中發現的最大一個變體,但它不匹配什麼CPU被發現:

// Returns the maximum value of 
// an array of size n 
float GetMax(float *maxes, int n) 
{ 
    int i = 0; 
    float max = -100000; 
    for(i = 0; i < n; i++) 
    { 
     if(maxes[i] > max) 
      max = maxes[i]; 
    } 

    return max; 
} 

// Too obvious... 
__device__ float MaxOf2(float a, float b) 
{ 
    if(a > b) return a; 
    else   return b; 
} 


__global__ void MaxReduction(int n, float *g_idata, float *g_odata) 
{ 
    extern __shared__ float sdata[]; 
    unsigned int tid = threadIdx.x; 
    unsigned int i = blockIdx.x*(BLOCKSIZE*2) + tid; 
    unsigned int gridSize = BLOCKSIZE*2*gridDim.x; 

    sdata[tid] = 0; 

    //MMX(index,i) 
    //MMX(index,i+blockSize) 
    // Final Optimized Kernel 
    while (i < n) { 
     sdata[tid] = MaxOf2(g_idata[i], g_idata[i+BLOCKSIZE]); 
     i += gridSize; 
    } 
    __syncthreads(); 

    if (BLOCKSIZE >= 512) { if (tid < 256) { sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 256]); } __syncthreads(); } 
    if (BLOCKSIZE >= 256) { if (tid < 128) { sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 128]); } __syncthreads(); } 
    if (BLOCKSIZE >= 128) { if (tid < 64) { sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 64]); } __syncthreads(); } 

    if (tid < 32) { 
     if (BLOCKSIZE >= 64) sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 32]); 
     if (BLOCKSIZE >= 32) sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 16]); 
     if (BLOCKSIZE >= 16) sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 8]); 
     if (BLOCKSIZE >= 8) sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 4]); 
     if (BLOCKSIZE >= 4) sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 2]); 
     if (BLOCKSIZE >= 2) sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 1]); 
    } 

    if (tid == 0) g_odata[blockIdx.x] = sdata[0]; 
} 

我有一個巨大的設置來測試這個算法:

#include <cstdio> 
#include <cstdlib> 
#include <ctime> 
#include <iostream> 
#include <sys/time.h> 

#include <cuda.h> 
#include <cuda_runtime.h> 
#include <device_launch_parameters.h> 

#include "book.h" 

#define ARRAYSIZE 16384 
#define GRIDSIZE 60 
#define BLOCKSIZE 32 
#define SIZEFLOAT 4 

using namespace std; 

// Function definitions 
float GetMax(float *maxes, int n); 
__device__ float MaxOf2(float a, float b); 
__global__ void MaxReduction(int n, float *g_idata, float *g_odata); 

// Returns random floating point number 
float RandomReal(float low, float high) 
{ 
    float d; 

    d = (float) rand()/((float) RAND_MAX + 1); 
    return (low + d * (high - low)); 
} 

int main() 
{ 
    /*****************VARIABLE SETUP*****************/ 
    // Pointer to CPU numbers 
    float *numbers; 
    // Pointer to GPU numbers 
    float *dev_numbers; 
    // Counter 
    int i = 0; 

    // Randomize 
    srand(time(0)); 

    // Timers 
    // Kernel timers 
    cudaEvent_t start_kernel, stop_kernel; 
    float elapsedTime_kernel; 
    HANDLE_ERROR(cudaEventCreate(&start_kernel)); 
    HANDLE_ERROR(cudaEventCreate(&stop_kernel)); 
    // cudaMalloc timers 
    cudaEvent_t start_malloc, stop_malloc; 
    float elapsedTime_malloc; 
    HANDLE_ERROR(cudaEventCreate(&start_malloc)); 
    HANDLE_ERROR(cudaEventCreate(&stop_malloc)); 
    // CPU timers 
    struct timeval start, stop; 
    float elapsedTime = 0; 
    /*****************VARIABLE SETUP*****************/ 


    /*****************CPU ARRAY SETUP*****************/ 
    // Setup CPU array 
    HANDLE_ERROR(cudaHostAlloc((void**)&numbers, ARRAYSIZE * sizeof(float), cudaHostAllocDefault)); 
    for(i = 0; i < ARRAYSIZE; i++) 
     numbers[i] = RandomReal(0, 50000.0); 
    /*****************CPU ARRAY SETUP*****************/ 


    /*****************GPU ARRAY SETUP*****************/ 
    // Start recording cuda malloc time 
    HANDLE_ERROR(cudaEventRecord(start_malloc,0)); 

    // Allocate memory to GPU 
    HANDLE_ERROR(cudaMalloc((void**)&dev_numbers, ARRAYSIZE * sizeof(float))); 
    // Transfer CPU array to GPU 
    HANDLE_ERROR(cudaMemcpy(dev_numbers, numbers, ARRAYSIZE*sizeof(float), cudaMemcpyHostToDevice)); 
    // An array to temporarily store maximum values on the GPU 
    float *dev_max; 
    HANDLE_ERROR(cudaMalloc((void**)&dev_max, GRIDSIZE * sizeof(float))); 
    // An array to hold grab the GPU max 
    float maxes[GRIDSIZE]; 
    /*****************GPU ARRAY SETUP*****************/ 

    /*****************KERNEL EXECUTION*****************/ 
    // Start recording kernel execution time 
    HANDLE_ERROR(cudaEventRecord(start_kernel,0)); 
    // Run kernel 
    MaxReduction<<<GRIDSIZE, BLOCKSIZE, SIZEFLOAT*BLOCKSIZE>>> (ARRAYSIZE, dev_numbers, dev_max); 
    // Transfer maxes over 
    HANDLE_ERROR(cudaMemcpy(maxes, dev_max, GRIDSIZE * sizeof(float), cudaMemcpyDeviceToHost)); 
    // Print out the max 
    cout << GetMax(maxes, GRIDSIZE) << endl; 

    // Stop recording kernel execution time 
    HANDLE_ERROR(cudaEventRecord(stop_kernel,0)); 
    HANDLE_ERROR(cudaEventSynchronize(stop_kernel)); 
    // Retrieve recording data 
    HANDLE_ERROR(cudaEventElapsedTime(&elapsedTime_kernel, start_kernel, stop_kernel)); 
    // Stop recording cuda malloc time 
    HANDLE_ERROR(cudaEventRecord(stop_malloc,0)); 
    HANDLE_ERROR(cudaEventSynchronize(stop_malloc)); 
    // Retrieve recording data 
    HANDLE_ERROR(cudaEventElapsedTime(&elapsedTime_malloc, start_malloc, stop_malloc)); 
    // Print results 
    printf("%5.3f\t%5.3f\n", elapsedTime_kernel, elapsedTime_malloc); 
    /*****************KERNEL EXECUTION*****************/ 


    /*****************CPU EXECUTION*****************/ 
    // Capture the start time 
    gettimeofday(&start, NULL); 
    // Call generic P7Viterbi function 
    cout << GetMax(numbers, ARRAYSIZE) << endl; 
    // Capture the stop time 
    gettimeofday(&stop, NULL); 
    // Retrieve time elapsed in milliseconds 
    long int elapsed_sec = stop.tv_sec - start.tv_sec; 
    long int elapsed_usec = stop.tv_usec - start.tv_usec; 
    elapsedTime = (float)(1000.0f * elapsed_sec) + (float)(0.001f * elapsed_usec); 
    // Print results 
    printf("%5.3f\n", elapsedTime); 
    /*****************CPU EXECUTION*****************/ 

    // Free memory 
    cudaFreeHost(numbers); 
    cudaFree(dev_numbers); 
    cudaFree(dev_max); 
    cudaEventDestroy(start_kernel); 
    cudaEventDestroy(stop_kernel); 
    cudaEventDestroy(start_malloc); 
    cudaEventDestroy(stop_malloc); 

    // Exit program 
    return 0; 
} 

我在此測試程序上運行cuda-memcheck,並且-g & -G開啓,並報告0個問題。任何人都可以發現問題嗎?

注意:編譯程序時,一定要在當前目錄下的CUDA示例手冊中有book.h。來源鏈接在這裏:http://developer.nvidia.com/cuda-example-introduction-general-purpose-gpu-programming 下載源代碼,並book.h將是公共目錄/文件夾下。

+0

是否使用了費米GPU DOR呢? – talonmies

+0

@talonmies對不起,應該提到,我正在使用特斯拉C1060。雖然在家裏我有兩個560 Ti。 – sj755

回答

5

內核看起來破我。線程本地搜索(在共享內存減少之前),應該看起來像這樣:

sdata[tid] = g_idata[i]; 
i += gridSize; 

while (i < n) { 
    sdata[tid] = MaxOf2(sdata[tid], g_idata[i]); 
    i += gridSize; 
} 

不應該嗎?

另請注意,如果您在Fermi上運行此共享內存緩衝區,則應聲明爲volatile,並且如果線程本地搜索使用寄存器變量而不是共享內存,您將獲得顯着的性能改進。兩者之間的有效帶寬相差約8倍。


編輯:這是您的縮減內核的簡化,工作版本。您應該注意與您的原始數據相比的一些差異:

__global__ void MaxReduction(int n, float *g_idata, float *g_odata) 
{ 
    extern __shared__ volatile float sdata[]; 
    unsigned int tid = threadIdx.x; 
    unsigned int i = blockIdx.x*(BLOCKSIZE) + tid; 
    unsigned int gridSize = BLOCKSIZE*gridDim.x; 

    float val = g_idata[i]; 
    i += gridSize; 
    while (i < n) { 
     val = MaxOf2(g_idata[i],val); 
     i += gridSize; 
    } 
    sdata[tid] = val; 
    __syncthreads(); 

    // This versions uses a single warp for the shared memory 
    // reduction 
# pragma unroll 
    for(int i=(tid+32); ((tid<32)&&(i<BLOCKSIZE)); i+=32) 
     sdata[tid] = MaxOf2(sdata[tid], sdata[i]); 

    if (tid < 16) sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 16]); 
    if (tid < 8) sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 8]); 
    if (tid < 4) sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 4]); 
    if (tid < 2) sdata[tid] = MaxOf2(sdata[tid], sdata[tid + 2]); 
    if (tid == 0) g_odata[blockIdx.x] = MaxOf2(sdata[tid], sdata[tid + 1]); 
} 

此代碼在費米也應該是安全的。您還應該熟悉CUDA數學庫,因爲您應該使用內在函數來替代您的函數MaxOf2

+0

我認爲我們的兩個代碼最終都可以訪問數組的相同索引,儘管您的代碼更有意義。我編譯了代碼,而不是正確的結果。任何機會,我如何設置我的代碼? – sj755

+0

@ seljuq70:不,它是以兩種不同方式出錯的內核代碼。代碼中有兩個不同的超出界限內存訪問源,全局內存中有一個,共享內存中有一個。我已經更新了我的答案,並提供了一個可供您學習的實施方案。 – talonmies

+0

它的作品,但是,你的代碼有問題。它假定數組的大小大於線程的數量。這會導致全局內存中的內存訪問衝突,這會導致將值與垃圾值進行比較。你的代碼適用於16384的情況,所以謝謝! – sj755