OpenCL多次循环执行内核的一个简单样例
最近有不少朋友在多次循环执行OpenCL内核程序的时候碰到一些问题。由于对OpenCL初学者而言可能比较普遍,因此我这里给出一个清晰简单的demo来掩饰如何简单又高效地执行循环执行OpenCL内核。
以下程序的大概意思与流程是:
内核程序含有两个参数,第一个参数既是输入又是输出,第二个参数仅仅用于输入。不过第一个参数只对其初始化一次,而第二个参数在每次循环执行新一次的内核程序前会再传递一次数据。这么做有助于同学更好地去理解、把握存储器对象的基本使用方法。
存储器对象在通过cl_context上下文创建完之后,其所在的GPU端的位置就不变了。因此,我们在循环执行内核程序之前不需要把存储器对象释放掉,然后重新分配。这么做就比较低效了。我们完全可以重用同一个存储器对象。
以下代码在我的MacBook Air上能完全通过编译执行。没有任何warning。
执行环境:基于Haswell微架构的Intel Core i7 4650U,Intel HD Graphics 5000,8GB DDR3L,128GB SSD。
OS X 10.9.2 Mavericks,Xcode 5.1,Apple LLVM 5.1,支持GNU11标准的C编译器。
#include <stdio.h>
#include <string.h>
#include <stdlib.h> #ifdef __APPLE__
#include <OpenCL/opencl.h>
#else
#include <CL/cl.h>
#endif int main(void)
{
cl_int ret; cl_platform_id platform_id = NULL;
cl_device_id device_id = NULL;
cl_context context = NULL;
cl_command_queue command_queue = NULL;
cl_mem memObj1 = NULL;
cl_mem memObj2 = NULL;
char *kernelSource = NULL;
cl_program program = NULL;
cl_kernel kernel = NULL;
int *pInputBuffer1 = NULL;
int *pInputBuffer2 = NULL;
int *pOutputBuffer = NULL; clGetPlatformIDs(, &platform_id, NULL);
if(platform_id == NULL)
{
puts("Get OpenCL platform failed!");
goto FINISH;
} clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_GPU, , &device_id, NULL);
if(device_id == NULL)
{
puts("No GPU available as a compute device!");
goto FINISH;
} context = clCreateContext(NULL, , &device_id, NULL, NULL, &ret);
if(context == NULL)
{
puts("Context not established!");
goto FINISH;
} command_queue = clCreateCommandQueue(context, device_id, , &ret);
if(command_queue == NULL)
{
puts("Command queue cannot be created!");
goto FINISH;
} // Specify the path of the kernel source
const char *pFileName = "/Users/zennychen/Downloads/test.cl"; FILE *fp = fopen(pFileName, "r");
if (fp == NULL)
{
puts("The specified kernel source file cannot be opened!");
goto FINISH;
}
fseek(fp, , SEEK_END);
const long kernelLength = ftell(fp);
fseek(fp, , SEEK_SET); kernelSource = malloc(kernelLength); fread(kernelSource, , kernelLength, fp);
fclose(fp); program = clCreateProgramWithSource(context, , (const char**)&kernelSource, (const size_t*)&kernelLength, &ret);
ret = clBuildProgram(program, , &device_id, NULL, NULL, NULL);
if (ret != CL_SUCCESS)
{
size_t len;
char buffer[ * ]; printf("Error: Failed to build program executable!\n");
clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, sizeof(buffer), buffer, &len);
printf("%s\n", buffer);
goto FINISH;
} kernel = clCreateKernel(program, "test", &ret);
if(kernel == NULL)
{
puts("Kernel failed to create!");
goto FINISH;
} const size_t contentLength = sizeof(*pInputBuffer1) * * ; // 这里预分配的缓存大小为4MB,第一个参数是读写的
memObj1 = clCreateBuffer(context, CL_MEM_READ_WRITE, contentLength, NULL, &ret);
if(memObj1 == NULL)
{
puts("Memory object1 failed to create!");
goto FINISH;
} // 这里预分配的缓存大小为4MB,第一个参数是只读的
memObj2 = clCreateBuffer(context, CL_MEM_READ_ONLY, contentLength, NULL, &ret);
if(memObj1 == NULL)
{
puts("Memory object2 failed to create!");
goto FINISH;
} ret = clSetKernelArg(kernel, , sizeof(cl_mem), (void *)&memObj1);
ret |= clSetKernelArg(kernel, , sizeof(cl_mem), (void *)&memObj2); if(ret != CL_SUCCESS)
{
puts("Set arguments error!");
goto FINISH;
} // 以下为在主机端分配输入缓存
pInputBuffer1 = malloc(contentLength);
pInputBuffer2 = malloc(contentLength); // 然后对此工作缓存进行初始化
for(int i = ; i < * ; i++)
pInputBuffer1[i] = i + ; memset(pInputBuffer2, , contentLength); // 然后分配输出缓存
pOutputBuffer = malloc(contentLength); // 先将第一个参数的数据传入GPU端,以后就不去改动了
ret = clEnqueueWriteBuffer(command_queue, memObj1, CL_TRUE, , contentLength, pInputBuffer1, , NULL, NULL);
if(ret != CL_SUCCESS)
{
puts("Data transfer failed");
goto FINISH;
} int count = ; // 执行5次循环 do
{
// 先将第二个参数传给GPU
ret = clEnqueueWriteBuffer(command_queue, memObj2, CL_TRUE, , contentLength, pInputBuffer2, , NULL, NULL);
if(ret != CL_SUCCESS)
{
puts("Data transfer failed");
goto FINISH;
} // 这里指定将总共有1024 * 1024个work-item
ret = clEnqueueNDRangeKernel(command_queue, kernel, , NULL, (const size_t[]){ * }, NULL, , NULL, NULL); // 将结果拷贝给主机端
ret = clEnqueueReadBuffer(command_queue, memObj1, CL_TRUE, , contentLength, pOutputBuffer, , NULL, NULL); // 做次同步,这里偷懒,不用wait event机制了~
clFinish(command_queue); // 做校验
const int newValue = - count + ;
const int addition = ( - count) * newValue / ;
for(int i = ; i < * ; i++)
{
if(pOutputBuffer[i] != i + + addition)
{
puts("Result error!");
break;
}
} // 最后,给第二个缓存初始化新数据
for(int i = ; i < * ; i++)
pInputBuffer2[i] = newValue;
}
while(--count > ); FINISH: /* Finalization */
if(pInputBuffer1 != NULL)
free(pInputBuffer1);
if(pInputBuffer2 != NULL)
free(pInputBuffer2);
if(pOutputBuffer != NULL)
free(pOutputBuffer); if(kernelSource != NULL)
free(kernelSource); if(memObj1 != NULL)
clReleaseMemObject(memObj1);
if(memObj2 != NULL)
clReleaseMemObject(memObj2); if(kernel != NULL)
clReleaseKernel(kernel); if(program != NULL)
clReleaseProgram(program); if(command_queue != NULL)
clReleaseCommandQueue(command_queue); if(context != NULL)
clReleaseContext(context); return ;
}
上面OpenCL内核源文件的路径被写死了——“/Users/zennychen/Downloads/test.cl”。各位可以根据自己环境重新指定。
另外,上面用了一些C99语法特性。如果是用Win7的小伙伴们,请使用Visual Studio 2013(Express/Professional)的C编译器。
下面是OpenCL内核源文件:
__kernel void test(__global int *pInOut, __global int *pIn)
{
int index = get_global_id(); pInOut[index] += pIn[index];
}
OpenCL多次循环执行内核的一个简单样例的更多相关文章
- Spring Ajax一个简单样例
配置不说了.要在前面helloworld的样例基础上弄. 相同在hello下新建ajax.jsp <%@ page language="java" contentType=& ...
- VB.net数据库编程(03):一个SQLserver连接查询的简单样例
这个样例,因为在ADO.net入门已经专门学了,再次进行复习 一下. 主要掌握连接字串的情况. 过程就是: 1.引用System.Data.SqlClient.而Access中引用 的是System. ...
- PHP初学者如何搭建环境,并在本地服务器(or云端服务器)运行自己的第一个PHP样例
页面底部有PHP代码样例供测试使用. 1.PHP开发,你需要什么? 1)开发代码的工具,可以用IDE名字叫做phpDesigner.当然也可以临时用记事本代替,记得文件扩展名为.php 2)服务器(本 ...
- hdu1011(树形背包)(提供一个特殊样例)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011 Starship Troopers Time Limit: 10000/5000 MS (Jav ...
- golang 记录函数执行耗时的一个简单方法。
先写一个公共函数, 比如在 common 包下有这么一个方法: // 写超时警告日志 通用方法 func TimeoutWarning(tag, detailed string, start time ...
- 【Xcode学C-1】怎样用Xcode练习C语言,并练习一个输出样例,以及重要的注意事项
直接用Xcode学习C语言,为iOS开发打基础. (1)选择OS X >>> Application >>> Command Line Tool (2)输入产品名称 ...
- C# 调用系统API 内核 简单样例
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.R ...
- eclipse 配置执行hadoop 2.7 程序样例參考步骤
前提:你搭建好了hadoop 2.x的linux环境,并可以成功执行.还有就是window可以訪问到集群.over 1. hfds-site.xml 添加属性:关闭集群的权限校验.windows的用户 ...
- C#开发Unity游戏教程循环遍历做出推断及Unity游戏演示样例
C#开发Unity游戏教程循环遍历做出推断及Unity游戏演示样例 Unity中循环遍历每一个数据,并做出推断 非常多时候.游戏在玩家做出推断以后.游戏程序会遍历玩家身上大量的所需数据,然后做出推断. ...
随机推荐
- JS 详解 Cookie、 LocalStorage 与 SessionStorage-转载
记录一下这些知识,有时候用到会忘记,对原文作者表达感谢. 附上原文链接:JS 详解 Cookie. LocalStorage 与 SessionStorage 基本概念 Cookie Cookie 是 ...
- Computer Vision_33_SIFT:PCA-SIFT A More Distinctive Representation for Local Image Descriptors——2004
此部分是计算机视觉部分,主要侧重在底层特征提取,视频分析,跟踪,目标检测和识别方面等方面.对于自己不太熟悉的领域比如摄像机标定和立体视觉,仅仅列出上google上引用次数比较多的文献.有一些刚刚出版的 ...
- Free lunch is over
译文:http://www.mamicode.com/info-detail-1324737.html 原文:http://www.gotw.ca/publications/concurrency-d ...
- 【C++/html版 代码 : 暴力破解数字红包 】-- 只要有编译器或者,不看运气,用手速敲代码说话,多人合作效果更佳!
需求分析: 或者是更大的范围! 是不是很捉急!运气背点不就over了! C++版: #include <stdio.h> #include <stdlib.h> #includ ...
- eclipse 编辑器 setDirty方法无效
可能是因为使用的编辑器继承了FormPage导致的,在setDirty方法里面多加一行getManagedForm().dirtyStateChanged();就可以了.
- SQL分表
一.为什么要水平分表?简而言之,当单表数据量过大时,无法对其进行有效的维护,以及查询速度严重变慢时,我们就需要对其时行水平分表. 二.什么时候需要水平分表?在数据库结构的设计中,需要充分考虑后期数据的 ...
- 内存泄露检测之mtrace
————————————————版权声明:本文为CSDN博主「知耻而后勇的蜗牛」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog ...
- mongod破解版的安装
navicat for mongodb 12,又叫做navicat 12 for mongodb,是针对mongodb软件而开发的一款管理软件,拥有高效图形用户界面,能够连接本地或远程的MongoDB ...
- [SaSS] Using Object like style to create class dynamiclly
$black: #; $white: #fff; $yellow: #ffe183; $dark-red: #e70404; $dark-green: #0d8268; $cloud-blue: #d ...
- 添加QQ群
点击右边添加 <a target=" src="//pub.idqqimg.com/wpa/images/group.png" alt="陆小果哥 ...