OpenVINO 2021r2 - Remote Blob API of GPU Plugin 示例复现(一) OpenCL Kernel Execution on a Shared Buffer

本文主要是介绍OpenVINO 2021r2 - Remote Blob API of GPU Plugin 示例复现(一) OpenCL Kernel Execution on a Shared Buffer,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前一阵尝试了一下OpenVINO纯GPU推理的代码实现, 主要复现了OpenVINO官方文档Remote Blob API of GPU Plugin 中的 OpenCL Kernel Execution on a Shared Buffer例子

官方示例的完整实现可以具体参考这篇文章 OpenVINO 2020r3 体验GPU Remote Blob API

 

在代码里,把OpenVINO clDNN的cl::context提取出来,再基于这个context创建了cl::device和cl::queue.

然后基于这个cl::context创建了一个cl::Buffer (就是cl_mem对象), 再将这个cl::Buffer 用gpu::make_shared_blob把Buffer转换成了Inference Engine能识别的内存Blob,

最后通过 InferRequest::SetBlob将这个Blob和推理网络的输入层连在一起,这个在inf_req_shared.Infer()的时候就会在读取输入层数据的时候从这个Blob的cl::Buffer里读取数据,这样就完成了从GPU显存(OpenCL cl_mem对象)里读取输入数据

 

上次的这段代码主要参考自OV源码库的openvino-master\inference-engine\tests\functional\plugin\gpu\remote_blob_tests\cldnn_remote_blob_tests.cpp里面。这次基于OpenVINO 2021r2把这段代码补全一下,将上图中的“OpenVINO GPU推理”的输出部分的数据也存放到cl_mem对象里,这样推理结束后就可以基于这个cl_mem对象用自己的OpenCL代码来实现基于GPU的数据后处理,避免使用CPU将数据读取到系统内存中再拷回显存的开销。 这个例子叫 OpenCL Kernel Execution on a Shared Buffer and Output to a Shared Buffer :)

 

GPU RemoteBlob API推理代码的实现

// inference using remote blobauto inf_req_shared = executable_network.CreateInferRequest();// obtain the RemoteContext pointer from the executable network object//从ExecutableNetwork中获取cldnn的cl_contextauto cldnn_context = executable_network.GetContext();cl_context ctx = std::dynamic_pointer_cast<gpu::ClContext>(cldnn_context)->get();cl::Context _context;cl::Device _device;cl::CommandQueue _queue;// user-supplied context handle// 基于这个cl_context创建cl::Context/Device/Queue_context = cl::Context(ctx, true);_device = cl::Device(_context.getInfo<CL_CONTEXT_DEVICES>()[0].get(), true);cl_command_queue_properties props = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE;_queue = cl::CommandQueue(_context, _device, props);auto dims = network.getInputsInfo().begin()->second->getTensorDesc().getDims();size_t imSize = dims[1] * dims[2] * dims[3];cout << "imSize = " << imSize << " dims[1]=" << dims[1] << " dims[2]=" << dims[2] << " dims[3]=" << dims[3] << endl << endl;size_t num_channels = dims[1];size_t image_size = dims[3] * dims[2];//prepare input image data/** Iterate over all pixel in image (b,g,r) **///将输入图像的RGB数据放到ImageBuffer里unsigned char *ImageBuffer;ImageBuffer = (unsigned char *)malloc(imSize);unsigned char* pixels = (unsigned char*)(jpg.data);for (size_t pid = 0; pid < image_size; pid++) {/** Iterate over all channels **/for (size_t ch = 0; ch < num_channels; ++ch) {/**          [images stride + channels stride + pixel id ] all in bytes            **/ImageBuffer[ch * image_size + pid] = pixels[pid*num_channels + ch];//set input data to 0//ImageBuffer[ch * image_size + pid] = 0;}}//创建cl::Buffer shared_buffer作为输入Buffer,并将ImageBuffer的数据放入shared_buffer内cl_int err;cl::Buffer shared_buffer(_context, CL_MEM_READ_WRITE, imSize, NULL, &err);{void *buffer = ImageBuffer;_queue.enqueueWriteBuffer(shared_buffer, true, 0, imSize, buffer);}//将cl::Buffer封装进InferenceEngine::BlobBlob::Ptr shared_blob = gpu::make_shared_blob(network.getInputsInfo().begin()->second->getTensorDesc(), cldnn_context,shared_buffer);//将网络输入层的数据指针指向存有输入图像RGB值的Blobinf_req_shared.SetBlob(network.getInputsInfo().begin()->first, shared_blob);//这里创建2个内存块, 块大小和推理网络输出层的数据块大小一致//SqueezeNet输出是1000个FP32的值,所以每个块的大小为4KB//块的大小可以通过getOutputsInfo()来获取,这里偷懒,手工写成4KB//C[]全填0,用来将推理输出层的Blob全清0; D[]用来存放从推理输出的cl::Buffer中读取的数据size_t outputSize = 1000 * 4;float *C = new float[1000];float *D = new float[1000];for (int i = 0; i < 1000; i++){C[i] = 0;D[i] = 0;}//创建cl::Buffer shared_output_buffer作为输出Buffer,并将输出Buffer清0cl::Buffer shared_output_buffer(_context, CL_MEM_READ_WRITE, outputSize, NULL, &err);{void *buffer = ImageBuffer;_queue.enqueueWriteBuffer(shared_output_buffer, true, 0, sizeof(float)*1000, C);}//将cl::Buffer封装进InferenceEngine::Blob shared_output_blobBlob::Ptr shared_output_blob = gpu::make_shared_blob(network.getOutputsInfo().begin()->second->getTensorDesc(), cldnn_context,shared_output_buffer);//将网络输出层的数据指针指向shared_output_blobinf_req_shared.SetBlob(network.getOutputsInfo().begin()->first, shared_output_blob);inf_req_shared.Infer();// Copy the output data back to the host//将shared_output_buffer内的数据读到D[]中_queue.enqueueReadBuffer(shared_output_buffer, CL_TRUE, 0, sizeof(float) * 1000, D);//这里只打印每个class的confidence大于0.0001的class的信息for (int i = 0; i < 1000; i++){if (D[i] > 0.0001){cout << "C[" << i << "] = " << C[i] << " - D[" << i << "] = " << D[i] << endl;}}//输出数据在调用Infer()的时候已经放到shared_output_blob里了, 所以不需要通过调用GetBlob获得输出数据//auto outputBlob_shared = inf_req_shared.GetBlob(network.getOutputsInfo().begin()->first);free(ImageBuffer);if (C != NULL){delete C;}if (D != NULL){delete D;}//cout << "Processing output shared blobs" << endl;//ClassificationResult classificationResult_shared(outputBlob_shared, validImageNames,//	batchSize, FLAGS_nt,//	labels);//classificationResult_shared.print();

 

运行程序,可以得到结果

输出数据放在cl::Buffer shared_output_blob->shared_output_buffer中,这里偷懒了, 没有做输出数据的排序,而是输出了每个class分类confidence>0.0001的所有类的index和confidence

可以看到

top 1是 D[817] = 0.6386719

top 2是 D[479] = 0.2141113

top 3是 D[511] = 0.1010742

和前半部分的GPU标准推理流程的输出一致。

classid probability label
------- ----------- -----
817     0.6386719   sports car, sport car
479     0.2141113   car wheel
511     0.1010742   convertible
436     0.0251923   beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon
751     0.0065804   racer, race car, racing car
656     0.0047264   minivan
717     0.0031071   pickup, pickup truck
581     0.0026569   grille, radiator grille
468     0.0014210   cab, hack, taxi, taxicab
661     0.0010548   Model T

 

整个流程的实现顺序如图

个人感受:

Remote Blob API主要用在OpenVINO GPU推理与其他GPU加速代码的集成这种场景。在OpenCL Kernel Execution on a Shared Buffer的例子里,OCL的context是从OpenVINO的ExecutableNetwork里获取的,所以必须要先创建OV的对象,再在这个基础上添加运行自己的OCL的处理代码,个人感觉比较适合从零开始开发自己的AI推理项目,或者只用OCL做推理前后数据的预处理和后处理的评估预研这种场景。如果要是把OpenVINO集成到自己已经有的OpenCL项目里,可能就需要官网文档里提到的另一种方法(Running GPU Plugin Inference within User-Supplied Shared Context)了。

PS: 因为我不是很懂OCL的开发, 所以以上为纯个人感受,欢迎拍砖 :P

 

最后完整项目奉上,仅供参考

https://gitee.com/tisandman/cl_ov_sharing

 

 

 

这篇关于OpenVINO 2021r2 - Remote Blob API of GPU Plugin 示例复现(一) OpenCL Kernel Execution on a Shared Buffer的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/301005

相关文章

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码

《在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码》在MyBatis的XML映射文件中,trim元素用于动态添加SQL语句的一部分,处理前缀、后缀及多余的逗号或连接符,示... 在MyBATis的XML映射文件中,<trim>元素用于动态地添加SQL语句的一部分,例如SET或W

Go语言使用Buffer实现高性能处理字节和字符

《Go语言使用Buffer实现高性能处理字节和字符》在Go中,bytes.Buffer是一个非常高效的类型,用于处理字节数据的读写操作,本文将详细介绍一下如何使用Buffer实现高性能处理字节和... 目录1. bytes.Buffer 的基本用法1.1. 创建和初始化 Buffer1.2. 使用 Writ

Redis延迟队列的实现示例

《Redis延迟队列的实现示例》Redis延迟队列是一种使用Redis实现的消息队列,本文主要介绍了Redis延迟队列的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录一、什么是 Redis 延迟队列二、实现原理三、Java 代码示例四、注意事项五、使用 Redi

在Pandas中进行数据重命名的方法示例

《在Pandas中进行数据重命名的方法示例》Pandas作为Python中最流行的数据处理库,提供了强大的数据操作功能,其中数据重命名是常见且基础的操作之一,本文将通过简洁明了的讲解和丰富的代码示例,... 目录一、引言二、Pandas rename方法简介三、列名重命名3.1 使用字典进行列名重命名3.编

Python使用Colorama库美化终端输出的操作示例

《Python使用Colorama库美化终端输出的操作示例》在开发命令行工具或调试程序时,我们可能会希望通过颜色来区分重要信息,比如警告、错误、提示等,而Colorama是一个简单易用的Python库... 目录python Colorama 库详解:终端输出美化的神器1. Colorama 是什么?2.

Go Gorm 示例详解

《GoGorm示例详解》Gorm是一款高性能的GolangORM库,便于开发人员提高效率,本文介绍了Gorm的基本概念、数据库连接、基本操作(创建表、新增记录、查询记录、修改记录、删除记录)等,本... 目录1. 概念2. 数据库连接2.1 安装依赖2.2 连接数据库3. 数据库基本操作3.1 创建表(表关

Python视频剪辑合并操作的实现示例

《Python视频剪辑合并操作的实现示例》很多人在创作视频时都需要进行剪辑,本文主要介绍了Python视频剪辑合并操作的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录介绍安装FFmpegWindowsMACOS安装MoviePy剪切视频合并视频转换视频结论介绍

python多进程实现数据共享的示例代码

《python多进程实现数据共享的示例代码》本文介绍了Python中多进程实现数据共享的方法,包括使用multiprocessing模块和manager模块这两种方法,具有一定的参考价值,感兴趣的可以... 目录背景进程、进程创建进程间通信 进程间共享数据共享list实践背景 安卓ui自动化框架,使用的是