Kernel 在OpenCL中多次执行同一内核

Kernel 在OpenCL中多次执行同一内核,kernel,opencl,Kernel,Opencl,我想与OpenCL并行运行一个内核(相同的)多次(比如说3次)。我读过类似的标题,但我仍然感到困惑。我已经编写了一个执行过一次的程序,我知道我需要对clEnqueueNDKernelRangeKernel()进行更改,我尝试过,但失败了。所以,请有人告诉我,我怎样才能做到多次。谢谢你的帮助 //Includes #include <stdio.h> #include <stdlib.h> #include <iostream> #ifdef __APPLE_

我想与OpenCL并行运行一个内核(相同的)多次(比如说3次)。我读过类似的标题,但我仍然感到困惑。我已经编写了一个执行过一次的程序,我知道我需要对clEnqueueNDKernelRangeKernel()进行更改,我尝试过,但失败了。所以,请有人告诉我,我怎样才能做到多次。谢谢你的帮助

//Includes
#include <stdio.h>
#include <stdlib.h>
#include <iostream>

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

#define DATA_SIZE 10

using namespace std;

const char *ProgramSource =
"__kernel void add(__global float *inputA, __global float *inputB, __global 
float *inputC, __global float *output)\n"\
"{\n"\
"  size_t id = get_global_id(0);\n"\
"float f;\n"\
"float y1 = 0.0f;\n"\
"y1 = inputA[id] + inputB[id] + inputC[id];\n"\
"  output[id] = y1;\n"\
"}\n";

int main(void)
{
cl_context context;
cl_context_properties properties[3];
cl_kernel kernel;
cl_command_queue command_queue;
cl_program program;
cl_int err;
cl_uint num_of_platforms = 0;
cl_platform_id platform_id;
cl_device_id device_id;
cl_uint num_of_devices = 0;
cl_mem inputA, inputB, inputC, output;

size_t global;

float inputDataA[DATA_SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
float inputDataB[DATA_SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
float inputDataC[DATA_SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };    
float y1[DATA_SIZE] = { 0 };
int i;

// retreive a list of platforms avaible
if (clGetPlatformIDs(1, &platform_id, &num_of_platforms) != CL_SUCCESS)
{
    printf("Unable to get platform_id\n");
    return 1;
}

// try to get a supported GPU device
if (clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_GPU, 1, &device_id, 
&num_of_devices) != CL_SUCCESS)
{
    printf("Unable to get device_id\n");
    return 1;
}

// context properties list - must be terminated with 0
properties[0] = CL_CONTEXT_PLATFORM;
properties[1] = (cl_context_properties)platform_id;
properties[2] = 0;

// create a context with the GPU device
context = clCreateContext(properties, 1, &device_id, NULL, NULL, &err);

// create command queue using the context and device
command_queue = clCreateCommandQueue(context, device_id, 0, &err);

// create a program from the kernel source code
program = clCreateProgramWithSource(context, 1, (const char 
**)&ProgramSource, NULL, &err);

// compile the program
if (clBuildProgram(program, 0, NULL, NULL, NULL, NULL) != CL_SUCCESS)
{
    printf("Error building program\n");
    return 1;
}

// specify which kernel from the program to execute
kernel = clCreateKernel(program, "add", &err);

// create buffers for the input and ouput
inputA = clCreateBuffer(context, CL_MEM_READ_ONLY, sizeof(float) * DATA_SIZE, NULL, NULL);
inputB = clCreateBuffer(context, CL_MEM_READ_ONLY, sizeof(float) * DATA_SIZE, NULL, NULL);
inputC = clCreateBuffer(context, CL_MEM_READ_ONLY, sizeof(float) * DATA_SIZE, NULL, NULL);
output = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(float) * DATA_SIZE, NULL, NULL);

// load data into the input buffer
clEnqueueWriteBuffer(command_queue, inputA, CL_TRUE, 0, sizeof(float) * DATA_SIZE, inputDataA, 0, NULL, NULL);
clEnqueueWriteBuffer(command_queue, inputB, CL_TRUE, 0, sizeof(float) * DATA_SIZE, inputDataB, 0, NULL, NULL);
clEnqueueWriteBuffer(command_queue, inputC, CL_TRUE, 0, sizeof(float) * DATA_SIZE, inputDataC, 0, NULL, NULL);
// set the argument list for the kernel command
clSetKernelArg(kernel, 0, sizeof(cl_mem), &inputA);
clSetKernelArg(kernel, 1, sizeof(cl_mem), &inputB);
clSetKernelArg(kernel, 2, sizeof(cl_mem), &inputC);
clSetKernelArg(kernel, 3, sizeof(cl_mem), &output);

global = DATA_SIZE;

clEnqueueNDRangeKernel(command_queue, kernel, 1, NULL, &global, NULL, 0, NULL, NULL);
clFinish(command_queue);

// copy the results from out of the output buffer
clEnqueueReadBuffer(command_queue, output, CL_TRUE, 0, sizeof(float) *DATA_SIZE, y1, 0, NULL, NULL);

// print the results
printf("y1: ");

for (i = 0; i<DATA_SIZE; i++)
{
    printf("%f\n ", y1[i]);
}

// cleanup - release OpenCL resources
clReleaseMemObject(inputA);
clReleaseMemObject(inputB);
clReleaseMemObject(inputC);
clReleaseMemObject(output);
clReleaseProgram(program);
clReleaseKernel(kernel);
clReleaseCommandQueue(command_queue);
clReleaseContext(context);

return 0;
//包括
#包括
#包括
#包括
#苹果__
#包括
#否则
#包括
#恩迪夫
#定义数据大小为10
使用名称空间std;
常量字符*程序源=
“\uuuu内核void add(\uuuu全局浮点*inputA,\uuuu全局浮点*inputB,\uuu全局
浮点*输入UTC,\全局浮点*输出)\n“\
“{\n”\
“大小\u t id=获取全局\u id(0);\n”\
“浮点f;\n”\
“浮动y1=0.0f;\n”\
“y1=inputA[id]+inputB[id]+InputUTC[id];\n”\
“输出[id]=y1;\n”\
“}\n”;
内部主(空)
{
语境;
cl_上下文_属性[3];
cl_核;
cl_命令_队列命令_队列;
CLU计划;
cl_int err;
平台的cl_uint num_=0;
cl_平台id平台id;
cl_设备id设备id;
设备的数量=0;
cl_mem inputA、inputB、InputUTC、output;
全球规模;
float inputDataA[DATA_SIZE]={1,2,3,4,5,6,7,8,9};
float inputDataB[DATA_SIZE]={1,2,3,4,5,6,7,8,9};
float inputDataC[DATA_SIZE]={1,2,3,4,5,6,7,8,9};
浮点y1[数据大小]={0};
int i;
//检索可用平台的列表
if(clGetPlatformIDs(1,平台id和平台数量)!=CLU成功)
{
printf(“无法获取平台\u id\n”);
返回1;
}
//尝试获取受支持的GPU设备
if(CLGetDeviceID(平台id、CL设备类型、GPU、1和设备id、,
&设备数量)!=CL\u成功)
{
printf(“无法获取设备\u id\n”);
返回1;
}
//上下文属性列表-必须以0终止
属性[0]=CL_上下文_平台;
属性[1]=(上下文属性)平台id;
属性[2]=0;
//使用GPU设备创建上下文
context=clCreateContext(属性、1和设备标识、NULL、NULL和err);
//使用上下文和设备创建命令队列
命令队列=clCreateCommandQueue(上下文、设备id、0和错误);
//从内核源代码创建一个程序
program=clCreateProgramWithSource(上下文,1,(常量字符
**)&ProgramSource、NULL和err);
//编译程序
if(clBuildProgram(程序,0,NULL,NULL,NULL)!=CL\u成功)
{
printf(“错误生成程序\n”);
返回1;
}
//指定要从程序中执行的内核
kernel=clCreateKernel(程序,“添加”&错误);
//为输入和输出创建缓冲区
inputA=clCreateBuffer(上下文,CL_MEM_只读,sizeof(float)*数据大小,NULL,NULL);
inputB=clCreateBuffer(上下文,CL\u MEM\u只读,sizeof(float)*数据大小,NULL,NULL);
inputC=clCreateBuffer(上下文,CL\u MEM\u只读,sizeof(float)*数据大小,NULL,NULL);
输出=clCreateBuffer(上下文,仅CL_MEM_WRITE_,sizeof(float)*数据大小,NULL,NULL);
//将数据加载到输入缓冲区
clEnqueueWriteBuffer(命令队列,输入,CL\u真,0,sizeof(float)*数据大小,输入数据,0,NULL,NULL);
clEnqueueWriteBuffer(命令队列,输入b,CL\u真,0,sizeof(float)*数据大小,输入数据,0,NULL,NULL);
clEnqueueWriteBuffer(命令队列,输入UTC,输入TRUE,0,sizeof(浮点)*数据大小,输入数据C,0,NULL,NULL);
//设置内核命令的参数列表
clSetKernelArg(内核,0,sizeof(cl_mem),&inputA);
clSetKernelArg(内核,1,sizeof(cl_mem)和inputB);
clSetKernelArg(kernel,2,sizeof(cl_mem)和inputC);
clSetKernelArg(kernel,3,sizeof(cl_mem),&output);
全局=数据大小;
clEnqueueNDRangeKernel(命令队列,内核,1,NULL,&global,NULL,0,NULL,NULL);
clFinish(命令队列);
//从输出缓冲区中复制结果
clenqueueredbuffer(命令队列,输出,CL_真,0,sizeof(float)*数据大小,y1,0,NULL,NULL);
//打印结果
printf(“y1:”);

对于(i=0;i,基本上您使用的是命令队列和给定的设备上下文

两件事:

  • 如果您没有特别指定某些内容,那么这些命令将按顺序执行,而不会在队列上独立并行执行。
    • 为每个内核执行创建多个队列,以便在每个队列上独立地并行执行内核(3个队列执行3次)
    • 这种方法对于许多独立的并行执行来说是浪费的。因此,我看不出它有什么好处。然而:OpenCl是关于性能测量的,目的是为了看到这种方法的好处,我不知道你的
  • 最好编写一个函数来执行这些步骤(缓冲区创建、缓冲区填充、参数设置、内核执行请求、从缓冲区读取、缓冲区删除)并多次调用。为了最大限度地提高性能,只需创建一次缓冲区,并在程序结束时将其删除。尽可能经常地重用它们(如果按顺序执行)

最后但并非最不重要的一点:测量性能提升并评估哪种方法最适合您的问题。

“我尝试过但失败了”-您尝试了什么,它是如何失败的?我更改了SetkernelArg()参数,但它不起作用,但现在如果我编写ClenqueueEndKernelRangeKernel()我会得到正确的结果命令3次。但我不确定这3个命令是否独立并行运行。您需要在问题中添加一个选项:如果我们无法复制您的结果,那么我们就很难就需要修复的内容提供建议。我添加了一个简单的数组添加示例,您能告诉我在哪里需要进行更改以运行此ke吗rnel三次并行独立运行,对马有很大帮助,谢谢