首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >CUDA Mandelbrot集

CUDA Mandelbrot集
EN

Stack Overflow用户
提问于 2013-11-25 23:55:00
回答 3查看 7K关注 0票数 3

这是一个顺序的Mandelbrot集实现。

代码语言:javascript
复制
 void mandelbrot(PGMData *I)
{
    float x0,y0,x,y,xtemp;
    int i,j;
    int color;
    int iter;
    int MAX_ITER=1000;  
    for(i=0; i<I->height; i++)
        for(j=0; j<I->width; j++)
        {
            x0 = (float)j/I->width*(float)3.5-(float)2.5; 
            y0 = (float)i/I->height*(float)2.0-(float)1.0;
            x = 0;
            y = 0;
            iter = 0;
            while((x*x-y*y <= 4) && (iter < MAX_ITER))
            { 
                xtemp = x*x-y*y+x0;
                y = 2*x*y+y0;
                x = xtemp;
                iter++;
            }
            color = (int)(iter/(float)MAX_ITER*(float)I->max_gray);
            I->image[i*I->width+j] = I->max_gray-color;
        }
}

我想用CUDA对它进行平行化,但我似乎误解了什么,现在我被困住了。我试过搜索互联网,但没有什么好消息。

内核:

代码语言:javascript
复制
__global__ void calc(int *pos)
{
    int row= blockIdx.y * blockDim.y + threadIdx.y;  // WIDTH
    int col = blockIdx.x * blockDim.x + threadIdx.x;  // HEIGHT
    int idx = row * WIDTH + col;

    if(col > WIDTH || row > HEIGHT || idx > N) return;

    float x0 = (float)row/WIDTH*(float)3.5-(float)2.5;
    float y0 = (float)col/HEIGHT*(float)2.0-(float)1.0; 

    int x = 0, y = 0, iter = 0, xtemp = 0;
    while((x*x-y*y <= 4) && (iter < MAX_ITER))
    { 
        xtemp = x*x-y*y+x0;
        y = 2*x*y+y0;
        x = xtemp;
        iter++;
    }
    int color = 255 - (int)(iter/(float)MAX_ITER*(float)255);
    __syncthreads();
    pos[idx] = color;//color;// - color;

}

内核是以这种方式启动的:

代码语言:javascript
复制
dim3 block_size(16, 16);
dim3 grid_size((N)/block_size.x, (int) N / block_size.y);
calc<<<grid_size,block_size>>>(d_pgmData);

以下是常量:

代码语言:javascript
复制
#define HEIGHT 512
#define WIDTH 512   
#define N (HEIGHT*WIDTH)

整个GPU功能

代码语言:javascript
复制
void mandelbrotGPU(PGMData *I)
{
    int *pos = (int *)malloc(HEIGHT*WIDTH*sizeof(int));
    int *d_pgmData;

    cudaMalloc((void **)&d_pgmData, sizeof(int)*WIDTH*HEIGHT);


    cudaMemcpy(d_pgmData, pos ,HEIGHT*WIDTH*sizeof(int) ,cudaMemcpyHostToDevice);

    dim3 block_size(16, 16);
    dim3 grid_size((N)/block_size.x, (int) N / block_size.y);
    calc<<<grid_size,block_size>>>(d_pgmData);

    cudaMemcpy(pos,d_pgmData,HEIGHT*WIDTH*sizeof(int) ,cudaMemcpyDeviceToHost);
    cudaFree(d_pgmData);
    I->image = pos;
}

问题是:它要么返回垃圾,要么驱动程序崩溃。我真的很感激你给我一些建议,因为我真的被困住了。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-11-26 04:43:23

下面是代码的工作版本(使用OpenCV):

代码语言:javascript
复制
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace cv;
using namespace std;

#define HEIGHT 512 // must be multiple of block_size.y
#define WIDTH 512 // must be multiple of block_size.x
#define MAX_ITER 10000

void mandelbrotGPU(char*);
__global__ void calc(char* image_buffer);

#define cudaAssertSuccess(ans) { _cudaAssertSuccess((ans), __FILE__, __LINE__); }
inline void _cudaAssertSuccess(cudaError_t code, char *file, int line)
{
  if (code != cudaSuccess)  {
    fprintf(stderr,"_cudaAssertSuccess: %s %s %d\n", cudaGetErrorString(code), file, line);
    exit(code);
  }
}

int main(int argc, char** argv)
{
  IplImage* image_output = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1);
  mandelbrotGPU(image_output->imageData);
  cvShowImage("GPU", image_output);
  waitKey(0);
  cvReleaseImage(&image_output);
}

void mandelbrotGPU(char* image_buffer)
{
  char* d_image_buffer;
  cudaAssertSuccess(cudaMalloc(&d_image_buffer, WIDTH * HEIGHT));
  dim3 block_size(16, 16);
  dim3 grid_size(WIDTH / block_size.x, HEIGHT / block_size.y);
  calc<<<grid_size, block_size>>>(d_image_buffer);
  cudaAssertSuccess(cudaPeekAtLastError());
  cudaAssertSuccess(cudaDeviceSynchronize());
  cudaAssertSuccess(cudaMemcpy(image_buffer, d_image_buffer, HEIGHT * WIDTH, cudaMemcpyDeviceToHost));
  cudaAssertSuccess(cudaFree(d_image_buffer));
}

__global__ void calc(char* image_buffer)
{
  int row = blockIdx.y * blockDim.y + threadIdx.y;  // WIDTH
  int col = blockIdx.x * blockDim.x + threadIdx.x;  // HEIGHT
  int idx = row * WIDTH + col;
  if(col >= WIDTH || row >= HEIGHT) return;

  float x0 = ((float)col / WIDTH) * 3.5f - 2.5f;
  float y0 = ((float)row / HEIGHT) * 3.5f - 1.75f;

  float x = 0.0f;
  float y = 0.0f;
  int iter = 0;
  float xtemp;
  while((x * x + y * y <= 4.0f) && (iter < MAX_ITER))
  { 
    xtemp = x * x - y * y + x0;
    y = 2.0f * x * y + y0;
    x = xtemp;
    iter++;
  }

  int color = iter * 5;
  if (color >= 256) color = 0;
  image_buffer[idx] = color;
}

输出:

最重要的变化是:

  • 移除__syncthreads();。该算法不使用其他线程生成的数据,因此不需要同步线程。
  • 将主机缓冲区复制移到设备上。这是不必要的,因为Mandelbrot算法编写了整个设备缓冲区。
  • 修正了不正确的网格大小计算。
  • 删除主机内存的malloc,因为结果被直接复制到OpenCV映像缓冲区中。
  • 将缓冲区改为使用字节,而不是ints,这在只有8位分辨率的单一灰色通道时更为方便。
  • 删除了一些非必要的浮点数。当在计算中使用整数和浮点数时,整数将自动提升为浮点数。
  • 修正了Mandelbrot算法中的两个问题:
    • xy被声明为ints,而它们应该是floats。
    • while循环中的第一个表达式应该包含一个+,而不是一个-

票数 9
EN

Stack Overflow用户

发布于 2013-11-26 03:56:12

这当然是不正确的:

代码语言:javascript
复制
    dim3 grid_size((N)/block_size.x, (int) N / block_size.y);

这会导致内核中的越界访问。您希望总共启动WIDTH x HEIGHT线程,图像中的每个像素都要启动一个线程。相反,您正在启动N/16 x N/16线程。

而且您的内核中似乎有一个线程检查行(应该防止错误线程的越界访问),但是它没有正确的表述:

代码语言:javascript
复制
if(col > WIDTH || row > HEIGHT || idx > N) return;

例如,这允许idx = N通过线程检查,但是当内核的最后一行写入时,这不是一个有效的内存位置:

代码语言:javascript
复制
pos[idx] = color;

您可以使用以下方法修复此线程检查:

代码语言:javascript
复制
if(col >= WIDTH || row >= HEIGHT || idx >= N) return;

其他几点评论:

票数 3
EN

Stack Overflow用户

发布于 2013-11-26 00:05:15

只是有一些想法要看:

  1. 没有必要使用__syncthreads()。块中的线程不能相互通信。
  2. 不需要为I_WIDTH和I_HEIGHT创建设备内存。您只需将它们作为值传入(而不是作为指针或引用)。pos确实需要设备内存。
  3. 您需要检查所有CUDA函数(例如,cudaMalloc)的返回值,并确保一切正常。
  4. 当启动内核时,您的程序可以在GPU完成之前返回。在某些情况下,您需要显式地等待完成;启动后可以通过调用cudaDeviceSynchronize()来做到这一点。在您的情况下,您不必这样做,因为您的CUDA memcpy将等待内核完成。
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20205941

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档