OpenCL 事件的使用,以及回调函数
▶ 事件的两种使用方法。第一种是用事件 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 事件的使用,以及回调函数的更多相关文章
- react native 之 事件监听 和 回调函数
同原生一样,react native 同样也有事件监听和回调函数这玩意. 场景很多,比如:A界面push到B界面,B界面再pop回A界面,可以给A界面传值或者告诉A刷新界面. 事件监听 事件监听类似于 ...
- property的使用(事件可能就是回调函数)
TOnUserInfoShow = procedure(userName:string;userAge:Integer)of object;//定义事件模型中的回调函数原型 TUserInfo = c ...
- 小兔JS教程(三)-- 彻底攻略JS回调函数
这一讲来谈谈回调函数. 其实一句话就能概括这个东西: 回调函数就是把一个函数当做参数,传入另一个函数中.传进去的目的仅仅是为了在某个时刻去执行它. 如果不执行,那么你传一个函数进去干嘛呢? 就比如说对 ...
- Java|今天起,别再扯订阅和回调函数
编程史上有两个令人匪夷所思的说辞,一个是订阅,一个是回调函数. 我想应该还有很多同学为“事件的订阅”和“回调函数”所困扰,因为事情本来就不应该按这个套路来解释. 多直白,所谓的“回调函数”你完全可以线 ...
- JavaScript 回调函数中的 return false 问题
今天一个同事问了我一个问题,就是在 Ajax 方法中,请求成功后(success)的回调函数中根据响应的值来判断程序是否继续执行,他不解的是在回调函数中已经 return false 了,但是 Aja ...
- PHP回调函数的几种用法
PHP回调函数的实现方法 目录 前言 全局函数的回调 类静态函数的回调 对象的方法的回调 PHP事件模型(观察者模式)的实现思路 前言 最近在开发一个PH ...
- js中的回调函数 和promise解决异步操作中的回调地狱问题。
回调函数 : 函数作为参数传递到另外一个函数中.简单数据类型和引入数据类型中的数组和对象作为参数传递大家肯定都不陌生,其实引用数据类型中的函数也是可以的. 事实上大家见到的很多,用到的也很多,比如jQ ...
- android 回调函数使用简介
//1---定义回调函数 public interface GirdMenuStateListener { void onSuccess(); void onError(); } //2---使用的地 ...
- JS之Callback function(回调函数)
JS中的回调函数: 1.概念: 函数a有一个参数,这个参数是个函数b,当函数a执行完以后执行函数b,那么这个过程就叫回调,即把函数作为参数传入到另一个函数中,这个函数就是所谓的回调函数. 2.举例: ...
- JavaScript Callback 回调函数
JavaScript callback回调函数 你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货.在这 ...
随机推荐
- 为什么需要消息队列MQ?
主要原因:是在高并发情况下,由于来不及同步处理,请求往往会发生堵塞,比如诸多的insert.update之类的请求同时到达mysql,直接导致无数的行锁表锁,甚至最后请求会堆积很多,从而触发大量的to ...
- [图床神器]Windows下的图片上传工具MPic
最近用hexo在github上搭建了一个静态博客,开始几天用起来感觉还挺好的,但是用了些天就觉得每次写文章插入图片就非常麻烦,而且如果图片多了的话上传和访问就很慢了.后来网上看了下发现mac下有款ip ...
- Java队列的两种实现方式
1. 基于数组 package Algorithm.learn; import java.util.Arrays; /** * Created by liujinhong on 2017/3/7. * ...
- 过滤器系列(三)—— RSQF
这个过滤器本身是一篇论文中提出的过滤器的简化版本,去掉了计数功能,我觉得简化版本应用的可能也很广,专门写一篇简化版本的RSQF.RSQF全称是rank-and-select based filter, ...
- LeetCode 47
class Solution { public: vector<vector<int>> permuteUnique(vector<int>& nums) ...
- redis集群登陆
如何登陆redis集群 redis-cli -h 10.12.4.45 -p 7000 -c -a alkdsjf134rj01 ip:10.12.4.45 集群中的一个点 -c 以集群方式登陆.cl ...
- PHP:第五章——字符串转换与比较
<?php header("Content-Type:text/html;charset=utf-8"); //字符串的转换与比较 //1.ord——返回首字符的ASCLL: ...
- 解决PCI Geomatica 无法卸载的问题
之前安装过PCI Geomatica 2016,非正常卸载,应该有一定残留,但我已经尽可能将注册表中包含PCI.Geomatica.Geomatics等关键字的条目删除干净了. 在重新安装新版本201 ...
- halcon之共线连接union_collinear_contours_xld
union_collinear_contours_xld 很多时候当我们用edges_sub_pix, threshold_sub_pix 等算子得到边缘后,因为有噪声.物体本身断裂等原因 很多边缘 ...
- Python PIL : IOError: decoder jpeg not available
The first thing I check when I got this error was to check if libjpeg was installed. Lets try this s ...