OpenCL如何更改cl_mem的内存地址?

时间:2016-04-05 03:02:02

标签: opencl matrix-multiplication

我想做一个子矩阵乘法。说我有一个功能:

void MatMul(cl_mem A, cl_mem B, cl_mem C, int M, int K, int N)

其中A是M * K,B是K * N,C是M * N,A,B,C是主机存储器float *h_A, *h_B, *hC传递的所有行主要1维数组,具有以下功能: / p>

void ocl_push_array(cl_mem d_x, float *h_x, int n){
    size_t data_size = sizeof(float)*n;
    err = clEnqueueWriteBuffer(queue, d_x, CL_TRUE, 0, data_size, h_x, 0, NULL, NULL);
}
  

我想问

如果我想进行子矩阵乘法,比如逐行切片:

    // cl_mem A, B, C;
    for(int x=0; x<M; x+=16)
    {
        cl_mem A_sub = (cl_mem)((float *)A+x*K);
        cl_mem C_sub = (cl_mem)((float *)C+x*N);
        if((M-x+1)>=16)
            MatMul(A_sub, B, C_sub, 16, K, N);
        else
            MatMul(A_sub, B, C_sub, M-x+1, K, N);
    }

执行此操作是否是正确的代码?我有一个运行时错误说:"CL_INVALID_MEM_OBJECT" (-38)当它为OpenCL内核(clSetKernelArg)分配参数时。

我想要进行此操作的原因是当我的输入矩阵A和B变大时,我发现矩阵乘法得到了错误的答案。

  

我的OpenCL内核是:

#define BLOCK_SIZE 16

#define AS(i, j) As[j + i * BLOCK_SIZE]
#define BS(i, j) Bs[j + i * BLOCK_SIZE]

__kernel void
matrixMul(__global float* A, __global float* B, __global float* C, 
   __local float* As, __local float* Bs, int uiWA, int uiWB)
{
    int bx = get_group_id(0);
    int by = get_group_id(1);
    int tx = get_local_id(0);
    int ty = get_local_id(1);
    int aBegin = uiWA * BLOCK_SIZE * by;
    int aEnd   = aBegin + uiWA - 1;
    int aStep  = BLOCK_SIZE;
    int bBegin = BLOCK_SIZE * bx;
    int bStep  = BLOCK_SIZE * uiWB;
    float Csub = 0.0f;
    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {
        AS(ty, tx) = A[a + uiWA * ty + tx];
        BS(ty, tx) = B[b + uiWB * ty + tx];
        barrier(CLK_LOCAL_MEM_FENCE); 
        #pragma unroll
        for (int k = 0; k < BLOCK_SIZE; ++k)
        Csub += AS(ty, k) * BS(k, tx);
            barrier(CLK_LOCAL_MEM_FENCE);
        }
        C[get_global_id(1) * get_global_size(0) + get_global_id(0)] = Csub; 
    }

,大小为:

#define BLOCK_SIZE 16

size_t localWorkSize[] = {BLOCK_SIZE, BLOCK_SIZE};
size_t globalWorkSize[] = {shrRoundUp(BLOCK_SIZE, N), shrRoundUp(BLOCK_SIZE, M)};

size_t shrRoundUp(int group_size, int global_size) 
{
    int r = global_size % group_size;
    if(r == 0) 
    {
        return global_size;
    } else 
    {
        return global_size + group_size - r;
    }
}

代码采用Nvidia OpenCL矩阵乘法样本。我的GPU是:英特尔(R)HD Graphics 4600。

谢谢!

1 个答案:

答案 0 :(得分:0)

我认为你不能这样做:

cl_mem A_sub = (cl_mem)((float *)A+x*K);

因为cl_mem是OpenCL中的一个对象,它实际上是一个复杂的数据结构,而不仅仅是一个数据指针。它维护诸如指向实际内存的数据指针,对象引用,内存属性等信息。不同的运行时间甚至可能具有不同的cl_mem对象实现。这就是您收到CL_INVALID_MEM_OBJECT错误消息的原因。

如何为子矩阵获取所需数据是以下之一:

  1. 定义两个新的cl_mem对象,并使用单独的内核来完成 复制工作。

  2. 使用clEnqueueCopyBuffer函数复制主机上的数据 代码域。

  3. 使用CL_MEM_ALLOC_HOST_PTR到内存缓冲区,然后使用 clEnqueueMapBuffer将GPU内存映射到主机内存指针,和 然后使用映射的主机内存修改内存内容 指针,完成后,取消映射指针以返回GPU内存 到设备内存域。