2013-08-07 2 views
0

У меня есть ядро ​​CUDA, которое берет краевое изображение и обрабатывает его для создания меньшего массива 1D краевых пикселей. Теперь вот странное поведение. Каждый раз, когда я запускаю ядро ​​и вычисляю количество краевых пикселей в «d_nlist» (см. Код рядом с printf), я получаю большее количество пикселей каждый раз, даже когда я использую одно и то же изображение и полностью останавливаю программу, запустить. Поэтому каждый раз, когда я запускаю его, требуется больше времени для запуска, пока, в конце концов, он не выбрасывает исключение.Преобразование 2D Canny Edge изображения в 1D краевой массив пикселей в CUDA - странное поведение

Мой вопрос в том, как я могу остановить это, чтобы я мог получать согласованные результаты каждый раз, когда запускаю ядро?

Мое устройство является Geforce 620.

Константы:

THREADS_X = 32
THREADS_Y = 4
PIXELS_PER_THREAD = 4
MAX_QUEUE_LENGTH = THREADS_X * THREADS_Y * PIXELS_PER_THREAD
img_width = 256
IMG_HEIGHT = 256
IMG_SIZE = IMG_WIDTH * IMG_HEIGHT
BLOCKS_X = img_width/(THREADS_X * PIXELS_PER_THREAD)
BLOCKS_Y = img_height/THREADS_Y

Ядро выглядит следующим образом:

__global__ void convert2DEdgeImageTo1DArray(unsigned char const * const image, 
unsigned int* const list, int* const glob_index) { 

unsigned int const x = blockIdx.x * THREADS_X*PIXELS_PER_THREAD + threadIdx.x; 
unsigned int const y = blockIdx.y * THREADS_Y + threadIdx.y; 

volatile int qindex = -1; 
volatile __shared__ int sh_qindex[THREADS_Y]; 
volatile __shared__ int sh_qstart[THREADS_Y]; 
sh_qindex[threadIdx.y] = -1; 

// Start by making an array 
volatile __shared__ unsigned int sh_queue[MAX_QUEUE_LENGTH]; 

// Fill the queue 
for(int i=0; i<PIXELS_PER_THREAD; i++) 
{ 
    int const xx = i*THREADS_X + x; 

    // Read one image pixel from global memory 
    unsigned char const pixel = image[y*IMG_WIDTH + xx]; 
    unsigned int const queue_val = (y << 16) + xx; 

    if(pixel) 
    {   
     do { 
      qindex++; 
      sh_qindex[threadIdx.y] = qindex; 
      sh_queue[threadIdx.y*THREADS_X*PIXELS_PER_THREAD + qindex] = queue_val; 
     } while (sh_queue[threadIdx.y*THREADS_X*PIXELS_PER_THREAD + qindex] != queue_val); 
    } 

    // Reload index from smem (last thread to write to smem will have updated it) 
    qindex = sh_qindex[threadIdx.y]; 
} 

// Let thread 0 reserve the space required in the global list 
__syncthreads(); 
if(threadIdx.x == 0 && threadIdx.y == 0) 
{ 
    // Find how many items are stored in each list 
    int total_index = 0; 
    #pragma unroll 
    for(int i=0; i<THREADS_Y; i++) 
    { 
     sh_qstart[i] = total_index; 
     total_index += (sh_qindex[i] + 1u); 
    } 

    // Calculate the offset in the global list 
    unsigned int global_offset = atomicAdd(glob_index, total_index); 
    #pragma unroll 
    for(int i=0; i<THREADS_Y; i++) 
    { 
     sh_qstart[i] += global_offset; 
    } 
} 
__syncthreads(); 

// Copy local queues to global queue 
for(int i=0; i<=qindex; i+=THREADS_X) 
{ 
    if(i + threadIdx.x > qindex) 
     break; 

    unsigned int qvalue = sh_queue[threadIdx.y*THREADS_X*PIXELS_PER_THREAD + i + threadIdx.x]; 
    list[sh_qstart[threadIdx.y] + i + threadIdx.x] = qvalue; 
} 
} 

Ниже приведен метод, который вызывает ядро:

void call2DTo1DKernel(unsigned char const * const h_image) 
{ 
    // Device side allocation 
    unsigned char *d_image = NULL; 
    unsigned int *d_list = NULL; 
    int h_nlist, *d_nlist = NULL; 
    cudaMalloc((void**)&d_image, sizeof(unsigned char)*IMG_SIZE); 
    cudaMalloc((void**)&d_list, sizeof(unsigned int)*IMG_SIZE); 
    cudaMalloc((void**)&d_nlist, sizeof(int)); 

    // Time measurement initialization 
    cudaEvent_t start, stop, startio, stopio; 
    cudaEventCreate(&start); 
    cudaEventCreate(&stop); 
    cudaEventCreate(&startio); 
    cudaEventCreate(&stopio); 

    // Start timer w/ io 
    cudaEventRecord(startio,0); 

    // Copy image data to device 
    cudaMemcpy((void*)d_image, (void*)h_image, sizeof(unsigned char)*IMG_SIZE, cudaMemcpyHostToDevice); 

    // Start timer 
    cudaEventRecord(start,0); 

    // Kernel call 
    // Phase 1 : Convert 2D binary image to 1D pixel array 
    dim3 dimBlock1(THREADS_X, THREADS_Y); 
    dim3 dimGrid1(BLOCKS_X, BLOCKS_Y); 
    convert2DEdgeImageTo1DArray<<<dimGrid1, dimBlock1>>>(d_image, d_list, d_nlist); 

    // Stop timer 
    cudaEventRecord(stop,0); 
    cudaEventSynchronize(stop); 

    // Stop timer w/ io 
    cudaEventRecord(stopio,0); 
    cudaEventSynchronize(stopio); 

    // Time measurement 
    cudaEventElapsedTime(&et,start,stop); 
    cudaEventElapsedTime(&etio,startio,stopio); 

    // Time measurement deinitialization 
    cudaEventDestroy(start); 
    cudaEventDestroy(stop); 
    cudaEventDestroy(startio); 
    cudaEventDestroy(stopio); 

    // Get list size 
    cudaMemcpy((void*)&h_nlist, (void*)d_nlist, sizeof(int), cudaMemcpyDeviceToHost); 

    // Report on console 
    printf("%d pixels processed...\n", h_nlist); 

    // Device side dealloc 
    cudaFree(d_image); 
    cudaFree(d_space); 
    cudaFree(d_list); 
    cudaFree(d_nlist); 
} 

Большое спасибо за вашу помощь всем.

+0

У вас есть реальный вопрос здесь? Все, что я вижу, - это много кода и симптомы списка. Что * точно * неверно и, в равной степени важно, почему это неправильно? Что вы ожидаете от ответа, скажут вам? Помогите нам помочь вам .... – talonmies

+0

Ну, в основном число обработанных пикселей должно быть одинаковым каждый раз, когда я использую одно и то же изображение. Проблема в том, что printf дает мне другой результат каждый раз. Программа должна буквально считываться на изображении и помещать краевые пиксели в список, который меньше исходного изображения. Поэтому я должен получать одинаковое количество пикселей в массиве каждый раз. Мой вопрос, я думаю, как я могу остановить это? Надеюсь это поможет. –

+0

Итак, где находится 'houghKernel2_3_phase1' в коде, который вы указали? – sgarizvi

ответ

1

В преамбуле, позвольте мне предложить некоторые шаги по устранению неполадок, которые могут быть полезны:

  1. инструмент код с proper cuda error checking
  2. запустить свой код cuda-memcheck например cuda-memcheck ./myapp

Если вы делаете эти шаги, вы обнаружите, что ваше ядро ​​не удается, и неудачи должны делать глобальные записи размера 4. Так что будет акцентировать ваше внимание на последнем сегменте вашего ядра , начиная с комментарием // Copy local queues to global queue

Что касается кода, то у вас есть по крайней мере 2 проблемы:

  1. адресации/индексации в вашем заключительном отрезке своего ядра, где вы пишете отдельные очереди, чтобы глобальная память, испорчена. Я не собираюсь пытаться отлаживать это для вас.
  2. Вы не инициализируете переменную d_nlist равной нулю. Поэтому, когда вы добавляете атом, добавьте свои значения в значение нежелательной почты, которое будет увеличиваться по мере повторения процесса.

Ниже приведен код, в котором проблемы были удалены (я не пытался отсортировать код копии очереди) и добавлена ​​проверка ошибок.Он дает повторяющиеся результаты для меня:

$ cat t216.cu 
#include <stdio.h> 
#include <stdlib.h> 

#define THREADS_X 32 
#define THREADS_Y 4 
#define PIXELS_PER_THREAD 4 
#define MAX_QUEUE_LENGTH (THREADS_X*THREADS_Y*PIXELS_PER_THREAD) 
#define IMG_WIDTH 256 
#define IMG_HEIGHT 256 
#define IMG_SIZE (IMG_WIDTH*IMG_HEIGHT) 
#define BLOCKS_X (IMG_WIDTH/(THREADS_X*PIXELS_PER_THREAD)) 
#define BLOCKS_Y (IMG_HEIGHT/THREADS_Y) 

#define cudaCheckErrors(msg) \ 
    do { \ 
     cudaError_t __err = cudaGetLastError(); \ 
     if (__err != cudaSuccess) { \ 
      fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \ 
       msg, cudaGetErrorString(__err), \ 
       __FILE__, __LINE__); \ 
      fprintf(stderr, "*** FAILED - ABORTING\n"); \ 
      exit(1); \ 
     } \ 
    } while (0) 

__global__ void convert2DEdgeImageTo1DArray(unsigned char const * const image, 
unsigned int* const list, int* const glob_index) { 

unsigned int const x = blockIdx.x * THREADS_X*PIXELS_PER_THREAD + threadIdx.x; 
unsigned int const y = blockIdx.y * THREADS_Y + threadIdx.y; 

volatile int qindex = -1; 
volatile __shared__ int sh_qindex[THREADS_Y]; 
volatile __shared__ int sh_qstart[THREADS_Y]; 
sh_qindex[threadIdx.y] = -1; 

// Start by making an array 
volatile __shared__ unsigned int sh_queue[MAX_QUEUE_LENGTH]; 

// Fill the queue 
for(int i=0; i<PIXELS_PER_THREAD; i++) 
{ 
    int const xx = i*THREADS_X + x; 

    // Read one image pixel from global memory 
    unsigned char const pixel = image[y*IMG_WIDTH + xx]; 
    unsigned int const queue_val = (y << 16) + xx; 

    if(pixel) 
    { 
     do { 
      qindex++; 
      sh_qindex[threadIdx.y] = qindex; 
      sh_queue[threadIdx.y*THREADS_X*PIXELS_PER_THREAD + qindex] = queue_val; 
     } while (sh_queue[threadIdx.y*THREADS_X*PIXELS_PER_THREAD + qindex] != queue_val); 
    } 

    // Reload index from smem (last thread to write to smem will have updated it) 
    qindex = sh_qindex[threadIdx.y]; 
} 

// Let thread 0 reserve the space required in the global list 
__syncthreads(); 
if(threadIdx.x == 0 && threadIdx.y == 0) 
{ 
    // Find how many items are stored in each list 
    int total_index = 0; 
    #pragma unroll 
    for(int i=0; i<THREADS_Y; i++) 
    { 
     sh_qstart[i] = total_index; 
     total_index += (sh_qindex[i] + 1u); 
    } 

    // Calculate the offset in the global list 
    unsigned int global_offset = atomicAdd(glob_index, total_index); 
    #pragma unroll 
    for(int i=0; i<THREADS_Y; i++) 
    { 
     sh_qstart[i] += global_offset; 
    } 

} 
__syncthreads(); 

// Copy local queues to global queue 
/* 
for(int i=0; i<=qindex; i+=THREADS_X) 
{ 
    if(i + threadIdx.x > qindex) 
     break; 

    unsigned int qvalue = sh_queue[threadIdx.y*THREADS_X*PIXELS_PER_THREAD + i + threadIdx.x]; 
    list[sh_qstart[threadIdx.y] + i + threadIdx.x] = qvalue; 
} 
*/ 
} 

void call2DTo1DKernel(unsigned char const * const h_image) 
{ 
    // Device side allocation 
    unsigned char *d_image = NULL; 
    unsigned int *d_list = NULL; 
    int h_nlist=0, *d_nlist = NULL; 
    cudaMalloc((void**)&d_image, sizeof(unsigned char)*IMG_SIZE); 
    cudaMalloc((void**)&d_list, sizeof(unsigned int)*IMG_SIZE); 
    cudaMalloc((void**)&d_nlist, sizeof(int)); 
    cudaCheckErrors("cudamalloc fail"); 

    // Time measurement initialization 
    cudaEvent_t start, stop, startio, stopio; 
    cudaEventCreate(&start); 
    cudaEventCreate(&stop); 
    cudaEventCreate(&startio); 
    cudaEventCreate(&stopio); 
    float et, etio; 

    // Start timer w/ io 
    cudaEventRecord(startio,0); 
    cudaMemcpy(d_nlist, &h_nlist, sizeof(int), cudaMemcpyHostToDevice); 
    // Copy image data to device 
    cudaMemcpy((void*)d_image, (void*)h_image, sizeof(unsigned char)*IMG_SIZE, cudaMemcpyHostToDevice); 
    cudaCheckErrors("cudamemcpy 1"); 
    // Start timer 
    cudaEventRecord(start,0); 

    // Kernel call 
    // Phase 1 : Convert 2D binary image to 1D pixel array 
    dim3 dimBlock1(THREADS_X, THREADS_Y); 
    dim3 dimGrid1(BLOCKS_X, BLOCKS_Y); 
    convert2DEdgeImageTo1DArray<<<dimGrid1, dimBlock1>>>(d_image, d_list, d_nlist); 
    cudaDeviceSynchronize(); 
    cudaCheckErrors("kernel fail"); 
    // Stop timer 
    cudaEventRecord(stop,0); 
    cudaEventSynchronize(stop); 

    // Stop timer w/ io 
    cudaEventRecord(stopio,0); 
    cudaEventSynchronize(stopio); 

    // Time measurement 
    cudaEventElapsedTime(&et,start,stop); 
    cudaEventElapsedTime(&etio,startio,stopio); 

    // Time measurement deinitialization 
    cudaEventDestroy(start); 
    cudaEventDestroy(stop); 
    cudaEventDestroy(startio); 
    cudaEventDestroy(stopio); 

    // Get list size 
    cudaMemcpy((void*)&h_nlist, (void*)d_nlist, sizeof(int), cudaMemcpyDeviceToHost); 
    cudaCheckErrors("cudaMemcpy 2"); 
    // Report on console 
    printf("%d pixels processed...\n", h_nlist); 

    // Device side dealloc 
    cudaFree(d_image); 
// cudaFree(d_space); 
    cudaFree(d_list); 
    cudaFree(d_nlist); 
} 

int main(){ 

    unsigned char *image; 

    image = (unsigned char *)malloc(IMG_SIZE * sizeof(unsigned char)); 
    if (image == 0) {printf("malloc fail\n"); return 0;} 

    for (int i =0 ; i<IMG_SIZE; i++) 
    image[i] = rand()%2; 

    call2DTo1DKernel(image); 
    call2DTo1DKernel(image); 
    call2DTo1DKernel(image); 
    call2DTo1DKernel(image); 
    call2DTo1DKernel(image); 
    cudaCheckErrors("some error"); 
    return 0; 
} 

$ nvcc -arch=sm_20 -O3 -o t216 t216.cu 
$ ./t216 
32617 pixels processed... 
32617 pixels processed... 
32617 pixels processed... 
32617 pixels processed... 
32617 pixels processed... 
$ ./t216 
32617 pixels processed... 
32617 pixels processed... 
32617 pixels processed... 
32617 pixels processed... 
32617 pixels processed... 
$