▶ 事件的两种使用方法。第一种是用事件 a 标记进入命令队列的操作 A,于是后续进入命令队列的操作 B 可以被要求等到前面事件 a 完成(即操作 A 完成)以后才能开始调度执行。第二种是使用用户自定义的事件创造和标记完成操作来手动控制时间,阻塞任务的进行。

● 事件的使用代码(用两向量之和的代码改过来的)

 #include <stdio.h>
#include <stdlib.h>
#include <cl.h> const int nElement = ; const char *programSource = " \
__kernel void vectorAdd(__global int *A, __global int *B, __global int *C) \
{ \
int idx = get_global_id(); \
C[idx] = A[idx] + B[idx]; \
return; \
} \
"; int main()
{
const size_t datasize = sizeof(int) * nElement;
int i, *A, *B, *C;
cl_int status;
cl_event eventList[];// 一个事件列表 A = (int*)malloc(datasize);
B = (int*)malloc(datasize);
C = (int*)malloc(datasize);
for (i = ; i < nElement; A[i] = B[i] = i, i++); cl_uint nPlatform;
clGetPlatformIDs(, NULL, &nPlatform);
cl_platform_id *listPlatform = (cl_platform_id*)malloc(nPlatform * sizeof(cl_platform_id));
clGetPlatformIDs(nPlatform, listPlatform, NULL);
cl_uint nDevice = ;
clGetDeviceIDs(listPlatform[], CL_DEVICE_TYPE_ALL, , NULL, &nDevice);
cl_device_id *listDevice = (cl_device_id*)malloc(nDevice * sizeof(cl_device_id));
clGetDeviceIDs(listPlatform[], CL_DEVICE_TYPE_ALL, nDevice, listDevice, NULL);
cl_context context = clCreateContext(NULL, nDevice, listDevice, NULL, NULL, &status);
cl_command_queue cmdQueue = clCreateCommandQueue(context, listDevice[], , &status);
cl_program program = clCreateProgramWithSource(context, , (const char**)&programSource, NULL, &status);
status = clBuildProgram(program, nDevice, listDevice, NULL, NULL, NULL);
cl_kernel kernel = clCreateKernel(program, "vectorAdd", &status); cl_mem bufferA, bufferB, bufferC;
bufferA = clCreateBuffer(context, CL_MEM_READ_ONLY, datasize, NULL, &status);
bufferB = clCreateBuffer(context, CL_MEM_READ_ONLY, datasize, NULL, &status);
bufferC = clCreateBuffer(context, CL_MEM_WRITE_ONLY, datasize, NULL, &status); eventList[] = clCreateUserEvent(context, &status); // 用户自定义事件
clEnqueueWriteBuffer(cmdQueue, bufferA, CL_FALSE, , datasize, A, , &eventList[], &eventList[]); // 将该行语句标记为 eventList[1],要求它等待 eventList[0],事件列表长度为 1
clEnqueueWriteBuffer(cmdQueue, bufferB, CL_FALSE, , datasize, B, , &eventList[], &eventList[]); // 将该行语句标记为 eventList[2],要求它等待 eventList[0],事件列表长度为 1 clSetKernelArg(kernel, , sizeof(cl_mem), &bufferA);
clSetKernelArg(kernel, , sizeof(cl_mem), &bufferB);
clSetKernelArg(kernel, , sizeof(cl_mem), &bufferC);
size_t globalSize[] = { nElement }, localSize[] = { }; clEnqueueNDRangeKernel(cmdQueue, kernel, , NULL, globalSize, localSize, , eventList, NULL); // 核函数的调用需要等待事件列表,事件列表长度为 3 clSetUserEventStatus(eventList[], CL_COMPLETE);// 自定义完成事件 eventList[0],这样一来写入缓冲区和内核才能开始运行 clEnqueueReadBuffer(cmdQueue, bufferC, CL_TRUE, , datasize, C, , NULL, NULL);
for (i = ; i < nElement; i++)
{
if (C[i] != i + i)
break;
}
printf("Output is %s.\n", (i == nElement) ? "correct" : "incorrect"); free(A);
free(B);
free(C);
free(listPlatform);
free(listDevice);
clReleaseContext(context);
clReleaseMemObject(bufferA);
clReleaseMemObject(bufferB);
clReleaseMemObject(bufferC);
clReleaseCommandQueue(cmdQueue);
clReleaseProgram(program);
clReleaseKernel(kernel);
getchar();
return ;
}

● 输出结果

Output is correct.

● 若将第 58 行注释掉,则程序被挂起,不能结束。

▶ 回调函数,当到达特定状态(用事件定义)时在主机端执行的程序(函数)。一般用于主机端在等待设备端执行的过程中,用于调度其他任务或执行某些辅助计算,提高设备利用效率。

● 回调函数代码(用上面的事件代码改进得到的)

 #include <stdio.h>
#include <stdlib.h>
#include <cl.h> const int nElement = ; const char *programSource = " \
__kernel void vectorAdd(__global int *A, __global int *B, __global int *C) \
{ \
int idx = get_global_id(); \
C[idx] = A[idx] + B[idx]; \
return; \
} \
"; void hostFunction(int data) // 需要在主机端执行的回调函数
{
printf("<hostFunction> data = %d\n", data);
} void CL_CALLBACK callbackFunction(cl_event eventIn, cl_int status, void *userData) // 注明回调函数,注意参数的固定格式
{
hostFunction(*(int*)userData);
} int main()
{
const size_t datasize = sizeof(int) * nElement;
int i, *A, *B, *C;
cl_int status;
cl_event eventList[];// 一个事件列表 A = (int*)malloc(datasize);
B = (int*)malloc(datasize);
C = (int*)malloc(datasize);
for (i = ; i < nElement; A[i] = B[i] = i, i++); cl_uint nPlatform;
clGetPlatformIDs(, NULL, &nPlatform);
cl_platform_id *listPlatform = (cl_platform_id*)malloc(nPlatform * sizeof(cl_platform_id));
clGetPlatformIDs(nPlatform, listPlatform, NULL);
cl_uint nDevice = ;
clGetDeviceIDs(listPlatform[], CL_DEVICE_TYPE_ALL, , NULL, &nDevice);
cl_device_id *listDevice = (cl_device_id*)malloc(nDevice * sizeof(cl_device_id));
clGetDeviceIDs(listPlatform[], CL_DEVICE_TYPE_ALL, nDevice, listDevice, NULL);
cl_context context = clCreateContext(NULL, nDevice, listDevice, NULL, NULL, &status);
cl_command_queue cmdQueue = clCreateCommandQueue(context, listDevice[], , &status);
cl_program program = clCreateProgramWithSource(context, , (const char**)&programSource, NULL, &status);
status = clBuildProgram(program, nDevice, listDevice, NULL, NULL, NULL);
cl_kernel kernel = clCreateKernel(program, "vectorAdd", &status); cl_mem bufferA, bufferB, bufferC;
bufferA = clCreateBuffer(context, CL_MEM_READ_ONLY, datasize, NULL, &status);
bufferB = clCreateBuffer(context, CL_MEM_READ_ONLY, datasize, NULL, &status);
bufferC = clCreateBuffer(context, CL_MEM_WRITE_ONLY, datasize, NULL, &status); eventList[] = clCreateUserEvent(context, &status);
clEnqueueWriteBuffer(cmdQueue, bufferA, CL_FALSE, , datasize, A, , &eventList[], &eventList[]);
clEnqueueWriteBuffer(cmdQueue, bufferB, CL_FALSE, , datasize, B, , &eventList[], &eventList[]); clSetKernelArg(kernel, , sizeof(cl_mem), &bufferA);
clSetKernelArg(kernel, , sizeof(cl_mem), &bufferB);
clSetKernelArg(kernel, , sizeof(cl_mem), &bufferC);
size_t globalSize[] = { nElement }, localSize[] = { };
clEnqueueNDRangeKernel(cmdQueue, kernel, , NULL, globalSize, localSize, , eventList, NULL); clSetUserEventStatus(eventList[], CL_COMPLETE); clSetEventCallback(eventList[], CL_COMPLETE, callbackFunction, (void*)&i);// 在自定义事件 eventList[0] 完成后允许回调函数开始执行 clEnqueueReadBuffer(cmdQueue, bufferC, CL_TRUE, , datasize, C, , NULL, NULL);
for (i = ; i < nElement; i++)
{
if (C[i] != i + i)
break;
}
printf("Output is %s.\n", (i == nElement) ? "correct" : "incorrect"); free(A);
free(B);
free(C);
free(listPlatform);
free(listDevice);
clReleaseContext(context);
clReleaseMemObject(bufferA);
clReleaseMemObject(bufferB);
clReleaseMemObject(bufferC);
clReleaseCommandQueue(cmdQueue);
clReleaseProgram(program);
clReleaseKernel(kernel);
getchar();
return ;
}

● 输出结果

<hostFunction> data =
Output is correct.

● 用到的定义,cl.h 中

 // 有关 windows 下接口函数和回调函数的标记
#if defined(_WIN32)
#define CL_API_ENTRY
#define CL_API_CALL __stdcall
#define CL_CALLBACK __stdcall
#else
#define CL_API_ENTRY
#define CL_API_CALL
#define CL_CALLBACK
#endif // 有关事件的定义
typedef struct _cl_event* cl_event; // 有关回调函数开始执行的条件的选项
#define CL_COMPLETE 0x0
#define CL_RUNNING 0x1
#define CL_SUBMITTED 0x2
#define CL_QUEUED 0x3 extern CL_API_ENTRY cl_event CL_API_CALL clCreateUserEvent( // 创建用户自定义的事件
cl_context, // 给定上下文
cl_int * // 返回错误代码
) CL_API_SUFFIX__VERSION_1_1; extern CL_API_ENTRY cl_int CL_API_CALL clSetUserEventStatus(// 改变用户自定义事件状态
cl_event, // 给定事件
cl_int // 返回错误代码
) CL_API_SUFFIX__VERSION_1_1; extern CL_API_ENTRY cl_int CL_API_CALL clSetEventCallback( // 调用回调函数
cl_event, // 给定回调函数相关的状态(用事件定义)
cl_int, // 给定开始执行回调函数的条件(即给定事件达到某个状态)
void (CL_CALLBACK *)(cl_event, cl_int, void *), // 回调函数指针(注意参数格式)
void * // 传给回调函数的参数(上述函数指针的第三个参数)
) CL_API_SUFFIX__VERSION_1_1;

OpenCL 事件的使用,以及回调函数的更多相关文章

  1. react native 之 事件监听 和 回调函数

    同原生一样,react native 同样也有事件监听和回调函数这玩意. 场景很多,比如:A界面push到B界面,B界面再pop回A界面,可以给A界面传值或者告诉A刷新界面. 事件监听 事件监听类似于 ...

  2. property的使用(事件可能就是回调函数)

    TOnUserInfoShow = procedure(userName:string;userAge:Integer)of object;//定义事件模型中的回调函数原型 TUserInfo = c ...

  3. 小兔JS教程(三)-- 彻底攻略JS回调函数

    这一讲来谈谈回调函数. 其实一句话就能概括这个东西: 回调函数就是把一个函数当做参数,传入另一个函数中.传进去的目的仅仅是为了在某个时刻去执行它. 如果不执行,那么你传一个函数进去干嘛呢? 就比如说对 ...

  4. Java|今天起,别再扯订阅和回调函数

    编程史上有两个令人匪夷所思的说辞,一个是订阅,一个是回调函数. 我想应该还有很多同学为“事件的订阅”和“回调函数”所困扰,因为事情本来就不应该按这个套路来解释. 多直白,所谓的“回调函数”你完全可以线 ...

  5. JavaScript 回调函数中的 return false 问题

    今天一个同事问了我一个问题,就是在 Ajax 方法中,请求成功后(success)的回调函数中根据响应的值来判断程序是否继续执行,他不解的是在回调函数中已经 return false 了,但是 Aja ...

  6. PHP回调函数的几种用法

    PHP回调函数的实现方法 目录 前言      全局函数的回调      类静态函数的回调      对象的方法的回调      PHP事件模型(观察者模式)的实现思路    前言 最近在开发一个PH ...

  7. js中的回调函数 和promise解决异步操作中的回调地狱问题。

    回调函数 : 函数作为参数传递到另外一个函数中.简单数据类型和引入数据类型中的数组和对象作为参数传递大家肯定都不陌生,其实引用数据类型中的函数也是可以的. 事实上大家见到的很多,用到的也很多,比如jQ ...

  8. android 回调函数使用简介

    //1---定义回调函数 public interface GirdMenuStateListener { void onSuccess(); void onError(); } //2---使用的地 ...

  9. JS之Callback function(回调函数)

    JS中的回调函数: 1.概念: 函数a有一个参数,这个参数是个函数b,当函数a执行完以后执行函数b,那么这个过程就叫回调,即把函数作为参数传入到另一个函数中,这个函数就是所谓的回调函数. 2.举例: ...

  10. JavaScript Callback 回调函数

    JavaScript callback回调函数 你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货.在这 ...

随机推荐

  1. 配置AD RMS的一点心得

    基本上是按照下面的连接配置的,微软写的很好 AD RMS Step-by-Step Guide http://technet.microsoft.com/en-us/library/cc753531( ...

  2. wpf中的触发器详解 (转自 乂乂的日志 - 网易博客)

    2010-03-24 16:19:07|  分类: WPF相关 |  标签: |字号大中小 订阅     wpf中的触发器详解 WPF/C# 2009-08-24 11:32:50 7.1.2  简单 ...

  3. powerdesigner安装图解

  4. Beta阶段贡献分配规则

    作业要求[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2382] 在新成员加入后,我们经过讨论,决定沿用alpha阶段贡献分分配规则. ...

  5. html5实现本页面元素拖放和本地文件拖放

    HTML5拖放 拖放本地数据   1.HTML拖放 拖放(Drag 和 Drop)是HTML5标准的组成部分 2.拖放开始: ondragStart:调用了一个函数,drag(event),它规定了被 ...

  6. HDU 1533

    http://acm.hdu.edu.cn/showproblem.php?pid=1533 人和房子数量相同,每个人进房子,费用是人到房子的曼哈顿距离,求最小费用 可用最小费用最大流求解,建立虚拟的 ...

  7. scikit-learn 学习笔记-- Generalized Linear Models (二)

    Lasso regression 今天介绍另外一种带正则项的线性回归, ridge regression 的正则项是二范数,还有另外一种是一范数的,也就是lasso 回归,lasso 回归的正则项是系 ...

  8. windows下 两个版本的JDK环境变量进行切换 MARK

    我们平时在window上做开发的时候,可能需要同时开发两个甚至多个项目,有时不同的项目对JDK的版本要求有区别,为了简化操作,我们可以通过批处理文件来完成环境变量切换的任务.使用方法:阅读代码我们就会 ...

  9. spring注解-@Autowired。@Resource。@Service

    Spring的@Autowired注解.@Resource注解和@Service注解 什么是注解 传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点: ...

  10. Hadoop_10_shuffle01_Hadoop中的Shuffle详解【来源网络】

    原文网址:http://blog.itpub.net/30316686/viewspace-2057204/ 详细的了解Shuffle过程,能更好的对hadoop集群进行优化.         Map ...