OpenCL矩阵乘法

时间:2014-02-06 14:01:55

标签: memory opencl matrix-multiplication

我是OpenCL的初学者。我一直在尝试编写矩阵乘法代码。 它工作正常只有它给垃圾值作为C数组的输出。我无法修复错误。 任何帮助将不胜感激。

这是主机和内核代码。

#include <CL/cl.h>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

using namespace std;
#define SUCCESS 0
#define FAILURE 1

// Function to convert file name into a string
int convertToString(const char *filename, std::string &s)
{
    size_t size;
    char *str;
    std::fstream f(filename, (std::fstream::in | std::fstream::binary));

    if (f.is_open())
    {
        size_t fileSize;
        f.seekg(0, std::fstream::end);
        size = fileSize = (size_t)f.tellg();
        f.seekg(0, std::fstream::beg);
        str = new char[size + 1];
        if (!str)
        {
            f.close();
            return 0;
        }

        f.read(str, fileSize);
        f.close();
        str[size] = '\0';
        s = str;
        delete[] str;
        return 0;
    }
    cout << "Error: failed to open file\n:" << filename << endl;
    return FAILURE;
}

int main()
{
    cl_uint status;
    cl_int *error;
    int A[9] = {1, 1, 1, 1, 1, 1, 1, 1, 1};
    int B[9] = {2, 2, 2, 2, 2, 2, 2, 2, 2};
    int C[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
    // Setting up platforms
    cl_platform_id platform = NULL;
    cl_uint numPlatforms = 0;
    // Getting no of platforms
    status = clGetPlatformIDs(0, NULL, &numPlatforms);
    if (status != CL_SUCCESS)
    {
        cout << "\nUnable to query platforms";
        return 0;
    }

    // Get the platform
    if (numPlatforms > 0)
    {
            cl_platform_id*platforms=
                  cl_platform_id*)malloc(numPlatforms*sizeof(cl_platform_id));
            status = clGetPlatformIDs(numPlatforms, platforms, NULL);
            platform = platforms[0];
            free(platforms);
    }

    cl_uint numDevices = 0;
    cl_device_id *devices = NULL;
    status =
        clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, devices, &numDevices);

    if (numDevices == 0)
    {
        cout << "No GPU device available! Choosing CPU.\n";
        status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_CPU, 0, devices,
                                &numDevices);
        devices = (cl_device_id *)malloc(numDevices * sizeof(cl_device_id));
        status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_CPU, numDevices,
                                devices, NULL);
    }

    else
    {
        devices = (cl_device_id *)malloc(numDevices * sizeof(cl_device_id));
        status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, numDevices,
                                devices, NULL);
        if (status == 0)
        {
            cout << "Device error!";
            return 0;
        }
    }

    // Creating contexts

    cl_context context =
        clCreateContext(NULL, 1, devices, NULL, NULL, (cl_int *)status);

    if (status != CL_SUCCESS)
    {
        cout << status;
    }

    // Creating command queues
    cl_command_queue command =
        clCreateCommandQueue(context, devices[0], 0, NULL);
    //  if(error!=CL_SUCCESS)
    //{
    //  cout<<error;
    //}

    // Creating buffers
    cl_mem bufferA = clCreateBuffer(context, CL_MEM_READ_ONLY,
                                    3 * 3 * sizeof(int), NULL, NULL);
    cl_mem bufferB = clCreateBuffer(context, CL_MEM_READ_ONLY,
                                    3 * 3 * sizeof(int), NULL, NULL);
    cl_mem bufferC = clCreateBuffer(context, CL_MEM_WRITE_ONLY,
                                    3 * 3 * sizeof(int), NULL, NULL);

    status = clEnqueueWriteBuffer(command, bufferA, CL_TRUE, 0, 9 * sizeof(int),
                                  (void *)A, 0, NULL, NULL);
    status = clEnqueueWriteBuffer(command, bufferB, CL_TRUE, 0, 9 * sizeof(int),
                                  (void *)B, 0, NULL, NULL);
    // status=clEnqueueReadBuffer(command,bufferA,CL_TRUE,0,9*sizeof(int),(void*)C,0,NULL,NULL);

    const char *filename = "kernel.cl";
    string sourceStr;
    status = convertToString(filename, sourceStr);
    const char *source = sourceStr.c_str();
    size_t sourceSize[] = {strlen(source)};
    cl_program program =
        clCreateProgramWithSource(context, 1, &source, sourceSize, NULL);

    status = clBuildProgram(program, numDevices, 0, NULL, NULL, NULL);
    cl_kernel myKernel = clCreateKernel(program, "multiply", NULL);

    // Setting kernel arguments
    clSetKernelArg(myKernel, 0, sizeof(cl_mem), &bufferC);
    clSetKernelArg(myKernel, 1, sizeof(cl_mem), &bufferA);
    clSetKernelArg(myKernel, 2, sizeof(cl_mem), &bufferB);

    size_t localws[2] = {9, 9};
    size_t globalws[2] = {3, 3};

    status = clEnqueueNDRangeKernel(command, myKernel, 2, NULL, globalws,
                                    localws, 0, NULL, NULL);
    status = clEnqueueReadBuffer(command, bufferC, CL_TRUE, 0, 9 * sizeof(int),
                                 (void *)C, 0, NULL, NULL);

    for (int i = 0; i < 9; i++) cout << C[i] << " ";
    status = clReleaseKernel(myKernel);  // Release kernel.
    status = clReleaseProgram(program);  // Release program object.
    status = clReleaseMemObject(bufferA);  // Release mem object.
    status = clReleaseMemObject(bufferB);
    status = clReleaseMemObject(bufferC);
    status = clReleaseCommandQueue(command);  // Release  Command queue.
    status = clReleaseContext(context);  // Release context.
}

内核代码:

__kernel void multiply(_global int outputC, _global int inputA,
                       _global int inputB)
{
    int row = get_global_id(0);
    int col = get_global_id(1);

    int sum = 0;
    for (int i = 0; i < 3; i++)
        sum += inputA[row * 3 + 1] * inputB[i * 3 + col];

    outputC[row + 3 + col] = sum;
}

1 个答案:

答案 0 :(得分:1)

正如@ Marco13已经指出的那样,内核存在很多问题。

通过clcc之类的工具运行此内核时,您可以看到有许多编译错误开始:

> clcc matmul.cl 
"/tmp/OCLu7FyFF.cl", line 1: error: identifier "_global" is undefined
  __kernel void multiply(_global int outputC, _global int inputA,
                         ^

"/tmp/OCLu7FyFF.cl", line 1: error: invalid combination of type specifiers
  __kernel void multiply(_global int outputC, _global int inputA,
                                 ^

"/tmp/OCLu7FyFF.cl", line 1: error: identifier "_global" is undefined
  __kernel void multiply(_global int outputC, _global int inputA,
                                              ^

"/tmp/OCLu7FyFF.cl", line 1: error: invalid combination of type specifiers
  __kernel void multiply(_global int outputC, _global int inputA,
                                                      ^

"/tmp/OCLu7FyFF.cl", line 2: error: identifier "_global" is undefined
                         _global int inputB)
                         ^

"/tmp/OCLu7FyFF.cl", line 2: error: invalid combination of type specifiers
                         _global int inputB)
                                 ^

6 errors detected in the compilation of "/tmp/OCLu7FyFF.cl".

clcc这样的工具对于及早发现错误非常有用。大多数供应商也有自己的独立内核编译器/检查器版本:例如英特尔有Kernel Builder,AMD的CodeXL包含一个静态内核分析器。另一种选择是直接从主机代码中检索内核编译错误,在clGetProgramBuildInfo返回clBuildProgram之后调用CL_BUILD_PROGRAM_FAILURE来检索编译器输出。

一旦修复了这些编译错误,看起来你的内核仍然没有达到预期效果:如上所述,输入和输出应该是指针,因为你将缓冲区传递给内核。此外,输入和输出数组的索引不正确:在for循环中inputA[row * 3 + 1]应为inputA[row * 3 + i]i而不是1)。将结果保存到outputC时,我希望outputC[row * 3 + col]row * 3)代替row + 3)。

我没有仔细查看主机代码,但我至少要确保,特别是刚开始使用OpenCL时,始终检查每个返回代码和错误。这将为您节省大量时间和挫折。

最后,如果您希望通过实践方法快速学习OpenCL,我强烈建议您参加Simon McIntosh-Smith和Tom Deakin的开源Hands-on OpenCL培训。它不需要很长时间,非常实用,并提供了许多有用的见解。优化矩阵乘法是逐步显示的用例之一。