OpenCL 管道
▶ 按书上写的管道的代码,需要使用 OpenCL2.0 的平台和设备,目前编译不通过,暂时不知道是什么问题,先把代码堆上来,以后换了新的设备再说
● 程序主要功能:用主机上的数组 srcHost 创建设备缓冲区 src,调用核函数 pipeProducer 将 src 分装到管道中,再调用核函数 pipeConsumer 将管道中的数据读到设备缓冲区 dst 中,最后拷贝回主机数组 dstHost 中检查结果。
● 代码
//pipe.cl
__kernel void pipeProducer(__global float *src, __write_only pipe float outPipe)
{
int gid = get_global_id();
float srcPipe = src[gid];
reserve_id_t resID = reserve_write_pipe(outPipe, );
if (is_valid_reserve_id(resID))
{
if (write_pipe(outPipe, resID, , &srcPipe) != )
return;
commit_write_pipe(outPipe, resID);
}
} __kernel void pipeConsumer(__global float *dst, __read_only pipe float inPipe)
{
int gid = get_global_id();
float dstPipe;
reserve_id_t resID = reserve_read_pipe(inPipe, );
if (is_valid_reserve_id(resID))
{
if (read_pipe(inPipe, resID, , &dstPipe) != )
return;
commit_read_pipe(inPipe, resID);
}
dst[gid] = dstPipe;
}
//main.c
#include <stdio.h>
#include <stdlib.h>
#include <cl.h> const char *sourceCode = "D:/Code/pipe.cl"; char* readSource(const char* kernelPath)// 读取文本文件,存储为 char *
{
FILE *fp;
char *source;
long int size;
//printf("readSource, Program file: %s\n", kernelPath);
fopen_s(&fp, kernelPath, "rb");
if (!fp)
{
printf("Open kernel file failed\n");
exit(-);
}
if (fseek(fp, , SEEK_END) != )
{
printf("Seek end of file faildd\n");
exit(-);
}
if ((size = ftell(fp)) < )
{
printf("Get file position failed\n");
exit(-);
}
rewind(fp);
if ((source = (char *)malloc(size + )) == NULL)
{
printf("Allocate space failed\n");
exit(-);
}
fread(source, , size, fp);
fclose(fp);
source[size] = '\0';
return source;
} int main()
{
const int nPacket = , dataSize = nPacket * sizeof(float);
char info[] = { };
int i; // 初始化平台
cl_int status;
cl_platform_id platform;
clGetPlatformIDs(, &platform, NULL);
cl_device_id device;
clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, , &device, NULL);
cl_context_properties contextProp[] = { CL_CONTEXT_PLATFORM,(cl_context_properties)(platform), };
cl_context context = clCreateContext(contextProp, , &device, NULL, contextProp, &status);
cl_command_queue queue = clCreateCommandQueueWithProperties(context, device, NULL, &status);
cl_event eventProducer, eventConsumer; const char* source = readSource(sourceCode);
cl_program program = clCreateProgramWithSource(context, , &source, NULL, &status);
status = clBuildProgram(program, , &device, "-w -g –cl-std=CL2.0", NULL, NULL); clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, , info, NULL);
printf("Build log:\n%s\n", info); cl_kernel kernelProducer = clCreateKernel(program, "pipeProducer", &status);
cl_kernel kernelConsumer = clCreateKernel(program, "pipeConsumer", &status);
size_t globalSize = nPacket, localSize = ; float *srcHost = (float *)malloc(dataSize);
float *dstHost = (float *)malloc(dataSize);
for (i = ; i < nPacket; srcHost[i] = i, dstHost[i] = 0.0f, i++); cl_mem src, dst;
src = clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, dataSize, srcHost, &status);
dst = clCreateBuffer(context, CL_MEM_WRITE_ONLY, dataSize, NULL, &status); cl_mem pipe = clCreatePipe(context, CL_MEM_HOST_NO_ACCESS, sizeof(float), nPacket, NULL, &status); clSetKernelArg(kernelProducer, , sizeof(cl_mem),src);
clSetKernelArg(kernelProducer, , sizeof(cl_mem), &pipe); clSetKernelArg(kernelProducer, , sizeof(cl_mem), dst);
clSetKernelArg(kernelProducer, , sizeof(cl_mem), &pipe); clEnqueueNDRangeKernel(queue, kernelProducer, , NULL, &globalSize, &localSize, , NULL, &eventProducer);
clEnqueueNDRangeKernel(queue, kernelConsumer, , NULL, &globalSize, &localSize, , &eventProducer, &eventConsumer);
clEnqueueReadBuffer(queue, dst, CL_TRUE, dataSize, dataSize, dstHost, , &eventConsumer, NULL);
clFinish(queue); for (i = ; i < nPacket; i++)
{
if (dstHost[i] != i)
break;
}
printf("Output is %s.\n", (i == nPacket) ? "correct" : "incorrect"); free(srcHost);
free(dstHost);
clReleaseContext(context);
clReleaseCommandQueue(queue);
clReleaseProgram(program);
clReleaseKernel(kernelProducer);
clReleaseKernel(kernelConsumer);
getchar();
return ;
}
● 输出结果
■ 使用编译参数 "-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)使用局部内存来统一工作组的写入
//pipe2.cl
__kernel void pipeProducer(__global float *src, __write_only pipe float outPipe)
{
int gid = get_global_id(), lid = get_local_id();
__local reserve_id_t resID;
if (lid == )
resID = reserve_write_pipe(outPipe, get_local_size()); // 工作组中首个工作项一次预定多个管道位置
barrier(CLK_LOCAL_MEM_FENCE); float srcPipe = src[id];
if (is_valid_reserve_id(resID))
{
if (write_pipe(outPipe, resID, lid, &srcPipe) != ) // 每个工作项写入预定的位置
return;
commit_write_pipe(outPipe, resID);
}
} __kernel void pipeConsumer(__global float *dst, __read_only pipe float inPipe)
{
int gid = get_global_id(), lid = get_local_id();
__local reserve_id_t resID;
if (lid == )
resID = reserve_read_pipe(inPipe, get_local_size());
barrier(CLK_LOCAL_MEM_FENCE); float dstPipe;
if (is_valid_reserve_id(resID))
{
if (read_pipe(inPipe, resID, lid, &dstPipe) != )
return;
commit_read_pipe(inPipe, resID);
}
dst[gid] = dstPipe;
}
● (2)使用工作组管道操作简化上述代码(只是干掉了一个 if 和一个同步)
//pipe3.cl
__kernel void pipeProducer(__global float *src, __write_only pipe float outPipe)
{
int gid = get_global_id(), lid = get_local_id();
__local reserve_id_t resID = work_group_reserve_write_pipe(outPipe, get_local_size());// 自带分支和同步 float srcPipe = src[id];
if (is_valid_reserve_id(resID))
{
if (write_pipe(outPipe, resID, lid, &srcPipe) != )
return;
commit_write_pipe(outPipe, resID);
}
} __kernel void pipeConsumer(__global float *dst, __read_only pipe float inPipe)
{
int gid = get_global_id(), lid = get_local_id();
__local reserve_id_t resID = work_group_reserve_read_pipe(inPipe, get_local_size()); float dstPipe;
if (is_valid_reserve_id(resID))
{
if (read_pipe(inPipe, resID, lid, &dstPipe) != )
return;
commit_read_pipe(inPipe, resID);
}
dst[gid] = dstPipe;
}
● 书上原本的主函数的内容(关于数据缓冲区的部分),是用虚拟内存写的,由于办公室的电脑不支持,上面的代码中被我换成了普通缓冲区
float *src = (float *)clSVMAlloc(context, CL_MEM_READ_WRITE | CL_MEM_SVM_FINE_GRAIN_BUFFER, dataSize, );
float *dst = (float *)clSVMAlloc(context, CL_MEM_READ_WRITE | CL_MEM_SVM_FINE_GRAIN_BUFFER, dataSize, );
if (src == NULL || dst == NULL)
{
printf("clSVMAlloc failed!\n");
getchar();
return ;
} clEnqueueSVMMap(queue, CL_TRUE, CL_MAP_WRITE, src, dataSize, , NULL, NULL);
for (i = ; i < nPacket; i++)
src[i] = i, dst[i] = 0.0f;
clEnqueueSVMUnmap(queue, src, , NULL, NULL); cl_mem pipe = clCreatePipe(context, CL_MEM_HOST_NO_ACCESS, sizeof(float), nPacket, NULL, &status); clSetKernelArgSVMPointer(kernelProducer, , src);
clSetKernelArg(kernelProducer, , sizeof(cl_mem), &pipe); clSetKernelArgSVMPointer(kernelProducer, , dst);
clSetKernelArg(kernelProducer, , sizeof(cl_mem), &pipe); clEnqueueNDRangeKernel(queue, kernelProducer, , NULL, &globalSize, &localSize, , NULL, &eventProducer);
clEnqueueNDRangeKernel(queue, kernelConsumer, , NULL, &globalSize, &localSize, , &eventProducer,NULL);
clFinish(queue); clEnqueueSVMMap(queue, CL_TRUE, CL_MAP_READ, dst, dataSize, , NULL, NULL);
for (i = ; i < nPacket; i++)
{
if (dst[i] != i)
break;
}
printf("Output is %s.\n", (i == nPacket) ? "correct" : "incorrect");
clEnqueueSVMUnmap(queue, dst, , NULL, NULL);
OpenCL 管道的更多相关文章
- 通过 Autostereograms 案例学习 OpenGL 和 OpenCL 的互操作性
引言 在过去的十年里, GPU (图形处理单元)已经从特殊硬件(特供)转变成能够在数值计算领域开辟新篇章的高性能计算机设备. 很多算法能够使用拥有巨大的处理能力的GPU来快速运行和处理大数据量.即使在 ...
- [译]Vulkan教程(13)图形管道基础之Shader模块
[译]Vulkan教程(13)图形管道基础之Shader模块 Shader modules Unlike earlier APIs, shader code in Vulkan has to be s ...
- OpenCL Kernel设计优化
使用Intel® FPGA SDK for OpenCL™ 离线编译器,不需要调整kernel代码便可以将其最佳的适应于固定的硬件设备,而是离线编译器会根据kernel的要求自适应调整硬件的结构. 通 ...
- ASP.NET Core HTTP 管道中的那些事儿
前言 马上2016年就要过去了,时间可是真快啊. 上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情.首先是 NPOI ...
- .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法
.NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法 0x00 为什么需要Map(MapWhen)扩展 如果业务逻辑比较简单的话,一条主管道就够了,确实用不到 ...
- .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类
.NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类 0x00 为什么要引入扩展方法 有的中间件功能比较简单,有的则比较复杂,并且依赖其它组件.除 ...
- .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理
.NET Core中间件的注册和管道的构建(1)---- 注册和构建原理 0x00 问题的产生 管道是.NET Core中非常关键的一个概念,很多重要的组件都以中间件的形式存在,包括权限管理.会话管理 ...
- 【翻译】MongoDB指南/聚合——聚合管道
[原文地址]https://docs.mongodb.com/manual/ 聚合 聚合操作处理数据记录并返回计算后的结果.聚合操作将多个文档分组,并能对已分组的数据执行一系列操作而返回单一结果.Mo ...
- 学习ASP.NET Core, 怎能不了解请求处理管道[6]: 管道是如何随着WebHost的开启被构建出来的?
注册的服务器和中间件共同构成了ASP.NET Core用于处理请求的管道, 这样一个管道是在我们启动作为应用宿主的WebHost时构建出来的.要深刻了解这个管道是如何被构建出来的,我们就必须对WebH ...
随机推荐
- 利用WebApplicationInitializer配置SpringMVC取代web.xml
对于Spring MVC的DispatcherServlet配置方式,传统的是基于XML方式的,也就是官方说明的XML-based,如下: <servlet> <servlet-na ...
- UVA 156:Ananagrams (vector+map+sort)
题意:一大堆单词中间有空格隔开,以'#'结束输出,问只出现一次的的单词有哪些(如果两个具有相同的长度,相同的字母也算是相同的,不区分大小写,如:noel和lone属于一个单词出现两次).最后按照字典序 ...
- 2015 PHP框架调查结果出炉,Laravel最受欢迎!
日前,SitePoint花了一个月时间进行了有关PHP框架使用情况的调查,通过调查结果所示,无论是在团队项目还是个人项目:无论是国家或是年龄层次,Laravel都是使用最多的一款框架. 其中,最流行的 ...
- SharePoint 列表多表联合查询
在SharePoint平台二次开发中,我们有时需要涉及多表关联查询展示多列表中的不同字段信息:SharePoint和Sql数据表一样,也支持多表联合查询,但是不像Sql语句那样简单,有一定的局限性,需 ...
- 《DSP using MATLAB》Problem 3.9
利用的频移性质为: 本习题代码: %% ------------------------------------------------------------------------ %% Outp ...
- 在 Laravel 5 中使用 Repository 模式实现业务逻辑和数据访问的分离
1.概述 首先需要声明的是设计模式和使用的框架以及语言是无关的,关键是要理解设计模式背后的原则,这样才能不管你用的是什么技术,都能够在实践中实现相应的设计模式. 按照最初提出者的介绍,Reposito ...
- HTTP 返回状态消息讲解
当浏览器从 web 服务器请求服务时,可能会发生错误. 从而有可能会返回下面的一系列状态消息: 1xx: 信息 消息: 描述: 100 Continue 服务器仅接收到部分请求,但是一旦服务器并没有拒 ...
- Lock 和 synchronized 的区别
Lock 和 synchronized 的区别 Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现: synchronized在发生异常时,会 ...
- ZedGraph控件的使用
http://blog.chinaunix.net/uid-20776117-id-1847015.html 在我们编写程序的时候,有时候是要做一些统计的,为了达到一目了然的效果,饼状图,曲线图,柱状 ...
- TopCoder客户端安装
参考:https://blog.csdn.net/github_39353095/article/details/76165940 首先,下载 Java 环境. https://www.java.co ...