OpenCL 管道

Posted cuancuancuanhao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCL 管道相关的知识,希望对你有一定的参考价值。

? 按书上写的管道的代码,就算使用支持 OpenCL2.0 的平台和设备,编译器还是报错曰 ”不支持修饰符 pipe“,暂时不知道是什么问题,先把代码堆上来,以后换了新的设备再说

● 程序主要功能:用主机上的数组 srcHost 创建设备缓冲区 src,调用核函数 pipeProducer 将 src 分装到管道中,再调用核函数 pipeConsumer 将管道中的数据读到设备缓冲区 dst 中,最后拷贝回主机数组 dstHost 中检查结果。

● 代码

 1 //pipe.cl
 2 __kernel void pipeProducer(__global float *src, __write_only pipe float outPipe)
 3 {
 4     int gid = get_global_id(0);
 5     float srcPipe = src[id];
 6     reserve_id_t resID = reserve_write_pipe(outPipe, 1);
 7     if (is_valid_reserve_id(resID))
 8     {
 9         if (write_pipe(outPipe, resID, 0, &srcPipe) != 0)
10             return;
11         commit_write_pipe(outPipe, resID);
12     }
13 }
14 
15 __kernel void pipeConsumer(__global float *dst, __read_only pipe float inPipe)
16 {
17     int gid = get_global_id(0);
18     float dstPipe;
19     reserve_id_t resID = reserve_read_pipe(inPipe, 1);
20     if (is_valid_reserve_id(resID))
21     {
22         if (read_pipe(inPipe, resID, 0, &dstPipe) != 0)
23             return;
24         commit_read_pipe(inPipe, resID);
25     }
26     dst[gid] = dstPipe;
27 }
  1 //main.c
  2 #include <stdio.h>  
  3 #include <stdlib.h>  
  4 #include <cl.h>
  5 
  6 const char *sourceCode = "D:/Code/pipe.cl";
  7 
  8 char* readSource(const char* kernelPath)// 读取文本文件,存储为 char *
  9 {
 10     FILE *fp;
 11     char *source;
 12     long int size;
 13     //printf("readSource, Program file: %s\n", kernelPath);
 14     fopen_s(&fp, kernelPath, "rb");
 15     if (!fp)
 16     {
 17         printf("Open kernel file failed\n");
 18         exit(-1);
 19     }
 20     if (fseek(fp, 0, SEEK_END) != 0)
 21     {
 22         printf("Seek end of file faildd\n");
 23         exit(-1);
 24     }
 25     if ((size = ftell(fp)) < 0)
 26     {
 27         printf("Get file position failed\n");
 28         exit(-1);
 29     }
 30     rewind(fp);
 31     if ((source = (char *)malloc(size + 1)) == NULL)
 32     {
 33         printf("Allocate space failed\n");
 34         exit(-1);
 35     }
 36     fread(source, 1, size, fp);
 37     fclose(fp);
 38     source[size] = \0;
 39     return source;
 40 }
 41 
 42 int main()
 43 {
 44     const int nPacket = 1024, dataSize = nPacket * sizeof(float);
 45     char info[1024] = { 0 };
 46     int i;
 47 
 48     // 初始化平台
 49     cl_int status;    
 50     cl_platform_id platform;
 51     clGetPlatformIDs(1, &platform, NULL);    
 52     cl_device_id device;
 53     clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL);
 54     cl_context_properties contextProp[] = { CL_CONTEXT_PLATFORM,(cl_context_properties)(platform), 0 };
 55     cl_context context = clCreateContext(contextProp, 1, &device, NULL, contextProp, &status);
 56     cl_command_queue queue = clCreateCommandQueueWithProperties(context, device, NULL, &status);    
 57     cl_event eventProducer, eventConsumer; 
 58 
 59     const char* source = readSource(sourceCode);
 60     cl_program program = clCreateProgramWithSource(context, 1, &source, NULL, &status);    
 61     status = clBuildProgram(program, 1, &device, "-w -g –cl-std=CL2.0", NULL, NULL);
 62 
 63     clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 1024, info, NULL);
 64     printf("Build log:\n%s\n", info);
 65 
 66     cl_kernel kernelProducer = clCreateKernel(program, "pipeProducer", &status);
 67     cl_kernel kernelConsumer = clCreateKernel(program, "pipeConsumer", &status);
 68     size_t globalSize = nPacket, localSize = 128;
 69 
 70     float *srcHost = (float *)malloc(dataSize);
 71     float *dstHost = (float *)malloc(dataSize);
 72     for (i = 0; i < nPacket; srcHost[i] = i, dstHost[i] = 0.0f, i++);
 73         
 74     cl_mem src, dst;
 75     src = clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, dataSize, srcHost, &status);
 76     dst = clCreateBuffer(context, CL_MEM_WRITE_ONLY, dataSize, NULL, &status);
 77    
 78     cl_mem pipe = clCreatePipe(context, CL_MEM_HOST_NO_ACCESS, sizeof(float), nPacket, NULL, &status);
 79 
 80     clSetKernelArg(kernelProducer, 0, sizeof(cl_mem),src);
 81     clSetKernelArg(kernelProducer, 1, sizeof(cl_mem), &pipe);
 82 
 83     clSetKernelArg(kernelProducer, 0, sizeof(cl_mem), dst);
 84     clSetKernelArg(kernelProducer, 1, sizeof(cl_mem), &pipe);
 85     
 86     clEnqueueNDRangeKernel(queue, kernelProducer, 1, NULL, &globalSize, &localSize, 0, NULL, &eventProducer);
 87     clEnqueueNDRangeKernel(queue, kernelConsumer, 1, NULL, &globalSize, &localSize, 1, &eventProducer, &eventConsumer);        
 88     clEnqueueReadBuffer(queue, dst, CL_TRUE, dataSize, dataSize, dstHost, 1, &eventConsumer, NULL);
 89     clFinish(queue);
 90 
 91     for (i = 0; i < nPacket; i++)
 92     {
 93         if (dstHost[i] != i)
 94             break;
 95     }
 96     printf("Output is %s.\n", (i == nPacket) ? "correct" : "incorrect");
 97     clEnqueueSVMUnmap(queue, dst, 0, NULL, NULL);
 98 
 99     free(srcHost);
100     free(dstHost);
101     clReleaseContext(context);    
102     clReleaseCommandQueue(queue);
103     clReleaseProgram(program);
104     clReleaseKernel(kernelProducer);
105     clReleaseKernel(kernelConsumer);
106     getchar();
107     return 0;
108 }

● 输出结果

■ 使用编译参数 "-w -g –cl-std=CL2.0" 时返回 status 为 -43(CL_INVALID_BUILD_OPTIONS),不使用参数 "–cl-std=CL2.0" 的情况下返回 -11(CL_BUILD_PROGRAM_FAILURE),麻烦的是调用函数 clGetProgramBuildInfo 查询编译日志 info 始终都是空的,不知道出了什么问题。

■ 转机,去掉了修饰符 __write_only 和 __read_only(只用于图像类型的缓冲区),返回 status 为 -11,至少报错信息有了:【identifier "pipe" is undefined】和【invalid combination of type specifiers】(指在 float 上)

 

● 后续代码,但是上述代码都编译不了,下面的也暂时没用。(1)使用局部内存来统一工作组的写入

 1 //pipe2.cl
 2 __kernel void pipeProducer(__global float *src, __write_only pipe float outPipe)
 3 {
 4     int gid = get_global_id(0), lid = get_local_id(0);
 5     __local reserve_id_t resID;
 6     if (lid == 0)
 7         resID = reserve_write_pipe(outPipe, get_local_size(0)); // 工作组中首个工作项一次预定多个管道位置
 8     barrier(CLK_LOCAL_MEM_FENCE);
 9 
10     float srcPipe = src[id];
11     if (is_valid_reserve_id(resID))
12     {
13         if (write_pipe(outPipe, resID, lid, &srcPipe) != 0)     // 每个工作项写入预定的位置
14             return;
15         commit_write_pipe(outPipe, resID);
16     }
17 }
18 
19 __kernel void pipeConsumer(__global float *dst, __read_only pipe float inPipe)
20 {
21     int gid = get_global_id(0), lid = get_local_id(0);    
22     __local reserve_id_t resID;
23     if (lid == 0)
24         resID = reserve_read_pipe(inPipe, get_local_size(0));
25     barrier(CLK_LOCAL_MEM_FENCE);
26     
27     float dstPipe;
28     if (is_valid_reserve_id(resID))
29     {
30         if (read_pipe(inPipe, resID, lid, &dstPipe) != 0)
31             return;
32         commit_read_pipe(inPipe, resID);
33     }
34     dst[gid] = dstPipe;
35 }

● (2)使用工作组管道操作简化上述代码(只是干掉了一个 if 和一个同步)

 1 //pipe3.cl
 2 __kernel void pipeProducer(__global float *src, __write_only pipe float outPipe)
 3 {
 4     int gid = get_global_id(0), lid = get_local_id(0);
 5     __local reserve_id_t resID = work_group_reserve_write_pipe(outPipe, get_local_size(0));// 自带分支和同步
 6 
 7     float srcPipe = src[id];
 8     if (is_valid_reserve_id(resID))
 9     {
10         if (write_pipe(outPipe, resID, lid, &srcPipe) != 0)
11             return;
12         commit_write_pipe(outPipe, resID);
13     }
14 }
15 
16 __kernel void pipeConsumer(__global float *dst, __read_only pipe float inPipe)
17 {
18     int gid = get_global_id(0), lid = get_local_id(0);    
19     __local reserve_id_t resID = work_group_reserve_read_pipe(inPipe, get_local_size(0));    
20     
21     float dstPipe;
22     if (is_valid_reserve_id(resID))
23     {
24         if (read_pipe(inPipe, resID, lid, &dstPipe) != 0)
25             return;
26         commit_read_pipe(inPipe, resID);
27     }
28     dst[gid] = dstPipe;
29 }

 ● 书上原本的主函数的内容(关于数据缓冲区的部分),是用虚拟内存写的,由于办公室的电脑不支持,上面的代码中被我换成了普通缓冲区

 1     float *src = (float *)clSVMAlloc(context, CL_MEM_READ_WRITE | CL_MEM_SVM_FINE_GRAIN_BUFFER, dataSize, 0);
 2     float *dst = (float *)clSVMAlloc(context, CL_MEM_READ_WRITE | CL_MEM_SVM_FINE_GRAIN_BUFFER, dataSize, 0);
 3     if (src == NULL || dst == NULL)
 4     {
 5         printf("clSVMAlloc failed!\n");
 6         getchar();
 7         return 0;
 8     }
 9 
10     clEnqueueSVMMap(queue, CL_TRUE, CL_MAP_WRITE, src, dataSize, 0, NULL, NULL);
11     for (i = 0; i < nPacket; i++)
12         src[i] = i, dst[i] = 0.0f;
13     clEnqueueSVMUnmap(queue, src, 0, NULL, NULL);
14 
15     cl_mem pipe = clCreatePipe(context, CL_MEM_HOST_NO_ACCESS, sizeof(float), nPacket, NULL, &status);
16 
17     clSetKernelArgSVMPointer(kernelProducer, 0, src);
18     clSetKernelArg(kernelProducer, 1, sizeof(cl_mem), &pipe);
19 
20     clSetKernelArgSVMPointer(kernelProducer, 0, dst);
21     clSetKernelArg(kernelProducer, 1, sizeof(cl_mem), &pipe);
22     
23     clEnqueueNDRangeKernel(queue, kernelProducer, 1, NULL, &globalSize, &localSize, 0, NULL, &eventProducer);
24     clEnqueueNDRangeKernel(queue, kernelConsumer, 1, NULL, &globalSize, &localSize, 1, &eventProducer,NULL);
25     clFinish(queue);
26     
27     clEnqueueSVMMap(queue, CL_TRUE, CL_MAP_READ, dst, dataSize, 0, NULL, NULL);    
28     for (i = 0; i < nPacket; i++)
29     {
30         if (dst[i] != i)
31             break;
32     }
33     printf("Output is %s.\n", (i == nPacket) ? "correct" : "incorrect");
34     clEnqueueSVMUnmap(queue, dst, 0, NULL, NULL);

 

以上是关于OpenCL 管道的主要内容,如果未能解决你的问题,请参考以下文章

使用 FFmpeg 通过管道输出视频片段

r 计算管道的步骤(基本片段)

15种Python片段去优化你的数据科学管道

如何在降价表的代码语句中转义管道字符?

从 GPU 获取 OpenCL 程序代码

使用 Visual Studio 2010 在 Nvidia GEFORCE 上的 OpenCL 代码