用cython编写程序能够得到更高的性能

我最喜欢的是python,它的代码优雅而实用,可惜纯粹从速度上来看它比大多数语言都要慢。大多数人也认为的速度和易于使用是两极对立的——编写c代码的确非常痛苦。而 cython 试图消除这种两重性,并让你同时拥有 python 的语法和 c 数据类型和函数——它们两个都是世界上最好的。请记住,我绝不是我在这方面的专家,这是我的第一次cython真实体验的笔记:

现在可以说,我们能使下文的great_circle 函数更快。所谓great_circle 是计算沿地球表面两点之间的距离的问题:
import math
def great_circle(lon1,lat1,lon2,lat2):
radius=3956#miles
x=math.pi/180.0
a=(90.0-lat1)*(x)
b=(90.0-lat2)*(x)
theta=(lon2-lon1)*(x)
c=math.acos((math.cos(a)*math.cos(b))+
(math.sin(a)*math.sin(b)*math.cos(theta)))
returnradius*c
让我们调用它 50 万次并测定它的时间 :
import timeit
lon1,lat1,lon2,lat2= -72.345,34.323,-61.823,54.826
num=500000
t=timeit.timer(p1.great_circle(%f,%f,%f,%f)%(lon1,lat1,lon2,lat2),
import p1)
printpure python function,t.timeit(num),sec
约2.2秒 。它太慢了!
让我们试着快速地用cython改写它,然后看看是否有差别:
import math
def great_circle(floatlon1,floatlat1,floatlon2,floatlat2):
cdeffloatradius=3956.0
cdeffloatpi=3.14159265
cdeffloatx=pi/180.0
cdeffloata,b,theta,c
a=(90.0-lat1)*(x)
b=(90.0-lat2)*(x)
theta=(lon2-lon1)*(x)
c=math.acos((math.cos(a)*math.cos(b))+(math.sin(a)*math.sin(b)*math.cos(theta)))
returnradius*c
请注意,我们仍然importmath——cython让您在一定程度上混搭python和c数据类型在。转换是自动的,但并非没有代价。在这个例子中我们所做的就是定义一个python函数,声明它的输入参数是浮点数类型,并为所有变量声明类型为c浮点数据类型。计算部分它仍然使用了python的 math 模块。
现在我们需要将其转换为c代码再编译为python扩展。完成这一部的最好的办法是编写一个名为setup.py发布脚本。但是,现在我们用手工方式 ,以了解其中的巫术:
# this will create a c1.c file - the c source code to build a python extension
cythonc1.pyx
# compile the object file
gcc-c-fpic-i/usr/include/python2.5/c1.c
# link it into a shared library
gcc-sharedc1.o-oc1.so
现在你应该有一个c1.so(或.dll)文件,它可以被python import。现在运行一下:
t=timeit.timer(c1.great_circle(%f,%f,%f,%f)%(lon1,lat1,lon2,lat2),
import c1)
printcython function (still using python math),t.timeit(num),sec
约1.8秒 。并没有我们一开始期望的那种大大的性能提升。使用 python 的 math 模块应该是瓶颈。现在让我们使用c标准库替代之:
cdef externfrommath.h:
floatcosf(floattheta)
floatsinf(floattheta)
floatacosf(floattheta)
def great_circle(floatlon1,floatlat1,floatlon2,floatlat2):
cdeffloatradius=3956.0
cdeffloatpi=3.14159265
cdeffloatx=pi/180.0
cdeffloata,b,theta,c
a=(90.0-lat1)*(x)
b=(90.0-lat2)*(x)
theta=(lon2-lon1)*(x)
c=acosf((cosf(a)*cosf(b))+(sinf(a)*sinf(b)*cosf(theta)))
returnradius*c
与 import math 相应,我们使用cdef extern 的方式使用从指定头文件声明函数(在此就是使用c标准库的math.h)。我们替代了代价高昂的的python函数,然后建立新的共享库,并重新测试
t=timeit.timer(c2.great_circle(%f,%f,%f,%f)%(lon1,lat1,lon2,lat2),
import c2)
printcython function (using trig function from math.h),t.timeit(num),sec
现在有点喜欢它了吧?0.4秒 –比纯python函数有5倍的速度增长。我们还有什么方法可以再提高速度?c2.great_circle()仍是一个python函数调用,这意味着它产生python的api的开销(构建参数元组等),如果我们可以写一个纯粹的c函数的话,我们也许能够加快速度。
cdef externfrommath.h:
floatcosf(floattheta)
floatsinf(floattheta)
floatacosf(floattheta)
cdef float_great_circle(floatlon1,floatlat1,floatlon2,floatlat2):
cdef floatradius=3956.0
cdef floatpi=3.14159265
cdef floatx=pi/180.0
cdef floata,b,theta,c
a=(90.0-lat1)*(x)
b=(90.0-lat2)*(x)
theta=(lon2-lon1)*(x)
c=acosf((cosf(a)*cosf(b))+(sinf(a)*sinf(b)*cosf(theta)))
returnradius*c
defgreat_circle(floatlon1,floatlat1,floatlon2,floatlat2,intnum):
cdef inti
cdef floatx
forifrom0< =i x=_great_circle(lon1,lat1,lon2,lat2)
returnx
请注意,我们仍然有一个python函数( def ),它接受一个额外的参数 num。这个函数里的循环使用for i from 0 < = i< num: ,而不是更pythonic,但慢得多的for i in range(num):。真正的计算工作是在c函数(cdef)中进行的,它返回float类型。这个版本只要0.2秒——比原先的python函数速度提高10倍。
为了证明我们所做的已经足够优化,可以用纯c写一个小应用,然后测定时间:
#include
#include
#define num 500000
floatgreat_circle(floatlon1,floatlat1,floatlon2,floatlat2){
floatradius=3956.0;
floatpi=3.14159265;
floatx=pi/180.0;
floata,b,theta,c;
a=(90.0-lat1)*(x);
b=(90.0-lat2)*(x);
theta=(lon2-lon1)*(x);
c=acos((cos(a)*cos(b))+(sin(a)*sin(b)*cos(theta)));
returnradius*c;
}
intmain(){
inti;
floatx;
for(i=0;i< =num;i++)
x=great_circle(-72.345,34.323,-61.823,54.826);
printf(%f,x);
}
用gcc -lm -octest ctest.c编译它,测试用time./ctest …大约0.2秒 。这使我有信心,我cython扩展相对于我的c代码也极有效率(这并不是说我的c编程能力很弱)。
能够用 cython 优化多少性能通常取决于有多少循环,数字运算和python函数调用,这些都会让程序变慢。已经有一些人报告说在某些案例上 100 至 1000 倍的速度提升。至于其他的任务,可能不会那么有用。在疯狂地用 cython重写 python 代码之前,记住这一点:
“我们应该忘记小的效率,过早的优化是一切罪恶的根源,有 97% 的案例如此。“——donaldknuth
换句话说,先用 python 编写程序,然后看它是否能够满足需要。大多数情况下,它的性能已经足够好了……但有时候真的觉得慢了,那就使用分析器找到瓶颈函数,然后用cython重写,很快就能够得到更高的性能。

柔性和刚性柔性版PCB怎样做可以加强
天猫精灵的确在阿里的重视之下首战告捷,搭载AliGenie2.0的火眼
控创工控机在钢铁行业的应用及介绍
苹果Campus 2“飞船总部”完工,航拍照大气,2017年大举迁入
Wolfspeed 200mm莫霍克谷器件工厂向中国终端客户批量出货碳化硅
用cython编写程序能够得到更高的性能
苹果缺乏创新力 iPhone全线降价也难改变现状
芝能月报:10月中国动力电池月度报告
说到性价比就不得不提到OPPOR9S、华为mate9和iPhone6s了,仍是旗舰中的精品
自举电路的原理和作用
我国发展氢燃料汽车行业决心强烈
距离矢量协议
降噪耳机哪个牌子好?千元降噪蓝牙耳机推荐
三项专利技术合一的FLIR MR277,凭实力获得国际奖项
UWB人员室内定位系统可实现哪些功能
RFID技术面对的是怎样的挑战
放大器的截止频率怎么算?
华为畅享7最新消息,华为畅享7KFC中国典藏版来袭,只要998!(1099~),遇见1987,你更爱肯德基还是更爱畅享
万向集团竞购美国电动车电池生产商已获发改委批准
在公共服务领域,大数据也越发显示出有效供给作用