OpenCLGPU计算错误
我通过将现有的C代码转换成OpenCL来开始OpenCL。我在CPU和GPU计算中得到了奇怪的结果。当我运行代码时,它们的值“每次”都会更改。当我与普通的C语言进行比较时,我会从CPU获得“某种程度上”可以接受的结果(但是,结果仍然和本机C语言甚至其他语言的结果不一样),但当我用GPU运行“完全相同”的代码时,我会得到乱七八糟的结果 这是我在主机上的代码OpenCLGPU计算错误,opencl,Opencl,我通过将现有的C代码转换成OpenCL来开始OpenCL。我在CPU和GPU计算中得到了奇怪的结果。当我运行代码时,它们的值“每次”都会更改。当我与普通的C语言进行比较时,我会从CPU获得“某种程度上”可以接受的结果(但是,结果仍然和本机C语言甚至其他语言的结果不一样),但当我用GPU运行“完全相同”的代码时,我会得到乱七八糟的结果 这是我在主机上的代码 #include <stdio.h> #include <stdlib.h> #include <CL/cl.h
#include <stdio.h>
#include <stdlib.h>
#include <CL/cl.h>
#include <math.h>
double *arange(double start, double end, double step)
{
// 'arange' routine.
int i;
int arr_size = ((end - start) / step) + 1;
double *output = malloc(arr_size * sizeof(double));
for(i=0;i<arr_size;i++)
{
output[i] = start + (step * i);
}
return output;
}
int main()
{
// This code executes on the OpenCL Host
// Host data
double nu_ini = 100.0, nu_end = 2000.0, nu_step = 1.0;
double *delnu = arange(nu_ini, nu_end, nu_step);
double *nu, *inten, A, *gam_air, gam_self, E_pprime, *n_air, *del_air;
double *gamma, *f;
double prs = 950.0;
int i, j, dum, lines=0, ID, delnu_size = (((nu_end - nu_ini)/nu_step) + 1);
FILE *fp = fopen("h2o_HITRAN.par","r");
char string[320];
while(!feof(fp))
{
dum = fgetc(fp);
if(dum == '\n')
{
lines++;
}
}
rewind(fp);
nu = malloc(lines * sizeof(double));
inten = malloc(lines * sizeof(double));
gam_air = malloc(lines * sizeof(double));
n_air = malloc(lines * sizeof(double));
del_air = malloc(lines * sizeof(double));
gamma = malloc(lines * sizeof(double));
f = malloc(delnu_size * sizeof(double));
i=0;
while(fgets(string, 320, fp))
{
sscanf(string, "%2d %12lf %10le %10le %5lf %5lf %10lf %4lf %8lf", &ID, &nu[i], &inten[i], &A, &gam_air[i], &gam_self, &E_pprime, &n_air[i], &del_air[i]);
i++;
}
size_t line_siz = sizeof(double) * lines;
size_t delnu_siz = sizeof(double) * delnu_size;
// gamma calculation
for(i=0;i<lines;i++)
{
gamma[i] = pow((296.0/300.0),n_air[i]) * (gam_air[i]*(prs/1013.0));
}
// Use this to check the output of each API call
cl_int status;
// Retrieve the number of Platforms
cl_uint numPlatforms = 0;
status = clGetPlatformIDs(0, NULL, &numPlatforms);
// Allocate enough space for each Platform
cl_platform_id *platforms = NULL;
platforms = (cl_platform_id*)malloc(numPlatforms*sizeof(cl_platform_id));
// Fill in the Platforms
status = clGetPlatformIDs(numPlatforms, platforms, NULL);
// Retrieve the number of Devices
cl_uint numDevices = 0;
status = clGetDeviceIDs(platforms[0],CL_DEVICE_TYPE_ALL, 0, NULL, &numDevices);
// Allocate enough spaces for each Devices
char name_data[100];
int *comp_units;
cl_device_fp_config cfg;
cl_device_id *devices = NULL;
devices = (cl_device_id*)malloc(numDevices*sizeof(cl_device_id));
// Fill in the Devices
status = clGetDeviceIDs(platforms[0], CL_DEVICE_TYPE_ALL, numDevices, devices, NULL);
// Create a context and associate it with the devices
cl_context context = NULL;
context = clCreateContext(NULL, numDevices, devices, NULL, NULL, &status);
// Create a command queue and associate it with the devices
cl_command_queue cmdQueue = NULL;
cmdQueue = clCreateCommandQueueWithProperties(context, devices[0], 0, &status);
// Create a buffer objects that will contain the data from the host array 'buf_xxxx'
cl_mem buf_inten = NULL;
cl_mem buf_gamma = NULL;
cl_mem buf_delnu = NULL;
cl_mem buf_nu = NULL;
cl_mem buf_del_air = NULL;
cl_mem buf_f = NULL;
buf_inten = clCreateBuffer(context, CL_MEM_READ_ONLY, line_siz, NULL, &status);
buf_gamma = clCreateBuffer(context, CL_MEM_READ_ONLY, line_siz, NULL, &status);
buf_delnu = clCreateBuffer(context, CL_MEM_READ_ONLY, delnu_siz, NULL, &status);
buf_nu = clCreateBuffer(context, CL_MEM_READ_ONLY, line_siz, NULL, &status);
buf_del_air = clCreateBuffer(context, CL_MEM_READ_ONLY, line_siz, NULL, &status);
buf_f = clCreateBuffer(context, CL_MEM_READ_ONLY, delnu_siz, NULL, &status);
// Write input array A to the Device buffer 'buf_xxx'
status = clEnqueueWriteBuffer(cmdQueue, buf_inten, CL_FALSE, 0, line_siz, inten, 0, NULL, NULL);
status = clEnqueueWriteBuffer(cmdQueue, buf_gamma, CL_FALSE, 0, line_siz, gamma, 0, NULL, NULL);
status = clEnqueueWriteBuffer(cmdQueue, buf_delnu, CL_FALSE, 0, delnu_siz, delnu, 0, NULL, NULL);
status = clEnqueueWriteBuffer(cmdQueue, buf_nu, CL_FALSE, 0, line_siz, nu, 0, NULL, NULL);
status = clEnqueueWriteBuffer(cmdQueue, buf_del_air, CL_FALSE, 0, line_siz, del_air, 0, NULL, NULL);
// Create Program with the source code
cl_program program = NULL;
size_t program_size;
char *program_Source;
FILE *program_handle = fopen("abs_calc.cl","r");
fseek(program_handle, 0, SEEK_END);
program_size = ftell(program_handle);
rewind(program_handle);
program_Source = (char*)malloc(program_size+1);
program_Source[program_size] = '\0';
fread(program_Source, sizeof(char), program_size, program_handle);
fclose(program_handle);
program = clCreateProgramWithSource(context, 1, (const char**)&program_Source, &program_size, &status);
// Compile the Program for the Device
status = clBuildProgram(program, numDevices, devices, NULL, NULL, NULL);
// Create the vector addition kernel
cl_kernel kernel = NULL;
kernel = clCreateKernel(program, "abs_cross", &status);
// Associate the input and output buffers with the kernel
status = clSetKernelArg(kernel, 0, sizeof(cl_mem), &buf_inten);
status = clSetKernelArg(kernel, 1, sizeof(cl_mem), &buf_gamma);
status = clSetKernelArg(kernel, 2, sizeof(cl_mem), &buf_delnu);
status = clSetKernelArg(kernel, 3, sizeof(cl_mem), &buf_nu);
status = clSetKernelArg(kernel, 4, sizeof(cl_mem), &buf_del_air);
status = clSetKernelArg(kernel, 5, sizeof(cl_mem), &buf_f);
// Define index space (global work size) of work items for execution.
// A workgroup size (local work size) is not required, but can be used.
size_t globalWorkSize[2] = {lines, delnu_size};
// Execute the kernel for execution
status = clEnqueueNDRangeKernel(cmdQueue, kernel, 2, NULL, globalWorkSize, NULL, 0, NULL, NULL);
// Read the Device output buffer to the host output array
clEnqueueReadBuffer(cmdQueue, buf_f, CL_TRUE, 0, delnu_siz, f, 0, NULL, NULL);
// Verify the output
FILE *file = fopen("opencl_output","w");
for(i=0;i<delnu_size;i++)
{
fprintf(file, "%le %le\n", delnu[i], f[i]);
}
// Free OpenCL resources
clReleaseKernel(kernel);
clReleaseProgram(program);
clReleaseCommandQueue(cmdQueue);
clReleaseMemObject(buf_nu);
clReleaseMemObject(buf_inten);
clReleaseMemObject(buf_del_air);
clReleaseMemObject(buf_gamma);
clReleaseMemObject(buf_f);
clReleaseMemObject(buf_delnu);
clReleaseContext(context);
// Free host resources
free(nu);
free(inten);
free(gam_air);
free(n_air);
free(del_air);
free(delnu);
free(gamma);
free(f);
free(platforms);
free(devices);
fclose(fp);
fclose(file);
return 0;
}
我做错什么了吗
谢谢。我正在使用AMD W2100,是的,我已经打印出了所有支持的扩展,其中包括cl_khr_fp64扩展 对不起,我忘了包括原来的计算。实际计算如下所示
for(i=0,i<lines;i++)
{
for(j=0;j<delnu_size;j++)
{
f[j] += inten[i] * ((1.0/pie) * (gamma[i] / (pow(gamma[i],2) + pow((delnu[j] - nu[i] + del_air[i] * 950.0/1013.0),2))));
}
}
对于(i=0,i我正在使用AMD W2100,是的,我已经打印出了所有支持的扩展,其中包括cl_khr_fp64扩展
对不起,我忘了包括原始计算。实际计算如下
for(i=0,i<lines;i++)
{
for(j=0;j<delnu_size;j++)
{
f[j] += inten[i] * ((1.0/pie) * (gamma[i] / (pow(gamma[i],2) + pow((delnu[j] - nu[i] + del_air[i] * 950.0/1013.0),2))));
}
}
for(i=0,i您似乎正在运行2D全局工作大小,但存储到仅基于维度1(而非0)的位置。因此,多个工作项正在使用+=”存储到同一位置。您有竞争条件。您可以使用原子解决此问题,但这可能会使性能降低太多。因此,您应该存储中间结果,然后执行并行缩减操作。您似乎正在运行2D全局工作大小,但仅基于维度1(而非0)对位置进行O型环化。因此,使用+=,多个工作项存储到同一位置。您有一个竞争条件。您可以使用原子来解决此问题,但这可能会使性能降低太多。因此,您应该存储中间结果,然后执行并行缩减操作。我将编写OpenCL内核,如下所示,
没有使用原子,只有一个工作维度。
全局工作大小=增量大小
可能有更好的办法,但这是最简单的办法
__kernel void test(__global double *gamma,
__global double *inten,
__global double *delnu,
__global double *delair,
__global double *f,
const int lines)
{
double pie = 4.0*atan(1.0);
int j = get_global_id(0);
f[j] = 0;
for(i=0,i<lines;i++)
{
f[j] += inten[i] * ((1.0/pie) * (gamma[i] / (pow(gamma[i],2) + pow((delnu[j] - nu[i] + del_air[i] * 950.0/1013.0),2))));
}
}
\uuuuu内核无效测试(\uuuu全局双*gamma,
__全球双重目的,
__全球双*delnu,
__全球双倍*delair,
__全球双*f,
常数(整数行)
{
双饼=4.0*atan(1.0);
int j=获取全局id(0);
f[j]=0;
对于(i=0,i我将编写如下的OpenCL内核,
没有使用原子,只有一个工作维度。
全局工作大小=增量大小
可能有更好的办法,但这是最简单的办法
__kernel void test(__global double *gamma,
__global double *inten,
__global double *delnu,
__global double *delair,
__global double *f,
const int lines)
{
double pie = 4.0*atan(1.0);
int j = get_global_id(0);
f[j] = 0;
for(i=0,i<lines;i++)
{
f[j] += inten[i] * ((1.0/pie) * (gamma[i] / (pow(gamma[i],2) + pow((delnu[j] - nu[i] + del_air[i] * 950.0/1013.0),2))));
}
}
\uuuuu内核无效测试(\uuuu全局双*gamma,
__全球双重目的,
__全球双*delnu,
__全球双倍*delair,
__全球双*f,
常数(整数行)
{
双饼=4.0*atan(1.0);
int j=获取全局id(0);
f[j]=0;
对于(i=0,i您使用的是什么GPU?您确定它支持cl_khr_fp64扩展吗?根据OpenCL规范,pown的ULP最低精度为4 ULP。数学库通常精度更高,可能返回正确的四舍五入结果,即0.5ulp。您可以为实际计算添加一些参考代码吗?在内核中el输出写入f[j]
这似乎是错误的,您可能需要原子加法。您使用的是什么GPU?您确定它支持cl_khr_fp64扩展吗?根据OpenCL规范,pown的ULP最低精度为4 ULP。数学库通常精度更高,可能返回正确的四舍五入结果,即0.5ulp。您是否可以添加一些参考值对于您正在进行的实际计算,de?内核内部输出被写入f[j]
这似乎是错误的,您可能需要原子加法。问题是,在原子加法方面,我很难将C转录到OpenCL。我会使用嵌套的forloop来实现这一点,但是,我不知道如何以OpenCL的方式实现。如果您在多任务或线程操作中得到非确定性结果(在GPU上运行它是一种大规模的多线程操作)你可以用一种或另一种方式来判断你是否有竞争条件。你可能在线程隔离、数组寻址方面有一些问题,或者正在尝试多线程执行一个不能多线程的操作。问题是,在原子加法方面,我很难将C转录到OpenCL。我会使用嵌套的forloop对于这一点,我不知道我应该如何用OpenCL的方式来做。如果你在多任务或多线程操作中得到了不确定的结果(并且在GPU上运行它是大量的多线程操作)你可以用一种或另一种方式来判断你是否有竞争条件。你可能在线程隔离、数组寻址方面有一些问题,或者正在尝试多线程执行一个不能多线程的操作。谢谢你的友好回答。我只有一个问题。get_global_id(j)中括号内是不是有一个“j”“?这是打字错误还是有意的?再次谢谢。我还有另一个问题。当谈到‘I’和‘j’的迭代速度时,哪一个更快?我很难理解哪一个是优先级。所以当你看我上面的代码时(嵌套的forloop一个)但是,在我的例子中,“i”的索引显然是“外环”。但是,我应该把“j”循环看作是内循环吗?还是‘i’是内循环?谢谢。谢谢你的友好回答。我只是有一个问题。“?这是打字错误还是有意的?再次谢谢。我还有另一个问题。当谈到‘I’和‘j’的迭代速度时,哪一个更快?我很难理解哪一个是优先级。所以当你看我上面的代码时(嵌套的forloop一个)“i”索引显然是“外环”。但是,在你的例子中,我应该把‘j’循环看作是内循环吗?还是‘i’是内循环?谢谢哟。