vlambda博客
学习文章列表

c++调用python脚本,指针快速传递

优秀的判断力来自经验,但经验来自错误的判断–Fred Brooks

c++调用python脚本,指针快速传递
为什么?

1、python以开发速度快著称。

2、python在数据处理方面具备强大的优势。

尤其在深度学习与机器学习方面,虽然现在有pytorch、tensorflow在部署上提供了像libtorch的c++版本,但那也只包括模型推理部分。如果在前期预处理上也用c++实现,那会是一件十分麻烦的事情。

因此,想通过c++调用python中函数的方式,将数据实现互传。(踩了许多的坑,网上有许多通过list或者tuple来传递的,如果是小数据也没什么问题。但如果遇到医疗这样的大数据,会需要极大的内存开销。本文将采用numpy指针的方式来传递数据,在内存开销与速度上都有极大的提升)。



环境

此处环境配置是为了在无安装python环境的机子上运行。(如果电脑已安装环境就不需要拷贝,只需将环境变量加入即可)。

注意:python与c++的位数一定要对应,例如全为32位或者全为64位。


新建一个项目,将python安装的include与libs,拷贝到c++运行目录下。

c++调用python脚本,指针快速传递

由于python安装是没有debug模式的,但又需要在c++中使用到debug模式。

方法一:直接将libs文件夹的python38.lib复制一份该名称为python38_d.lib。(该方式不推荐,后续代码释放内存会有问题)

c++调用python脚本,指针快速传递

方法二:修改include下的pyconfig.h。

将pragma comment(lib,"python38_d.lib")改为pragma comment(lib,"python38.lib"),并且注释掉 # define Py_DEBUG

c++调用python脚本,指针快速传递

添加python库

将python安装目录下的DLLs与Lib拷贝c++到运行目录下(此处默认你安装了Numpy)。

c++调用python脚本,指针快速传递

拷贝python38.dll到c++运行目录(python安装目录下的文件)。

c++调用python脚本,指针快速传递

上述准备材料即准备完成。

打开vs属性管理器添加:

1.包含库目录添加之前拷贝来的include以及Lib下的numpy。

c++调用python脚本,指针快速传递

2.库目录添加libs以及Lib下的numpy的lib。

c++调用python脚本,指针快速传递

3.连接器输入添加python38.lib和npymath.lib。

c++调用python脚本,指针快速传递

以上的配置都包含了numpy,如果不想用numpy,也可以不添加numpy的任何操作。数据传输也是可以用list或者tuple来完成。但经我实验发现,numpy的数据传输方式内存开销起码节约5倍以上,速度也快5倍以上。


编写测试代码
#include<iostream>#include"include\Python.h"//#include<string.h>#include"arrayobject.h"//#include<object.h>//using namespace std;int main(){ //指定python.exe位置 python的环境。后来在迁移环境时,将include libs放在工程目录下,指定工程目录就可以 Py_SetPythonHome(L"./");//指定python.exe位置需要修改成自己的 python的环境 //初始化Python环境  Py_Initialize();
//初始化Numpy import_array();
// 将当前目录加入sys.path 这两行的意思是你的主目录位置:是在工程目录下因此要把.py文件放到该目录下!。也可以自己定义 PyRun_SimpleString("import sys"); PyRun_SimpleString("sys.path.append('./')"); PyRun_SimpleString("import os"); PyRun_SimpleString("print(os.listdir())");

//导入模块 PyObject* pModule = PyImport_ImportModule("candpython");
//导入函数 PyObject* pFunc = PyObject_GetAttrString(pModule, "ff");
//数据准备 short* numpyptr = new short[90000000]; for (int i = 0; i < 90000000; i++) { short j = 1000; numpyptr[i] = j; } npy_intp dims[2] = { 3000,30000 };//矩阵维度 PyObject* PyArray = PyArray_SimpleNewFromData(2, dims, NPY_SHORT, numpyptr);//将数据变为numpy
//用tuple装起来传入python PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, PyArray);
//函数调用(有返回值也是numpy PyArrayObject* pRet = (PyArrayObject*)PyEval_CallObject(pFunc, args);
//返回的numpy矩阵的row和col int Rows = pRet->dimensions[0], columns = pRet->dimensions[1]; cout << Rows << " " << columns << endl;
//将数据变回一般的指针 short* numpyptr2 = new short[90000000]; int j = 0; for (int Index_m = 0; Index_m < Rows; Index_m++) {
for (int Index_n = 0; Index_n < columns; Index_n++) {
numpyptr2[j] = *(short*)(pRet->data + Index_m * pRet->strides[0] + Index_n * pRet->strides[1]);//访问数据,Index_m 和 Index_n 分别是数组元素的坐标,乘上相应维度的步长,即可以访问数组元素 j++; }
} cout << numpyptr2[1000] << endl;
//释放内存 Py_CLEAR(pModule); Py_CLEAR(pFunc); Py_CLEAR(PyArray); Py_CLEAR(args); Py_CLEAR(pRet); delete[] numpyptr; delete[] numpyptr2;
Py_Finalize(); // 释放资源 return 0;}

      Python代码:

import numpy as npdef ff(data): print(data.shape) print(data[0][0]) print(type(data[0][0])) return data
c++调用python脚本,指针快速传递
c++调用python脚本,指针快速传递

运行结果

c++调用python脚本,指针快速传递

c++调用python脚本,指针快速传递
c++调用python脚本,指针快速传递
c++调用python脚本,指针快速传递

总结

利用numpy的方式不仅在传递速度上有很大的优势,并且可以与python完美的契合。


引用

https://blog.csdn.net/qq_38232171/article/details/103955681

https://zhuanlan.zhihu.com/p/79896193

https://www.pythonf.cn/read/116854