最近有不少朋友在多次循环执行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多次循环执行内核的一个简单样例的更多相关文章

  1. Spring Ajax一个简单样例

    配置不说了.要在前面helloworld的样例基础上弄. 相同在hello下新建ajax.jsp <%@ page language="java" contentType=& ...

  2. VB.net数据库编程(03):一个SQLserver连接查询的简单样例

    这个样例,因为在ADO.net入门已经专门学了,再次进行复习 一下. 主要掌握连接字串的情况. 过程就是: 1.引用System.Data.SqlClient.而Access中引用 的是System. ...

  3. PHP初学者如何搭建环境,并在本地服务器(or云端服务器)运行自己的第一个PHP样例

    页面底部有PHP代码样例供测试使用. 1.PHP开发,你需要什么? 1)开发代码的工具,可以用IDE名字叫做phpDesigner.当然也可以临时用记事本代替,记得文件扩展名为.php 2)服务器(本 ...

  4. hdu1011(树形背包)(提供一个特殊样例)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011 Starship Troopers Time Limit: 10000/5000 MS (Jav ...

  5. golang 记录函数执行耗时的一个简单方法。

    先写一个公共函数, 比如在 common 包下有这么一个方法: // 写超时警告日志 通用方法 func TimeoutWarning(tag, detailed string, start time ...

  6. 【Xcode学C-1】怎样用Xcode练习C语言,并练习一个输出样例,以及重要的注意事项

    直接用Xcode学习C语言,为iOS开发打基础. (1)选择OS X >>> Application >>> Command Line Tool (2)输入产品名称 ...

  7. C# 调用系统API 内核 简单样例

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.R ...

  8. eclipse 配置执行hadoop 2.7 程序样例參考步骤

    前提:你搭建好了hadoop 2.x的linux环境,并可以成功执行.还有就是window可以訪问到集群.over 1. hfds-site.xml 添加属性:关闭集群的权限校验.windows的用户 ...

  9. C#开发Unity游戏教程循环遍历做出推断及Unity游戏演示样例

    C#开发Unity游戏教程循环遍历做出推断及Unity游戏演示样例 Unity中循环遍历每一个数据,并做出推断 非常多时候.游戏在玩家做出推断以后.游戏程序会遍历玩家身上大量的所需数据,然后做出推断. ...

随机推荐

  1. HTML之表格标签和form表单

    表格标签: table 一般用于信息展示 tr行 td文本单元格 th标题单元格(文本加粗) table属性: cellspacing:单元格间距,一般设置为0 cellpadding:文字到边框的距 ...

  2. linux下安装nginx+php+mysql环境 详细教程

    话不多说上代码 linux环境:centos 7.0 64位 nginx:nginx-1.8.0.tar.gz php: php-7.1.1.tar.gz mysql: mysql-5.6.21.ta ...

  3. 网络分类及OSI七层模型

    一.网络分类: 局域网(LAN)是指在某一区域内由多台计算机互联成的计算机组.一般是方圆几千米以内.局域网可以实现文件管理.应用软件共享.打印机共享.工作组内的日程安排.电子邮件和传真通信服务等功能. ...

  4. bat 判断 bat 是否是以管理员权限运行,和自动以管理员权限运行

    bat 判断 bat 是否是以管理员权限运行,和自动以管理员权限运行 判断 @echo off net.exe session 1>NUL 2>NUL && ( goto ...

  5. vimrc config and NERDTree

    nmap <C-N> :tabnext<CR> 下载和配置 NERDTree插件的官方地址如下,可以从这里获取最新的版本 https://github.com/scrooloo ...

  6. Fastdfs集群搭建

    1.关于集群,网上说最少要三台,其实也没必要,两台就够了 2.实验环境 192.168.2.201 tracker.storage.nginx + fastdfs-nginx-module-maste ...

  7. 第95题:不同的二叉搜素树II

    一. 问题描述 给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树. 示例: 输入: 3 输出: [ [1,null,3,2], [3,2,null,1], [3,1,null,n ...

  8. 2018web前端面试题总结

      web面试题 css面试 一.css盒模型 css中的盒子模型包括IE盒子模型和标准的W3C盒子模型.border-sizing: border-box, inherit, content-box ...

  9. Codeforces Round #543 (Div. 1, based on Technocup 2019 Final Round) 题解

    题面戳这里 A. Diana and Liana 首先如果s>ks>ks>k一定无解,特判一下.那么我们考虑找恰好满足满足题目中的要求的区间[l,r][l,r][l,r],那么需要要 ...

  10. python自动华 (十四)

    Python自动化 [第十四篇]:HTML介绍 本节内容: Html 概述 HTML文档 常用标签 2. CSS 概述 CSS选择器 CSS常用属性 1.HTML 1.1概述 HTML是英文Hyper ...