专栏名称: 马哥Linux运维
马哥linux致力于linux运维培训,连续多年排名第一,订阅者可免费获得学习机会和相关Linux独家实战资料!
目录
相关文章推荐
51好读  ›  专栏  ›  马哥Linux运维

给力!Cython三分钟入门教程

马哥Linux运维  · 公众号  · 运维  · 2018-01-16 08:00

正文


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


编辑:根据一些我收到的反馈,大家似乎有点混淆——Cython是用来生成 C 扩展到而不是独立的程序的。所有的加速都是针对一个已经存在的 Python 应用的一个函数进行的。没有使用C 或 Lisp 重写整个应用程序,也没有手写C扩展 。只是用一个简单的方法来整合C的速度和C数据类型到 Python 函数中去。


现在可以说,我们能使下文的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 )))

return radius* 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" )

print "Pure python function" , t . timeit ( num ), "sec"


约2.2秒 。它太慢了!


让我们试着快速地用Cython改写它,然后看看是否有差别:


import math

def great_circle ( float lon1 , float lat1 , float lon2 , float lat2 ) :

cdef float radius = 3956.0

cdef float pi = 3.14159265

cdef float x = pi / 180.0

cdef float a , 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 )))

return radius* 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

cython c1 . pyx

# Compile the object file

gcc - c - fPIC - I / usr / include / python2 . 5 / c1 . c

# Link it into a shared library

gcc - shared c1 . o - o c1 . so


现在你应该有一个c1.so(或.dll)文件,它可以被Python import。现在运行一下:


t = timeit . Timer ( "c1.great_circle(%f,%f,%f,%f)" % ( lon1 , lat1 , lon2 , lat2 ),

"import c1" )

print "Cython function (still using python math)" , t . timeit ( num ), "sec"


约1.8秒 。并没有我们一开始期望的那种大大的性能提升。使用 python 的 math 模块应该是瓶颈。现在让我们使用C标准库替代之:


cdef extern from "math.h" :

float cosf ( float theta )

float sinf ( float theta )

float acosf ( float theta )

def great_circle ( float lon1 , float lat1 , float lon2 , float lat2 ) :

cdef float radius = 3956.0

cdef float pi = 3.14159265

cdef float x = pi / 180.0

cdef float a , 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 )))

return radius* 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" )

print "Cython function (using trig function from math.h)" , t . timeit ( num ), "sec"


现在有点喜欢它了吧?0.4秒 –比纯Python函数有5倍的速度增长。我们还有什么方法可以再提高速度?c2.great_circle()仍是一个Python函数调用,这意味着它产生Python的API的开销(构建参数元组等),如果我们可以写一个纯粹的C函数的话,我们也许能够加快速度。


cdef extern from "math.h" :

float cosf ( float theta )

float sinf ( float theta )

float acosf ( float theta )

cdef float _great_circle ( float lon1 , float lat1 , float lon2 , float lat2 ) :

cdef float radius = 3956.0

cdef float pi = 3.14159265

cdef float x = pi / 180.0

cdef float a , 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 )))

return radius * c

def great_circle ( float lon1 , float lat1 , float lon2 , float lat2 , int num ) :

cdef int i

cdef float x

for i from 0 < = i num :

x = _great_circle ( lon1 , lat1 , lon2 , lat2 )







请到「今天看啥」查看全文