OpenCL矩阵乘法的例子

前两篇博客中介绍过矩阵转置的两种方法,矩阵乘法可以先对矩阵做转置运算,然后再对应相乘; 
矩阵大小是65*8192 
先对8192*65的矩阵转置为65*8192; 
然后由65个工作项,每个工作项负责一行数据的乘法; 
完成65行数据的乘法; 
转置是由8192个工作项完成的; 
代码中给出了C语言实现矩阵转置的测试代码; 
感兴趣的朋友可以测试一下C语言代码的运算时间和GPU运算时间的差别; 
下面是代码:

houst.c
#include<stdio.h>
#include<windows.h>
#include<CL/cl.h>
#pragma warning( disable : 4996 )
#define MIXSIZE 8192*65

int main() {
    cl_int error;
    cl_platform_id platforms;
    cl_device_id devices;
    cl_context context;
    FILE *program_handle;
    size_t program_size;
    char *program_buffer;
    cl_program program;
    size_t log_size;
    char *program_log;
    char tKernel_name[] = "mixtran";
    char mKernel_name[] = "mixmul";
    cl_kernel tKernel;
    cl_kernel mKernel;
    cl_command_queue queue;
    //获取平台
    error = clGetPlatformIDs(1, &platforms, NULL);
    if (error != 0) {
        printf("Get platform failed!");
        return -1;
    }
    error = clGetDeviceIDs(platforms, CL_DEVICE_TYPE_GPU, 1, &devices, NULL);
    if (error != 0) {
        printf("Get device failed!");
        return -1;
    }
    //创建上下文
    context = clCreateContext(NULL,1,&devices,NULL,NULL,&error);
    if (error != 0) {
        printf("Creat context failed!");
        return -1;
    }
    //创建程序
    program_handle = fopen("kernel.cl","rb");
    if (program_handle == NULL) {
        printf("The kernle can not be opened!");
        return -1;
    }
    fseek(program_handle,0,SEEK_END);
    program_size = ftell(program_handle);
    rewind(program_handle);

    program_buffer = (char *)malloc(program_size+1);
    program_buffer[program_size] = '\0';
    error=fread(program_buffer,sizeof(char),program_size,program_handle);
    if (error == 0) {
        printf("Read kernel failed!");
        return -1;
    }
    fclose(program_handle);
    program = clCreateProgramWithSource(context,1,(const char **)&program_buffer,&program_size,&error);
    if (error < 0) {
        printf("Couldn't create the program!");
        return -1;
    }
    //编译程序
    error = clBuildProgram(program,1,&devices,NULL,NULL,NULL);
    if (error < 0) {
        //确定日志文件的大小
        clGetProgramBuildInfo(program,devices,CL_PROGRAM_BUILD_LOG,0,NULL,&log_size);
        program_log = (char *)malloc(log_size+1);
        program_log[log_size] = '\0';
        //读取日志
        clGetProgramBuildInfo(program, devices, CL_PROGRAM_BUILD_LOG, log_size+1, program_log, NULL);
        printf("%s\n",program_log);
        free(program_log);
        getchar();
        return -1;
    }
    //创建命令队列
    queue = clCreateCommandQueue(context, devices, CL_QUEUE_PROFILING_ENABLE, &error);
    if (error < 0) {
        printf("Coudn't create the command queue");
        return -1;
    }

    /*本次实验进行矩阵乘法(A*B);
    *****矩阵乘法分为两步进行
    *****矩阵转置
    *****矩阵相乘
    ***********************************
    *****参数说明*************
    **矩阵A=input1
    **矩阵B=input2
    **转置结果为C=input3;
    **相乘的输出结果为result
    **kernel1对B做转置
    **kernel2做乘法
    **矩阵大小65*8192
    */

    //创建内核
    tKernel = clCreateKernel(program,tKernel_name,&error);
    if (tKernel==NULL) {
        printf("Couldn't create kernel!\n");
        return -1;
    }

    mKernel = clCreateKernel(program, mKernel_name, &error);
    if (mKernel == NULL) {
        printf("Couldn't create kernel!\n");
        return -1;
    }

    //创建缓存对象
    cl_mem memObject1 = clCreateBuffer(context,CL_MEM_READ_ONLY ,
                                                                    sizeof(float) * MIXSIZE,NULL,&error);
    if (error < 0) {
        printf("Creat memObject1 failed!\n");
        return -1;
    }
    cl_mem memObject2 = clCreateBuffer(context, CL_MEM_READ_ONLY,
                                                                   sizeof(float) * MIXSIZE, NULL, &error);
    if (error < 0) {
        printf("Creat memObject1 failed!\n");
        return -1;
    }
    cl_mem memObject3 = clCreateBuffer(context, CL_MEM_READ_WRITE , 
                                                                    sizeof(float) * MIXSIZE, NULL, &error);
    if (error < 0) {
        printf("Creat memObject2 failed!\n");
        return -1;
    }
    cl_mem memObject4 = clCreateBuffer(context, CL_MEM_WRITE_ONLY , 
                                                                    sizeof(float) * 65*65, NULL, &error);
    if (error < 0) {
        printf("Creat memObject3 failed!\n");
        return -1;
    }
    //设置内核参数
    error = clSetKernelArg(tKernel,0,sizeof(cl_mem),&memObject1);
    error|= clSetKernelArg(tKernel, 1, sizeof(cl_mem), &memObject3);
    if (error != CL_SUCCESS) {
        printf("Error setting kernel arguments!\n");
        return -1;
    }

    error  = clSetKernelArg(mKernel, 0, sizeof(cl_mem), &memObject2);
    error |= clSetKernelArg(mKernel, 1, sizeof(cl_mem), &memObject3);
    error |= clSetKernelArg(mKernel, 2, sizeof(cl_mem), &memObject4);
    if (error != CL_SUCCESS) {
        printf("Error setting kernel arguments!\n");
        return -1;
    }

    //初始化参数
    float* input1 = (float *)malloc(sizeof(float)*MIXSIZE);
    float* input2 = (float *)malloc(sizeof(float)*MIXSIZE);
    float* input3 = (float *)malloc(sizeof(float)*MIXSIZE);
    float* result = (float *)malloc(sizeof(float)*65*65);
    float* check = (float *)malloc(sizeof(float)*65*65);
    memset(input3, 0, sizeof(float)*MIXSIZE);
    memset(result, 0, sizeof(float)*65*65);
    memset(check, 0, sizeof(float) * 65 * 65);
    //数据读入
    //采用随机数函数产生输入

    //input1是8192*65
    for (int i = 0; i < 8192; i++) {
        srand(i);
        for (int j = 0; j < 65; j++) {
            input1[65 * i + j] = 20 * rand() / (double)(RAND_MAX);
        }
    }
    //input2是65*8192
    for (int i = 0; i < 65; i++) {
        srand(i);
        for (int j = 0; j < 8192; j++) {
            input2[8192 * i + j] = 20 * rand() / (double)(RAND_MAX);
        }
    }
    /*
    iput2*input1得到65*65的矩阵
    先对input1做转置,然后行和行乘加和;
    */

    for (int i = 0; i < 8192; i++) { 
        for (int j = 0; j < 65; j++) {
            input3[j*8192 + i] = input1[i*65 + j];
        }
    }
    for (int i = 0; i < 65; i++) {
        for (int k = 0; k < 65; k++) {
            for (int j = 0; j < 8192; j++) {
                check[i * 65 + k] += input2[j + i * 8192] * input3[j + k * 8192];
            }
        }

    }
    cl_int status = 0;
    cl_event evt1 ;
    cl_event evt2;
    cl_event evt3;

    //数据写入缓冲对像
    error = clEnqueueWriteBuffer(queue, memObject1, CL_FALSE, 0, 
                  MIXSIZE * sizeof(float), input1, 0, NULL, &evt1);
    if (error != CL_SUCCESS) {
        printf("write data failed!\n");
        return -1;
    }

    error = clEnqueueWriteBuffer(queue, memObject2, CL_FALSE, 0,
                 MIXSIZE * sizeof(float), input2, 0, NULL, &evt1);
    if (error != CL_SUCCESS) {
        printf("write data failed!\n");
        return -1;
    }

    //配置工作项(转置)
    size_t maxWorkGroupSize = 0;
    clGetDeviceInfo(devices, CL_DEVICE_MAX_WORK_GROUP_SIZE,
        sizeof(maxWorkGroupSize), &maxWorkGroupSize, NULL);
    size_t globalWorkSize = MIXSIZE / 65;
    size_t localWorkSize = maxWorkGroupSize;
    //执行内核
    error = clEnqueueNDRangeKernel(queue, tKernel, 1, NULL, &globalWorkSize,
                     &localWorkSize, 1, &evt1, &evt2);
    if (error != CL_SUCCESS) {
        printf("Error queuing kernel for execution!\n");
        return -1;
    }

    //配置工作项(乘法)
     globalWorkSize = 65;
     localWorkSize = NULL;
    //执行内核
    error = clEnqueueNDRangeKernel(queue, mKernel, 1, NULL, &globalWorkSize,
        NULL, 1, &evt2, &evt3);
    if (error != CL_SUCCESS) {
        printf("Error queuing kernel for execution!\n");
        return -1;
    }

    //读取内核结果
     error = clEnqueueReadBuffer(queue, memObject4, CL_TRUE, 0,
         65*65 * sizeof(float), result, 1, &evt3, NULL);
     if (error != CL_SUCCESS) {
         printf("Error reading result buffer!\n");
         return -1;
     }

    //检查结果
    for (int i = 0; i < 65*65; i++) {
        if (result[i] != check[i]) { 
            printf("failed!\n");
            printf("%f,%f,%d\n",result[i],check[i],i);
            getchar();
            return 0;
        }
    }
    printf("successed!\n");

    clReleaseProgram(program);
    clReleaseContext(context);
    clReleaseCommandQueue(queue);
    clReleaseDevice(devices);
    clReleaseKernel(tKernel);
    clReleaseKernel(mKernel);

    getchar();
    return 0;
}
kernel.cl
//矩阵转置
__kernel void mixtran(__global const float *input,
    __global  float *inputT) {
    int gid = get_global_id(0);
    for (int i = 0; i < 65; i++) {
        inputT[i*8192+gid] = input[gid*65+i];
    }
}

__kernel void mixmul(__global const float *input1,
    __global const float *input2,
    __global float *result) {
    int gid = get_global_id(0);
    for (int j = 0; j < 65; j ++ ) {
        for (int i = 0; i < 8192; i++) {
            result[gid*65+j] += input1[i + gid * 8192] * input2[i + j * 8192];
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Metal1/article/details/81084147