本文主要是为进程间通信(特别是语言都不同的进程)提供一种新的思路

  • 本想法来源于RoboMaster比赛中的神符检测,神符是指一个9宫格的手写体数字(Mnist)或火焰体动态数字,需要用到机器学习或深度学习模型对数字进行识别,从检测大符到识别全部数字到发射子弹,整个过程不能超过1.5秒,全部的运算量都集中在搭载在机器小车上的miniPC,由于miniPC性能有限,且除了这个程序之外还有个非常占用资源的自动射击程序,所以整个程序使用的是C++的代码。一开始我们使用的是xgboost,在python上把模型训练完成之后,把预测代码再改写成C++。而后来发现xgboost在真实场景上的准确率较低(只有70%),于是改为了复杂的深度学习模型,准确率上几乎可以达到100%。但此时的python代码已经比较复杂,再全部改成C++已经不现实,所以开始寻找一种能实现两者间通信的方案。

网上已有的通信方案

  • C++ 与python的混合编程方案比较多,比如使用,说实话,我没认真看,需要的自己去百度或谷歌吧
  • 进程间通信的常见的方案有共享内存、消息队列、管道等等,但是实现难度较大,特别是对于不同语言的程序

基于Redis的通信方案

基本思路

  • Redis是一种基于内存的NoSQL 数据库,所以读写速度非常快,且使用非常简单。但其底层是基于socket通信,所以速度比共享内存、消息队列要慢一个级别,但在本场景中,能有效解决问题
  • 在本应用场景中,每次需要通信的数据是9张图片,每张图片是28×28像素,所以其实就是 9 x 28 x 28的矩阵
  • 所以,在C++程序中进行检测和提取图像,将9张图片的像素值依次读取进来,存储进Redis,通过标志位的方式告诉python程序进行读取,完成预测之后再将结果存储进redis,并让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;
    }
  • 更多demo 参考HiredisTest
  • 一点经验:由于关于hiredis的文档比较少,所以很多参数、返回值不得不自己进行测试,通过阅读源代码是一种方式,我比较喜欢的是通过jetbrain CLion的debug模式进行查看。如下图,在不清楚hiredis的lrange返回的参数的情况下,我通过在返回值那里设断点,通过添加监视等操作来判断如何正确获取返回值。并且,Clion是可以跨平台的,对学生免费。
Python 使用redis
  • 这个非常简单,在这里仅提供一个demo,这个demo是我自己写某个比较大的爬虫项目时对redis进行封装的一系列接口。
Redis 踩坑笔记

以前写的一篇博客,记录了Redis使用过程中的一些坑,欢迎交流
Redis 踩坑笔记


2019.01.08更新

在《软件体系结构》这门课上发现,原来这种模式在软件工程领域称作Shared-Data Pattern,解释如下:

点击量:1170

分类: 数据挖掘

2 条评论

MacOS常用软件推荐——针对计算机相关专业的同学 – 小麦冬 · 2019年1月20日 下午8:09

[…] Redis我最喜欢的数据库,使用方便,支持的数据类型多,string,list,set,map等,有时候甚至可以当作一个大型的共享内存玩。比如这篇博客: 基于Redis的进程间通信——在C++里使用python的深度学习模型 […]

快速搭建RESTful服务————在Web项目中使用python机器学习模型 – 小麦冬 · 2019年2月26日 下午7:08

[…] 最近遇到的一个问题是需要从springboot的项目中调用另外一个python项目的机器学习模型等内容,之前的一篇文章讲的是利用Redis作为共享内存,从而实现在C++程序中调用python模型的方法,但这种方法的主要应用场景是离线环境(搭载在机器人上的miniPC)。并且python项目那边内容较多,除了加载机器学习模型之外还干了很多事情,如果全部将这些代码重写为Java工作量过于庞大,反之亦然。所以最终选择搭建RESTful服务器,在springboot项目中进行网络调用。 […]

发表评论

电子邮件地址不会被公开。 必填项已用*标注