c include 多层目录_python+C、C++混合编程的应用

e3fc41c929474d63f00ceb55a4b88868.png

TIOBE每个月都会新鲜出炉一份流行编程语言排行榜,这里会列出最流行的20种语言。

排序说明不了语言的好坏,反应的不过是某个软件开发领域的热门程度。语言的发展不是越来越common,而是越来越专注领域。有的语言专注于简单高效,比如python,内建的list,dict结构比c/c++易用太多,但同样为了安全、易用,语言也牺牲了部分性能。

在有些领域,比如通信,性能很关键,但并不意味这个领域的coder只能苦苦挣扎于c/c++的陷阱中,比如可以使用多种语言混合编程。

我看到的一个很好的Python与c/c++混合编程的应用是NS3(Network Simulator3)一款网络模拟软件,它的内部计算引擎需要用高性能,但在用户建模部分需要灵活易用。NS3的选择是使用C/C++来模拟核心部件和协议,用python来建模和扩展。

这篇文章介绍python和c/c++三种混合编程的方法,并对性能加以分析。

混合编程的原理

首先要说一下python只是一个语言规范,实际上python有很多实现:CPython是标准Python,是由C编写的,python脚本被编译成CPython字节码,然后由虚拟机解释执行,垃圾回收使用引用计数,我们谈与C/C++混合编程实际指的是基于CPython解释上的。

除此之外,还有Jython、IronPython、PyPy、Pyston,Jython是Java编写的,使用JVM的垃圾回收,可以与Java混合编程,IronPython面向.NET平台。

python与C/C++混合编程的本质是python调用C/C++编译的动态链接库,关键就是把python中的数据类型转换成c/c++中的数据类型,给编译函数处理,然后返回参数再转换成python中的数据类型。

python中使用ctypes moduel,将python类型转成c/c++类型

首先,编写一段累加数值的c代码:

extern "C"
{int addBuf(char* data, int num, char* outData);
}int addBuf(char* data, int num, char* outData){for (int i = 0; i < num; ++i)
{
outData[i] = data[i] + 3;
}return num;
}

然后,将上面的代码编译成so库,使用下面的编译指令

>gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC addbuf.c -o addbuf.o

最后编写python代码,使用ctypes库,将python类型转换成c语言需要的类型,然后传参调用so库函数:

from ctypes import * # cdll, c_int
lib = cdll.LoadLibrary('libmathBuf.so')
callAddBuf = lib.addBuf
num = 4
numbytes = c_int(num)
data_in = (c_byte * num)()
for i in range(num):
data_in[i] = i
data_out = (c_byte * num)()
ret = lib.addBuf(data_in, numbytes, data_out) #调用so库中的函数

在C/C++程序中使用Python.h,写wrap包装接口

这种方法需要修改c/c++代码,在外部函数中处理入/出参,适配python的参数。写一段c代码将外部入参作为shell命令执行:


#include static PyObject* SpamError;static PyObject* spam_system(PyObject* self, PyObject* args){const char* command;int sts;if (!PyArg_ParseTuple(args, "s", &command)) //将args参数按照string类型处理,给command赋值return NULL;
sts = system(command); //调用系统命令if (sts < 0) {
PyErr_SetString(SpamError, "System command failed");return NULL;
}return PyLong_FromLong(sts); //将返回结果转换为PyObject类型
}//方法表static PyMethodDef SpamMethods[] = {
{"system", spam_system, METH_VARARGS,"Execute a shell command."},
{NULL, NULL, 0, NULL}
};//模块初始化函数PyMODINIT_FUNC initspam(void){
PyObject* m;//m = PyModule_Create(&spammodule); // v3.4
m = Py_InitModule("spam", SpamMethods);if (m == NULL)return;
SpamError = PyErr_NewException("spam.error",NULL,NULL);
Py_INCREF(SpamError);
PyModule_AddObject(m,"error",SpamError);
}

处理上所有的入参、出参都作为PyObject对象来处理,然后使用转换函数把python的数据类型转换成c/c++中的类型,返回参数按相同方式处理。比第一种方法多了初始化函数,这部分是把编译的so库当做python module所必需要做的。

python这样使用:

imoprt spam
spam.system("ls")

使用c/c++编写python扩展可以参见:http://docs.python.org/2.7/extending/extending.html

使用SWIG,来生成独立的wrap文件

这种方式并不能算是一种新方式,实际上是基于第二中方式的一种包装。SWIG是个帮助使用C或者C++编写的软件能与其它各种高级编程语言进行嵌入联接的开发工具。SWIG能应用于各种不同类型的语言包括常用脚本编译语言例如Perl, PHP, Python, Tcl, Ruby, PHP,C#,Java,R等。

操作上,是针对c/c++程序编写独立的接口声明文件(通常很简单),swig会分析c/c++源程序自动分析接口要如何包装。在指定目标语言后,swig会生成额外的包装源码文件。编译so库时,把包装文件一起编译、连接即可。看个c代码例子:

int system(const char* command){
sts = system(command);if (sts < 0) {return NULL;
}return sts;
}

c源码中去掉适配python的包装,仅定义system函数本身,这比第二种方式简洁很多,并且剔除了c代码与python的耦合代码,是c代码通用性更好。

然后编写swig接口声明文件spam.i:

%module spam%{#include "spam.h"%}%include "spam.h"%include "typemaps.i"
int system(const char* INPUT);

这是一段语言无关的模块声明,要创建一个叫spam的模块,对system做一个声明,主要是声明参数作为入参使用。然后执行swig编译程序:

>swig -c++ -python spam.i

swig会生成spam_wrap.cxx和spam.py两个文件。先看spam_wrap.cxx,这个生成的文件很长,但关键的就是对函数的包装:

ba144f50a1307a6cc690f5279094a87c.png

包装函数传入的还是PyObejct对象,内部进行了类型转换,最终调了源码中的system函数。

生成的了另一个spam.py实际上是对so库又用python包装了一层(实际比较多余):

e1d1192085dbae7183af30b41008cdba.png

这里使用_spam模块,这里实际上是把扩展命名为了_spam。关于swig在python上的应用可以参见:http://www.swig.org/Doc1.3/Python.html

下面就是编译和安装python 模块,Python提供了distutils module,可以很方便的编译安装python的module。像下面这样写一个安装脚本setup.py:

8230ec3381a143c3cfd90807a6411433.png

执行 python setup.py build,即可以完成编译,程序会创建一个build目录,下面有编译好的so库。so库放在当前目录下,其实Python就可以通过import来加载模块了。

当然也可以用 python setup.py install 把模块安装到语言的扩展库——site-packages目录中。关于build python扩展,可以参考https://docs.python.org/2/extending/building.html#building

混合编程性能分析

混合编程的使用场景中,很重要一个就是性能攸关。那么这小节将通过几个小实验验证下混合编程的性能如何,或者说怎样写程序能发挥好混合编程的性能优势。

我们使用冒泡排序算法来验证性能。

1、实验一 使用冒泡程序验证python和c/c++程序的性能差距

python版冒泡程序:

def bubble(arr,length):
j = length - 1while j >= 0:
i = 0while i < j:if arr[i] > arr[i+1]:
tmp = arr[i+1]
arr[i+1] = arr[i]
arr[i] = tmp
i += 1
j -= 1

c语言版冒泡排序

void bubble(int* arr,int length){int j = length - 1;int i;int tmp;while(j >= 0){
i = 0;while(i < j){if(arr[i] > arr[i+1]){
tmp = arr[i+1];
arr[i+1] = arr[i];
arr[i] = tmp;
}
i += 1;
}
j -= 1;
}
}

 使用一个长度为100内容固定的数组,反复排序10000次(每次排序后,再把数组恢复成原始序列),记录执行时间:在相同的机器上多次执行,Python版执行时间是10.3s左右,而c语言版本(未使用任何优化编译参数)执行时间只有0.29s左右。相比之下python的性能的确差很多(主要是python中list的操作跟c的数组相比,效率差非常多),但python中很多扩展都是c语言写的,目的就是为了提升效率,python用于数据分析的numpy库就拥有不错的性能。下个实验就验证,如果python使用c语言版本的冒泡排序扩展库,性能会提升多少。2、实验二 python语言使用ctypes方式调用

这里直接使用c_int来定义了数组对象,这也节省了调用时数据类型转换的开销:

import timefrom ctypes import *
IntArray100 = c_int * 100
arr = IntArray100(87,23,41, 3, 2, 9,10,23,0,21,5,15,93, 6,19,24,18,56,11,80,34, 5,98,33,11,25,99,44,33,78,52,31,77, 5,22,47,87,67,46,83, 89,72,34,69, 4,67,97,83,23,47, 69, 8, 9,90,20,58,20,13,61,99,7,22,55,11,30,56,87,29,92,67,99,16,14,51,66,88,24,31,23,42,76,37,82,10, 8, 9, 2,17,84,32,66,77,32,17, 5,68,86,22, 1, 0)... ...if __name__ == "__main__":
libbubble = CDLL('libbubble.so')
time1 = time.time()for i in xrange(100000):
libbubble.initArr(arr1,arr,100)
libbubble.bubble(arr1,100)
time2 = time.time()print time2 - time1

再次执行:

为了减少误差,把循环增加到10万次,结果c原生程序使用优化参数编译后用时0.65s左右。python使用c扩展后(相同编译参数)执行仅需2.3s左右。

3、实验三 在c语言中使用PyObject处理入参

这种方式是在python中依然使用list装入待排序数列,在c函数中把list赋值给数组,再进行排序,排好序后,再对原始list赋值。循环排序10万次,执行用时1.0s左右。

4、实验四 使用swig来包装c方法

在接口文件中声明%array_class(int,intArray);然后在Python中使用initArray来作为数组,同样修改成10万次排序。python版本的程序(相同编译参数)执行仅需0.7s左右,比c原生程序慢大概7%。

结论

1.python 的list效率非常低,在高性能场景下避免对list大量循环、取值、赋值操作。如需要最好使用ctype中的数组,或者是用c语言来实现。

2.应该把耗时的cpu密集型的逻辑交给c/c++实现,python使用扩展即可。

- END -

原文链接:

https://blog.51cto.com/12557234/2294219

文源网络,仅供学习之用,如有侵权,联系删除。

往期精彩

92c627e3e4943187070812e958902a03.png

◆  50款开源工具你都用过吗?

◆  你知道Python的就业方向和薪资吗?(赠学习资料)

◆  python网络爬虫的基本原理详解

e6ee98a570047f2674bbb219372ecc26.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/530050.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

校友会2019中国大学计算机,校友会2019中国计算机类一流专业排名,清华大学排名第一...

原标题&#xff1a;校友会2019中国计算机类一流专业排名&#xff0c;清华大学排名第一中国哪些高校的计算机类本科专业跻身2019世界一流专业、中国顶尖专业和中国一流专业行列&#xff1f;哪些计算机类本科专业是2019年中国高考最优秀考生的最佳选择&#xff1f;为了给2019年全…

查询结果取交集_Elasticsearch 查询过程中的 prefilter 原理

大家都知道在对索引执行查询的时候&#xff0c;需要在所有的分片上执行查询&#xff0c;因为无法知道被查询的关键词位于哪个分片&#xff0c;对于全文查询来说诚然如此&#xff0c;然而对于时序型的索引&#xff0c;当你从 my_index-* 中执行 now-3d 的范围查询时&#xff0c;…

语音识别插件_AnsweringMachine XS: 越狱理由之二,iPhone 电话语音答录机

Apps & Tweaks| Jailbreak Guide| iDevicesTweak&#xff1a;AnsweringMachine XSVersion&#xff1a;XSRepo&#xff1a;http://limneos.net/iOS Support&#xff1a;12-13Price&#xff1a;3.99iOS 开发人员 Elias Limneos 开发了电话辅助系列插件&#xff0c;AnsweringM…

计算机账务处理流程图,账务处理流程图

手工业务流程图账务处理流程主要有 5 种形式&#xff1a;记账凭证核算形式、科目汇总表核算形式、汇总记账凭证核算形式、日记总账核算形式、和多栏式日记账核算形式。不同的账务处理流程其差别主要体现在登记总账的方法和依据不同&#xff0c;其中科目汇总表核算形式最为常见&…

css滑动门的用处,CSS滑动门是什么?有什么用处?[web前端培训]

在制作网页导航时&#xff0c;经常会碰到导航栏长度不同&#xff0c;但背景相同的情形。此时如果通过拉伸背景图的方式来适应文本内容&#xff0c;就会造成背景图变形。在制作网页时&#xff0c;为了使各种特殊形状的背景能够自适应元素中的文本内容&#xff0c;并且不会变形&a…

Gen系列服务器,新计算、新体验 | 新华三全新HPE Gen10系列服务器响彻“云”端

数字经济时代的数据中心正在加速向云计算融合&#xff0c;用户将面临传统架构与云架构并存的混合IT模式。如何既拥有专有数据中心对数据完全可控以及对关键业务充分优化的优势&#xff0c;又能拥有云计算的灵活弹性&#xff1f;如何有效利旧并满足混合IT架构的需求&#xff1f;…

收藏功能_微软Edge获得了新的收藏夹菜单、PDF功能等

作为其今年早些时候概述的战略的一部分&#xff0c;微软Edge现在正在向所有Windows 10 PC推出。与经典的Edge不同&#xff0c;Chromium Edge与任何特定的Windows更新无关&#xff0c;但微软又开始为该浏览器进行了一系列令人兴奋的改进。新的Edge基于Chromium&#xff0c;它还带…

服务器LIMIT是什么信号,Postfix添加milter-limit配置方案

[安装环境]操作系统&#xff1a;CentOS 5.6MAT&#xff1a;POSTFIX2.8.4安装之前必须保证POSTFIX能正常收发信如果已经安装过Berkeley Db3以上版本可以不安装新的DB(但是注意引入db.so)[安装步骤]1、milter-limit-0.14.tar.gz及libsnert-1.71.6.tar.gz包的获取方法需要创建一个…

数据存储方式_视频监控系统的数据存储方式的概念及应用

DAS&#xff1a;直连存储&#xff0c;直连式存储与服务器主机之间的连接通常采用SCSI连接&#xff0c;SCSI通道是IO瓶颈;服务器主机SCSI ID资源有限&#xff0c;能够建立的SCSI通道连接有限。无论直连式存储还是服务器主机的扩展&#xff0c;从一台服务器扩展为多台服务器组成的…

vue从url中获取token并加入到 请求头里_轻流amp;amp;企业微信——获取打卡数据...

企业微信开放了打卡应用的api&#xff0c;功能包括查询打卡数据&#xff0c;能获取到用户、地点、时间、打卡类型等信息&#xff0c;在轻流中可以基于以上数据做一段时间内的迟到/事假等统计&#xff0c;以及更深层数据处理&#xff0c;方便管理。第一步&#xff1a;获取access…

单片机串口通信学号显示_触摸屏与单片机串口通信测试

工业现场在使用触摸屏的时候&#xff0c;与第三方控制器进行通信连接的时候&#xff0c;一般都是使用成熟的通信协议进行通信连接。而这些协议的实现过程&#xff0c;触摸屏厂商也已经在编程环境中进行了封装集成&#xff0c;对于使用的工程师来说&#xff0c;通信的数据交换过…

2020笔记本性价比之王_2020轻薄本性价比之王_2020轻薄本哪款好

随着科技的进步&#xff0c;如今笔记本的功能愈发强大&#xff0c;接听电话、开展商务会议等&#xff0c;因此现在人们对于笔记本电脑的依赖程度愈发明显。厚重的商务本和游戏本显然不适合随身携带&#xff0c;因此轻薄本的出现受到白领们的广泛吹捧&#xff0c;那么2020哪款轻…

asp绑定gridview属性_如何在ASP.NET Core中自定义Azure Storage File Provider

主题&#xff1a;如何在ASP.NET Core中自定义Azure Storage File Provider作者&#xff1a; Lamond Lu地址: https://www.cnblogs.com/lwqlun/p/10406566.html项目源代码&#xff1a; https://github.com/lamondlu/AzureFileProvider背景ASP.NET Core是一个扩展性非常高的框架…

mysql中为表创建副本_如何为数据库建立一个副本?

展开全部大多情况下&#xff0c;需要可靠而有效地克隆 MySQL 实例e69da5e887aa62616964757a686964616f31333433643663数据。这包括 MySQL 高可用的解决方案&#xff0c;其中需要在将实例加入组复制集群之前配置实例&#xff0c;或者在经典复制模型中将其添加为 Slave。为复制拓…

win10安装mysql5.7.15_win10上如何安装mysql5.7.16(解压缩版)

注&#xff1a;本文涉及的是解压缩版的安装安装教程下载mysql解压缩下载的文件修改ini文件(在解压缩后的mysql文件夹中)实际上修改的是my-default.ini文件在文件中添加如下路径和地址其中的data文件夹是没有的必须自己创建。# These are commonly set, remove the # and set as…

mysql如何备份一个表单_Mysql亿级数据大表单表备份

上一篇Mysql已有亿级数据大表按时间分区&#xff0c;介绍了亿级数据大表如何按时间分区&#xff0c;也留下了一个问题&#xff1a;备份亿级数据大表要耗时多久。本篇将就如何备份亿级数据大表展开讨论。注意&#xff1a;我这里所说的备份指的是数据从一张表拷贝到另外一张表&am…

mysql mos login_MySQL 中常用的函数

一、DATE_FORMAT()需求&#xff1a;按照日期月份统计数据&#xff0c;但数据库存储的格式是 2020-10-01 10:20:45 &#xff0c;因此需要格式化日期语法&#xff1a;DATE_FORMAT(date,format)第一个参数&#xff1a;指定的日期&#xff0c;第二个参数&#xff1a;需要获取的格式…

需求调研的方法及过程_培训需求调研方法

课程设计与开发是每一位职业培训师都必须会的技能&#xff0c;今天我们就来分享一下如何开发课程。第一节课&#xff0c;让我们先从培训需求调研开始。培训需求调研方法有很多&#xff0c;从个体层次分为&#xff1a;问卷法、观察法、访谈法&#xff1b;从组织层次分为&#xf…

将Go语言开发的Web程序部署到K8S

搭建K8S基础环境 如果已经有K8S环境的同学可以跳过&#xff0c;如果没有&#xff0c;推荐你看看我的《Ubuntu22加Minikue搭建K8S环境》&#xff0c;课程目录如下&#xff1a; Ubuntu22安装Vscode 下载&#xff1a;https://code.visualstudio.com/Download 安装命令&#…

mysql分表 动态扩容_数据库hash分表后的扩容方案

postgres的hash分表不停机扩容方案原来我们hash分表之后&#xff0c;数据扩容采用的是rehash&#xff0c;这样迁移全部的数据&#xff0c;比较麻烦。本次扩容利用hash环原理&#xff0c;并在此基础上做一些适应性的改动。首先假定哈希环的范围为0-1023&#xff0c;总共1024的数…