前些时候,一个朋友突然问我:python做计算实在是太慢了,有什么办法可以加速python的运算吗?我说:简单啊,你直接调用外部c函数就行了,我印象中cython可以直接实现的。闻言,我那个朋友喜出望外,遂言:太好了,那你给我写个demo呗。。。
emmmm。。。
好吧,我承认我之前事实上只是知道可以这么做,真的要说实现。。。
唉,自己挖的坑,流着泪也要把它填平了。于是,趁着周末两天,我网上找了一些demo,然后自己实现了几种python调用外部c函数的实现方式。
不要问我为啥今天才发出来,问就是打字慢。
下面,话不多说,上干货!
c_types实现大约是最简单的外部c函数实现方法了,你只需要准备写好你的C函数实现,然后编译,最后调用就行了,无需任何中间文件,一切都是如此简单。
下面,我们以a到b的连续求和函数作为例子来进行说明:
完事,收工,一切都是如此顺利!
较之ctypes实现方法,cython方法会更加复杂一点,它不需要依赖于ctypes库,而是直接将c代码转译为python底层c实现中可读的代码,而后将这一部分封装为一个动态链接库。
因此,在这种情况下,我们完全可以将这个生成的动态链接库当成一个普通的python包来进行调用,其执行效率上也会优于ctypes方式的调用。
下面,我们来考察其具体实现。
由是,cython方法搞定收工!
注意到,cython方式构建动态链接库过程中,会调用cythonize函数,而这个函数会先生成一个.c中间文件,而这个中间文件即为我们的动态链接库中真实包含的c函数代码实现。
事实上,后续的setup函数就是针对这个.c中间文件进行编译并构建为动态链接库。
因此,我们可以绕过cythonize函数,直接自己来构建这个.c文件,然后进行动态链接库的构建。
而这,就是c extension方法的主要思路。
同样的,给出其操作流程如下:
综上,c extension方法搞定!
swig也是常用的python调用外部c函数的实现方法之一,其核心与上述cython完全相似,唯一的区别点在于,cython方法使用cython库来进行代码转义,而这里使用swig进行代码转义。。。
我们给出其操作流程如下:
至此,swig方法搞定!
现在,我们来比较一下上述各个方法调用外部c函数的性能。
我们重复执行10^7计算1到100的连续求和,得到各个方法的耗时如下:
方法 | 耗时 |
|---|---|
python | ~50s |
ctypes | ~7s |
cython | 1.88s |
c extension | 2.76s |
swig | 2.41s |
结论:
[1] 在python里调用C函数的三种方式 [2] python调用c和c++库(直接调用和使用swig) [3] SWIG and Python