基于Redis的进程间通信——在C++里使用python的深度学习模型
本文主要是为进程间通信(特别是语言都不同的进程)提供一种新的思路。
本想法来源于RoboMaster比赛中的神符检测,神符是指一个9宫格的手写体数字(Mnist)或火焰体动态数字,需要用到机器学习或深度学习模型对数字进行识别,从检测大符到识别全部数字到发射子弹,整个过程不能超过1.5秒,全部的运算量都集中在搭载在机器小车上的miniPC,由于miniPC性能有限,且除了这个程序之外还有个非常占用资源的自动射击程序,所以整个程序使用的是C++的代码。一开始我们使用的是xgboost,在python上把模型训练完成之后,把预测代码再改写成C++。而后来发现xgboost在真实场景上的准确率较低(只有70%),于是改为了复杂的深度学习模型,准确率上几乎可以达到100%。但此时的python代码已经比较复杂,再全部改成C++已经不现实,所以开始寻找一种能实现两者间通信的方案。
嗨喽:正在学习python的小伙伴或者打算学习的,可以私信小编“01”领取资料!
网上已有的通信方案
- C++ 与python的混合编程方案比较多,比如使用,说实话,我没认真看,需要的自己去百度或谷歌吧
- 进程间通信的常见的方案有共享内存、消息队列、管道等等,但是实现难度较大,特别是对于不同语言的程序
基于Redis的通信方案
基本思路
- Redis是一种基于内存的NoSQL 数据库,所以读写速度非常快,且使用非常简单。但其底层是基于socket通信,所以速度比共享内存、消息队列要慢一个级别,但在本场景中,能有效解决问题
- 在本应用场景中,每次需要通信的数据是9张图片,每张图片是28×28像素,所以其实就是 9 x 28 x 28的矩阵
- 所以,在C++程序中进行检测和提取图像,将9张图片的像素值依次读取进来,存储进Redis,通过标志位的方式告诉python程序进行读取,完成预测之后再将结果存储进redis,并让C++程序进行调用。整个程序的活动图如下:
- 理论上,因为都是基于内存的,所以这种方法的速度只会稍慢于C++的引用传参,相当于进行了一次深拷贝。
- 得益于redis的高效读取,在没有gpu的加速下,在mac i7 16G的配置上跑完一次读取和预测大概需要0.3秒,即使在性能较差的miniPC上,一次耗时大概在0.6秒,主要时间消耗都在预测上,如下图:
代码实现
- C++ 使用hiredis
mac上安装方法:brew install hiredis ubuntu安装方法:apt-get install hiredis
- CMake(仅供参考):
cmake_minimum_required(VERSION 3.6) project(rune) set(CMAKE_CXX_STANDARD 11) set(SOURCE_FILES main.cpp) find_package(OpenCV REQUIRED) add_executable(rune ${SOURCE_FILES}) link_directories(/usr/local/Cellar/hiredis/0.13.3/lib) target_link_libraries(rune libhiredis.dylib libhiredis.0.13.dylib libhiredis.a) TARGET_LINK_LIBRARIES(rune ${OpenCV_LIBS})
- demo1: 连接数据库
redisContext* connect_redis(){ redisContext *context = redisConnect("127.0.0.1", 6379); if(context->err) { redisFree(context); printf("connect redisServer err:%s\n", context->errstr); return NULL; } printf("connect redisServer success\n"); return context; }
- demo2: 测试redis lrange 操作
void test_redis_lrange(redisContext *context){ redisReply *result = (redisReply *)redisCommand(context, "lrange IMAGE_NAME_LIST 0 -1"); redisReply **elements = result->element; size_t size = result->elements; for(int i=0; i < size; i++){ string re = elements[i]->str; int r = re.at(7) - 48; cout << "r:" << r<<endl; } freeReplyObject(result); cout << endl; }
一点经验:由于关于hiredis的文档比较少,所以很多参数、返回值不得不自己进行测试,通过阅读源代码是一种方式,我比较喜欢的是通过jetbrain CLion的debug模式进行查看。如下图,在不清楚hiredis的lrange返回的参数的情况下,我通过在返回值那里设断点,通过添加监视等操作来判断如何正确获取返回值。并且,Clion是可以跨平台的,对学生免费。
最后多说一句,小编是一名python开发工程师,这里有我自己整理了一套最新的python系统学习教程,包括从基础的python脚本到web开发、爬虫、数据分析、数据可视化、机器学习等。想要这些资料的可以关注小编,并在后台私信小编:“01”即可领取。