TIOBE每个月都会发布流行编程语言榜单,这里会列出最流行的20种语言。排序并不能说明语言的好坏,而是反映了某个软件开发领域的受欢迎程度。语言的发展不是越来越普遍,而是越来越专注于领域。有些语言注重简单和高效,比如python。内置的list和dict结构比c/c++更容易使用,但该语言也为安全和易用性牺牲了一些性能。在一些领域,比如通信,性能很关键,但这并不意味着这个领域的coder只能在c/c++的陷阱里挣扎,比如可以使用多种语言的混合编程。我见过的Python和c/c++混合编程的一个很好的应用是网络模拟软件NS3(NetworkSimulator3)。其内部计算引擎需要使用高性能,但在用户建模部分需要灵活易用。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中使用ctypesmoduel将python类型转换为c/c++类型。首先写一段累加值的c代码:extern"C"{intaddBuf(char*data,intnum,char*outData);}intaddBuf(char*data,intnum,char*outData){for(inti=0;igcc-pthread-fno-strict-aliasing-g-O2-DNDEBUG-g-fwrapv-O3-Wall-Wstrict-prototypes-fPICaddbuf.c-oaddbuf.o最后编写Python代码,使用ctypes库,将python类型转换成c语言需要的类型,然后调用带参数的so库函数:fromctypesimport*#cdll,c_intlib=cdll.LoadLibrary('libmathBuf.so')callAddBuf=lib.addBufnum=4numbytes=c_int(num)data_in=(c_byte*num)()foriinrange(num):data_in[i]=idata_out=(c_byte*num)()ret=lib.addBuf(data_in,numbytes,data_out)#调用so库中的函数在C/C++程序中使用Python.h,编写wrap打包接口该方法需要修改c/c++代码,处理外部函数中的输入/输出参数,适配python的参数。写一段c代码将外部输入参数作为shell命令执行:国际统计局;if(!PyArg_ParseTuple(args,"s",&command))//将args参数处理为字符串类型,给command赋值returnNULL;sts=系统(命令);//调用系统命令if(sts<0){PyErr_SetString(SpamError,"系统命令失败");返回空值;}返回PyLong_FromLong(sts);//将返回结果转为PyObject类型}//方法表staticPyMethodDefSpamMethods[]={{"system",spam_system,METH_VARARGS,"Executeashellcommand."},{NULL,NULL,0,NULL}};//模块初始化函数PyMODINIT_FUNCinitspam(void){PyObject*m;//m=PyModule_Create(&spammodule);//v3.4m=Py_InitModule("spam",SpamMethods);如果(m==NULL)返回;SpamError=PyErr_NewException("spam.error",NULL,NULL);Py_INCREF(SpamError);PyModule_AddObject(m,"error",SpamError);}处理中所有输入输出参数都处理为PyObject对象,然后使用转换函数将python数据类型转换为c/c++中的类型,返回参数为同理处理。与第一种方法相比,初始化函数较多。这部分是将编译好的so库当作一个python模块所必需的。像这样使用python:imoprtspamspam.system("ls")使用c/c++编写python扩展,参见:http://docs.python.org/2.7/ex...使用SWIG生成独立的wrap文件。该方法并不是新的方法,实际上是在第二种方法的基础上进行的封装。SWIG是一种开发工具,可帮助将用C或C++编写的软件嵌入并与各种其他高级编程语言链接。SWIG可以适用于各种类型的语言,包括常见的脚本编译语言,如Perl、PHP、Python、Tcl、Ruby、PHP、C#、Java、R等。在操作上,就是写一个c/c++程序的独立接口声明文件(通常很简单),swig会分析c/c++源程序,自动分析接口应该如何封装。指定目标语言后,swig将生成额外的包装器源文件。编译so库时,将打包文件编译链接在一起。看一个C代码示例:intsystem(constchar*command){sts=system(command);如果(sts<0){返回NULL;}returnsts;}在c源码中去掉适配python的包,只定义system函数本身比第二种方法简洁很多,并且消除了c代码和python的耦合代码,使得c代码更通用。然后编写swig接口声明文件spam.i:%modulespam%{#include"spam.h"%}%include"spam.h"%include"typemaps.i"intsystem(constchar*INPUT);这是一段Language-independentmoduledeclaration,创建一个名为spam的模块,为system做一个声明,主要是声明参数为入参。然后执行swig编译程序:>swig-c++-pythonspam.iswig会生成spam_wrap.cxx和spam.py两个文件。先看spam_wrap.cxx,生成的文件很长,但关键是函数的封装:传入封装函数的PyObject对象在内部进行转换,最后调整源码中的系统函数。生成的另一个spam.py其实是用另外一层python包裹了so库(其实是多余的):这里使用了_spam模块,这里的扩展名其实是_spam。swig在python上的应用请参考:http://www.swig.org/Doc1.3/Py...下面是编译安装python模块。Python提供了distutils模块,可以方便的编译安装python。模块。编写一个安装脚本setup.py如下:执行pythonsetup.pybuild完成编译。该程序将创建一个构建目录,其中包含下面已编译的so库。so库放在当前目录下。其实Python可以通过import加载模块。当然你也可以使用pythonsetup.pyinstall将模块安装到语言扩展库-site-packages目录下。buildpython扩展可以参考https://docs.python.org/2/ext...混合编程性能分析在混合编程的使用场景中,最重要的事情之一就是性能。那么本节将通过几个小实验来验证混合编程的性能,或者说如何编写可以利用混合编程性能的程序。我们使用冒泡排序算法来验证性能。1、实验一使用冒泡程序验证python和c/c++程序的性能差距Python版的冒泡程序:defbubble(arr,length):j=length-1whilej>=0:i=0whileiarr[i+1]:tmp=arr[i+1]arr[i+1]=arr[i]arr[i]=tmpi+=1j-=1c语言版本风险冒泡排序voidbubble(int*arr,intlength){intj=length-1;诠释我;国际临时工;while(j>=0){i=0;while(iarr[i+1]){tmp=arr[i+1];arr[i+1]=arr[i];arr[i]=tmp;}我+=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来定义数组对象,这样也省去了调用时数据类型转换的开销:importtimefromctypesimport*IntArray100=c_int*100arr=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()foriinxrange(100000):libbubble.initArr(arr1,arr,100)libbubble.bubble(arr1,100)time2=time.time()printtime2-time1再次执行:为了减少错误,将循环增加到100,000次。结果cnative程序优化参数编译后耗时0.65s左右。python使用c扩展后(同样的编译参数),执行起来只需要2.3s左右。3、实验三C语言用PyObject处理输入参数。这个方法还是在python中使用list加载待排序的数组,在c函数中将list赋值给数组,然后进行排序。排序后,再排序Raw列表赋值。循环排序10万次,执行时间约1.0s。4、实验4使用swig封装c方法,在接口文件中声明%array_class(int,intArray);然后把initArray当做Python中的数组,修改为10万种。python版本的程序(编译参数相同)执行时间仅需0.7s左右,比c原生程序慢7%左右。结论1.Python的列表效率很低。在高性能场景下,避免对链表进行大量的循环、取值、赋值操作。如果需要,最好使用ctype中的数组,或者用c语言实现。2.比较耗时的CPU密集型逻辑应该交给c/c++实现,可以扩展python。