python之使用pybind11调用C为数值计算加速,python和C++混编极限提速之终极方法,让你的python数值计算速度起飞

一、前言

Python语言是目前比较火的语言,很容易上手,对数据处理也比较友好,可以用几行代码就能进行一些简单的数据处理工作。但是对于稍微大型的数值计算,或者一些涉及到大量循环的数值计算python的计算速度有点让人失望。

即使是使用numpy对算法算法进行优化,能提升的空间都非常有限了,当然网上有一行代码就提升100倍这种帖子,就是使用numba,但用了这个之后感受并不是太好,对于一个简单循环或许可以加速,对于复杂的循环效果也许并不好(可能是我不会使用,对numba没有太深入的研究)。

想要提升python的数值计算,目前看到比较好的解决方案就是用C给python写计算的lib,如果仅是数值计算,其实涉及到的C++语言的知识并不太多,基本就是for循环,if判断,以及一些STL库,个人觉得性价比还是挺高的。

Python调用C进行数值计算最大难点就是如何进行数据交换,也就是如何把输入参数的指针传递给C,以及如何返回输出参数指针给python。有大神早就将这些都封装好了,就是使用pybind11。

二、pybind11简介

Pybind11定义了一些python数据结构和C++的对应关系,使得数据交换变得非常简单,接下来简单介绍数值计算主要用的一些pybind11用到的知识。

1)获取pybind11

想要用到pybind11的功能当然得有pybind11的代码,网上可以直接下载到pybind11-master.rar文件,只需要下载到本地,然后解压就可以了。

2)cpp基本结构介绍

接下来直接就写代码吧,用Visual Studio创建个cpp文件(不会安装vs的可以自行百度安装一下,这个不是本文重点就不介绍了),本文使用的是VS2015。

首先include一些头文件。Emm…反正感觉会用的先include进来应该没错了。

接下来include本文的重点也就是pybind11的头文件,主要用到的是numpy.h和pybind11.h。

然后修改下命名空间,只是为了书写方便和import pandas as pd一个道理。

接下来要定义模块的入口,主要用的是PYBIND11_MODULE。

其中calc就是在python调用时候的模块名称,m是在C++文件中的模块实例,可以通过m.doc()给这个模块写下文档,一般就是这个模块是干什么的,可以在python环境help该模块名来查看,接下来通过m.def定义函数名,第一个参数是字符串,是在python调用时候的函数名,第二个参数是C++文件中的对应函数名称,三个参数是该函数的介绍。

以上写完之后就可以愉快的开始写函数了,整个cpp的结构大致就是下面这样了。

分别是头文件,命名空间,函数区,函数导出区。本文也按照常规套路hello world一把。

3)编译cpp成pyd文件

接下来需要通过编译器将cpp文件编译成python的pyd文件,首先要找到vs的x64本机工具命令提示符,本文以vs2015为例,在开始菜单直接搜索vs2015会出来以下选项(前提是你已经装了正确安装vs)。然后选择VS2014 x64 本机工具命令提示符(注意一定要是x64的)。

打开之后通过cd /d 路径,这个命令将路径切换到cpp所在路径。然后输入以下命令。

其中calc.cpp是待编译的cpp文件,路径1需要替换成前文获取到的pybind11-master文件夹下的include文件夹的所在路径,路径2需要替换成python安装路径的include文件夹的所在路径,路径3替换成python安装路径下的libs文件夹的所在路径,calc.pyd是生成的pyd名称,需要和cpp中模块名一致。

输入上述代码回车之后,编译成功会有如下信息打印,同时在cpp所在路径会产生四个文件,我们需要的只是后缀为.pyd的文件。

4)python执行

之后打开前文编译所有的python环境来进行测试,注意这里编译的pyd不同python版本是不能公用的(即python3.5编译出来的文件,python3.6并不能调用),有时同样是3.6编译出来的也不能使用,这个还没研究是怎么一个兼容关系。

Python调用pyd文件,一种简单的方法是用sys模块直接加入pyd文件所在路径,就可以直接调用,或者也可拷贝pyd文件到python能找到的路径下,比如python的安装路径下。然后执行写好的函数就有如下结果。

好的到此咱们已经完成了整个从下载所需文件,到cpp文件书写,然后对cpp进行编译,最后在python执行的全过程。

5)pybind11数据结构介绍

在数值计算用的最多的结构是array_t<>,可以是array_t<float>,array_t<int>或者array_t<double>,当然用的最多的肯定是array_t<double>了。

以上是一个两个矩阵输入,同时输出函数也是一个矩阵的函数声明,在array_t里面封装了数据矩阵的指针以及数据矩阵的大小。

通过以上方法获得了两个数据指针ptr1和ptr2,以及第一个矩阵的大小。

接下来定义输出参数,申请内存并获得数据指针。

上述简单介绍了py::array_t的基本用法,pybind11还定义了py::list等等数据封装内容,这些可以自行查看pybind11文档。地址为https://pybind11.readthedocs.io/en/master/intro.html,或者相应的pdf文档。

6)读取和数据存储

为了方便代码书写,本文会获得的指针进行宏定义,使得代码更有可读性,这里就涉及到了数据存储方式的问题。

这里示范的书写方法是默认输入矩阵和输出矩阵都是按行存储,这一点特别需要注意,其中numpy里的array数据默认是按行存储,也就是不管何种存储方式,只要对array数据进行copy操作之后,返回的数据都是按行存储。所以一般用array数据矩阵作为C函数输入时,进行copy操作是比较稳妥的方式,但是当矩阵较大时,进行矩阵的深拷贝的速度往往会很慢,甚至可能大于计算所需要的时间。


Python常用的库还有pandas,DataFrame数据的存储默认是按列存储,也就是从通过某个dataframe数据.values的方法获得的array数据矩阵,默认是按列存储。

那么如何知道一个array数据矩阵是按行存储还是按列存储呢,array数据有相应参数进行说明。

Array数据矩阵的flag属性下,有f_contiguous和c_contiguous这两个布尔类型的属性,当c_contiguous为真时,矩阵是按行存储,当f_contiguous为真时,矩阵是按列存储。其中f好像是表示Fortran语言,这种语言主要用来进行科学计算,是按列存储,据网上说超大型的数值计算都是用这种语言。c表示C语言,c语言是按行存储。平时用的比较多的数值计算的还有matlab,matlab是按列存储的。貌似对于面板数据来说,进行时间序列上的操作确实是按列存储比较占优。

7)C++函数结构

对于一个常用的输入为矩阵,输出也为矩阵的函数来说,大致的函数结构如下,

分为输入参数获取,输出参数定义,数据矩阵宏定义,运算逻辑,以及结果的返回。基本上的函数书写,仅需要关注运算逻辑如何书写,其他几部分内容都是相对固定的。

三、后语

前文内容详细介绍了如何构建整个cpp的内容结构,C++函数的内容结构,以及pybind11最常用的py::array_t的介绍。在给python写调用函数时,只需要专注于C++函数内容的运算逻辑区的代码即可,可以说已经非常简单了。

文中提到的pybind11-master.rar,pybind11用户pdf文档,关注【量化杂货铺】wx公众号回复【加速一】就可以获得。

这个只是本系列的第一篇介绍,后续有使得你运行速度更快的详细介绍,期待你的运算速度可以起飞~~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352