【问题标题】:opencl is giving wrong results for double data type?opencl 对双数据类型给出错误的结果?
【发布时间】:2014-02-07 21:24:22
【问题描述】:

我对 OpenCl 很陌生。

问题:我正在初始化一个包含 10 个元素的数组,其值为 -1 在 Host 中。我将相同的数组作为输入传递给设备并递增每个元素,然后通过缓冲区接收回数组作为输出。再一次,将相同的输出数组作为输入发送回主机并接收增加的输出缓冲区。这是循环完成的。

问题:当我将数组的数据类型设为浮点/整数时,程序运行正常。但是当我将数据类型作为数组的两倍时,它并没有给我想要的结果。 请建议我在哪里失踪?任何帮助/建议/关键字都会有很大帮助。提前致谢。

数组浮点数据类型代码:

#include <stdio.h>
#include <stdlib.h> 
#ifdef __APPLE__
#include <OpenCL/opencl.h>
#else
#include <CL/cl.h>
#endif


#define MEM_SIZE (10)
#define MAX_SOURCE_SIZE (0x100000)


int main() {
    float input[MEM_SIZE], output[MEM_SIZE];
    int go, i;
    for (i = 0; i < MEM_SIZE; i++) {
        input[i] = -1.0;
        output[i] = -1.0;
    }
    FILE *fp;
    cl_device_id device_id = NULL;
    cl_context context = NULL;
    cl_command_queue command_queue = NULL;
    cl_program program = NULL;
    cl_kernel kernel = NULL;
    cl_platform_id platform_id = NULL;
    cl_uint ret_num_devices;
    cl_uint ret_num_platforms;
    cl_int ret;
    size_t source_size;
    char *source_str;
    fp = fopen("calc_float.cl", "r");
    if (!fp) {
        fprintf(stderr, "Failed to load kernel.\n");
        exit(1);
    }
    source_str = (char*)malloc(MAX_SOURCE_SIZE);
    source_size = fread(source_str, 1, MAX_SOURCE_SIZE, fp);
    fclose(fp);


    /*Initialization*/
    /* Get Platform and Device Info */
    ret = clGetPlatformIDs(1, &platform_id, &ret_num_platforms);
    ret = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_DEFAULT, 1, &device_id, &ret_num_devices);

    /* Create OpenCL context */
    context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &ret);

    /* Create Command Queue */
    command_queue = clCreateCommandQueue(context, device_id, 0, &ret);

    /*Initialization complete*/

    cl_mem inputBuffer = clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, MEM_SIZE * sizeof(float),(void *) input, NULL);
    cl_mem outputBuffer = clCreateBuffer(context, CL_MEM_WRITE_ONLY , MEM_SIZE * sizeof(float), NULL, NULL);


    /* Create Kernel Program from the source */
    program = clCreateProgramWithSource(context, 1, (const char **)&source_str,(const size_t *)&source_size, &ret);

    /* Build Kernel Program */
    ret = clBuildProgram(program, 1, &device_id, NULL, NULL, NULL);

    /* Create OpenCL Kernel */
    kernel = clCreateKernel(program, "calc", &ret);

    /* Set OpenCL Kernel Parameters */
    ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&inputBuffer);
    ret = clSetKernelArg(kernel, 1, sizeof(cl_mem), (void *)&outputBuffer);

    /* Execute OpenCL Kernel */
    ret = clEnqueueTask(command_queue, kernel, 0, NULL,NULL);
    double x = 10, io;
    size_t global_work_size[1] = {MEM_SIZE};
    for (io = 0; io < x; io++) {
        inputBuffer = clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, MEM_SIZE * sizeof(float),(void *) output, NULL);
        ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&inputBuffer);
        ret = clEnqueueNDRangeKernel(command_queue, kernel, 1, NULL, global_work_size, NULL, 0, NULL, NULL);

        ret = clEnqueueReadBuffer(command_queue, outputBuffer, CL_TRUE, 0, MEM_SIZE * sizeof(float), output, 0, NULL, NULL);
        for (go = 0; go < MEM_SIZE; go++) {
            printf("output[%d] = %f\n",io, go, output[go]);
        }
        printf("\n\n");

    }


    /* Finalization */
    ret = clFlush(command_queue);
    ret = clFinish(command_queue);
    ret = clReleaseKernel(kernel);
    ret = clReleaseProgram(program);
    ret = clReleaseMemObject(inputBuffer);
    ret = clReleaseMemObject(outputBuffer);
    ret = clReleaseCommandQueue(command_queue);
    ret = clReleaseContext(context);

    return 1;
}

以上代码的 calc_float.cl 文件:

__kernel void calc(__global float* in, __global float* out)
{
    int i;
    for (i = 0; i < 10; i++) {
        out[i] = in[i] + 1;
    }
}

数组双精度数据类型代码:

#include <stdio.h>
#include <stdlib.h> 
#ifdef __APPLE__
#include <OpenCL/opencl.h>
#else
#include <CL/cl.h>
#endif

#define MEM_SIZE (10)
#define MAX_SOURCE_SIZE (0x100000)


int main() {
    double input[MEM_SIZE], output[MEM_SIZE];
    int go, i;
    for (i = 0; i < MEM_SIZE; i++) {
        input[i] = -1.0;
        output[i] = -1.0;
    }
    FILE *fp;
    cl_device_id device_id = NULL;
    cl_context context = NULL;
    cl_command_queue command_queue = NULL;
    cl_program program = NULL;
    cl_kernel kernel = NULL;
    cl_platform_id platform_id = NULL;
    cl_uint ret_num_devices;
    cl_uint ret_num_platforms;
    cl_int ret;
    size_t source_size;
    char *source_str;
    fp = fopen("calc_double.cl", "r");
    if (!fp) {
        fprintf(stderr, "Failed to load kernel.\n");
        exit(1);
    }
    source_str = (char*)malloc(MAX_SOURCE_SIZE);
    source_size = fread(source_str, 1, MAX_SOURCE_SIZE, fp);
    fclose(fp);


    /*Initialization*/
    /* Get Platform and Device Info */
    ret = clGetPlatformIDs(1, &platform_id, &ret_num_platforms);
    ret = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_DEFAULT, 1, &device_id, &ret_num_devices);

    /* Create OpenCL context */
    context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &ret);

    /* Create Command Queue */
    command_queue = clCreateCommandQueue(context, device_id, 0, &ret);

    /*Initialization complete*/

    cl_mem inputBuffer = clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, MEM_SIZE * sizeof(double),(void *) input, NULL);
    cl_mem outputBuffer = clCreateBuffer(context, CL_MEM_WRITE_ONLY , MEM_SIZE * sizeof(double), NULL, NULL);


    /* Create Kernel Program from the source */
    program = clCreateProgramWithSource(context, 1, (const char **)&source_str,(const size_t *)&source_size, &ret);

    /* Build Kernel Program */
    ret = clBuildProgram(program, 1, &device_id, NULL, NULL, NULL);

    /* Create OpenCL Kernel */
    kernel = clCreateKernel(program, "calc", &ret);

    /* Set OpenCL Kernel Parameters */
    ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&inputBuffer);
    ret = clSetKernelArg(kernel, 1, sizeof(cl_mem), (void *)&outputBuffer);

    /* Execute OpenCL Kernel */
    ret = clEnqueueTask(command_queue, kernel, 0, NULL,NULL);
    double x = 10, io;
    size_t global_work_size[1] = {MEM_SIZE};
    for (io = 0; io < x; io++) {
        inputBuffer = clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, MEM_SIZE * sizeof(double),(void *) output, NULL);
        ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&inputBuffer);
        ret = clEnqueueNDRangeKernel(command_queue, kernel, 1, NULL, global_work_size, NULL, 0, NULL, NULL);

        ret = clEnqueueReadBuffer(command_queue, outputBuffer, CL_TRUE, 0, MEM_SIZE * sizeof(double), output, 0, NULL, NULL);
        for (go = 0; go < MEM_SIZE; go++) {
            printf("output[%d] = %lf\n",io, go, output[go]);
        }
        printf("\n\n");

    }


    /* Finalization */
    ret = clFlush(command_queue);
    ret = clFinish(command_queue);
    ret = clReleaseKernel(kernel);
    ret = clReleaseProgram(program);
    ret = clReleaseMemObject(inputBuffer);
    ret = clReleaseMemObject(outputBuffer);
    ret = clReleaseCommandQueue(command_queue);
    ret = clReleaseContext(context);

    return 1;
}

以上代码的calc_double.cl文件:

__kernel void calc(__global double* in, __global double* out)
{
    int i;
    for (i = 0; i < 10; i++) {
        out[i] = in[i] + 1;
    }
}

程序编译:

gcc program.c -o doublesimulation -l OpenCL -I /usr/local/test/AMD-APP-SDK-v2.9-RC-lnx32/include/ -L /usr/local/test/AMD-APP-SDK-v2.9-RC-lnx32/lib/x86

我也尝试添加以下检查,但没有帮助:

#ifdef cl_khr_fp64
    #pragma OPENCL EXTENSION cl_khr_fp64 : enable
#elif defined(cl_amd_fp64)
    #pragma OPENCL EXTENSION cl_amd_fp64 : enable
#else
    #error "Double precision floating point not supported by OpenCL implementation."
#endif

操作系统:Centos5 32bit AMD显卡

【问题讨论】:

  • clEnqueueReadBuffer 执行成功了吗?始终检查 OpenCL API 的错误,这些都是潜在的帮助者。

标签: c opencl


【解决方案1】:

检查您的 GPU 是否支持双精度:

cl_uint native_double_width;    
clGetDeviceInfo(device_id, CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE, sizeof(cl_uint), &native_double_width, NULL);

if(native_double_width == 0){
    printf("No double precision support.\n");
}

如果原生双向量宽度等于 0,则不支持双精度 (clGetDeviceInfo description)

【讨论】:

  • 完全同意,使用 OpenCL 女巫是与 GPU 的通信 API,不检查返回值是完全不安全的。就像使用套接字并丢弃返回值一样。
  • 使用此方法的一个小警告是 CL_DEVICE_NATIVE_VECTOR_WIDTH_* 常量仅在标准的 1.1 版中添加,因此 1.0 版的实现应该会失败。检查双精度支持以及通常是否存在任何扩展的正确方法是检查为平台和设备返回的扩展。诚然,这是一个小问题,因为我不知道任何当前的 OpenCL 实现都停留在 1.0 版上,但我很迂腐。
  • 感谢指正。方法,必须适用于每个 OpenCL 设备:检查 cl_khr_fp64 扩展,不要被供应商特定的扩展混淆,例如。 G。 cl_amd_fp64.
  • 谢谢@RomanArzumanyan.. 我的 native_double_width 值即将变为 1。这表明它支持双精度。我在代码中遗漏了什么吗?因为 float 和 int 数据类型返回完美。可能是我错过了双倍的东西吗?很抱歉回复晚了。
  • 我在您的代码中看到的第一件事 - 您无需等待内核完成。要么等待命令队列完成,要么(更好)等待事件。内核正在使用的读取缓冲区未定义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多