最近有不少朋友在多次循环执行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. linux命令返回值 / $?

    原文:http://blog.csdn.net/wyabc1986/article/details/7876673 在 Linux 下,不管你是启动一个桌面程序也好,还是在控制台下运行命令,所有的程序 ...

  2. Python实现神经网络算法识别手写数字集

    最近忙里偷闲学习了一点机器学习的知识,看到神经网络算法时我和阿Kun便想到要将它用Python代码实现.我们用了两种不同的方法来编写它.这里只放出我的代码. MNIST数据集基于美国国家标准与技术研究 ...

  3. jade-mixin 代码的重用

    有时候页面有好多个区块,比如列表区块,但是他们代码结构又是一模一样的怎么弄?jade天生就是节约成本,节约时间的,mixin就是让代码块可以重用的函数   mixin lession p jade s ...

  4. 2018 ICPC上海大都会赛重现赛 D Thinking-Bear magic (几何)

    2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 D Thinking-Bear magic (几何) 链接:https://ac.nowcoder.com/acm/contest/163/ ...

  5. 【python】获取目录下的最新文件夹/文件

    直接上代码 def new_report(test_report): lists = os.listdir(test_report) #列出目录的下所有文件和文件夹保存到lists print(lis ...

  6. 希尔排序Shell_Sort

    概述:听到希尔排序这个名称,心里完全没有任何概念,因为这个名称不能给你提供任何有效的信息.但是它的名字又是那么的特殊,以至于学习过数据结构排序的都知道这种方法的存在.现在我们就来看一下所谓的希尔排序. ...

  7. Mongo Backup

    #!/bin/sh # This script is run on every mongo node. However, it checks to see if this node is the pr ...

  8. JavaEE企业面试问题之Java基础部分

    1. Java基础部分 1.1 Java中的方法覆盖(Override)和方法重载(Overload)是什么意思? 重载Overload表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不 ...

  9. BZOJ 3925: [Zjoi2015] 地震后的幻想乡(概率DP)

    这里有一篇很好很强的博客%%% YouSiki大佬的博客 多理解一会就行了- 代码 #include <bits/stdc++.h> using namespace std; typede ...

  10. 解压 .tar.xz 格式的压缩文件

    第一种方法: xz -d mysql-8.0.16-linux-glibc2.12-x86_64.tar.xz tar -xvf mysql-8.0.16-linux-glibc2.12-x86_64 ...