0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

马哥Linux运维 2018-01-16 16:35 次阅读

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

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

print"Pure 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")

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

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

cdef externfrom"math.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")

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 externfrom"math.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< = inum:

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重写,很快就能够得到更高的性能。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • python
    +关注

    关注

    51

    文章

    4657

    浏览量

    83379
  • C数据
    +关注

    关注

    0

    文章

    2

    浏览量

    1787

原文标题:给力!Cython三分钟入门教程

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    arm中编写程序实现串口通讯,c语言编写。请大神指教!

    如上!arm中编写程序实现串口通讯,c语言编写。请大神指教!
    发表于 05-15 16:48

    单片机编写程序如何做到程序能够正常运行?

    在使用单片机编写程序的时候,“裸奔”的情况下,有5-7个任务,如何做到程序能够正常运行。
    发表于 08-06 06:20

    为 Arduino Mega 编写程序

    封控在家编写了一段为 Arduino Mega 开发板编写程序的labview程序。下图是为 Arduino Mega 开发板编写程序的前面板。编写
    发表于 05-24 09:33

    Cython 是什么?为什么会有 Cython

    Cython 估计很多人都听说过,它是用来对 Python 进行加速的。如果你在使用 Python 编程时,有过如下想法,那么 Cython 非常适合你。因为某些需求导致不得不编写一些多重嵌套的循环
    发表于 06-16 16:11

    用C语言编写程序实现通过按键使LED灯周期闪烁

    用C语言编写程序实现通过按键使LED灯周期闪烁。
    发表于 03-30 16:54 10次下载

    单片机中独立按键用查询方式编写程序

    单片机中独立按键用查询方式编写程序的实际距离应用
    发表于 06-08 14:10 9次下载

    VFP9.0编写程序

    数据库编写程序,VFP9.0编写程序,中文版利用数据库知识编写可以运行程序
    发表于 06-21 16:42 0次下载

    使用 RX65N 云套件开始编辑和编写程序

    使用 RX65N 云套件开始编辑和编写程序
    发表于 02-02 19:07 0次下载
    使用 RX65N 云套件开始编辑和<b class='flag-5'>编写程序</b>

    关于stm32编写程序的头文件合集

    关于stm编写程序的合集
    发表于 04-23 09:39 1次下载

    740族C编写程序包V.1.01用户手册

    740族C编写程序包V.1.01用户手册
    发表于 05-06 18:38 0次下载
    740族C<b class='flag-5'>编写程序</b>包V.1.01用户手册

    740族C编写程序包V.1.01用户手册

    740族C编写程序包V.1.01用户手册
    发表于 06-26 20:24 0次下载
    740族C<b class='flag-5'>编写程序</b>包V.1.01用户手册

    使用 RX65N 云套件开始编辑和编写程序

    使用 RX65N 云套件开始编辑和编写程序
    发表于 07-04 18:53 0次下载
    使用 RX65N 云套件开始编辑和<b class='flag-5'>编写程序</b>

    虚拟机linux怎么编写程序

    在虚拟机Linux上编写程序是一项非常常见且有用的任务。无论是开发一个简单的脚本、一个Web应用程序还是一个复杂的软件项目,Linux提供了强大的工具和环境来帮助你进行编程。本文将详细介绍如何在
    的头像 发表于 11-17 10:08 495次阅读

    怎样在codeblocks上编写程序

    在CodeBlocks上编写程序是一项相对简单而又重要的任务。CodeBlocks是一个广泛使用的集成开发环境(IDE),它能够帮助程序编写、编译和运行他们的代码。在本篇文章中,我们
    的头像 发表于 11-26 09:26 509次阅读

    codeblocks怎么编写程序

    ::Blocks编写程序的过程,以使您对其操作有更深入的了解。 首先,我们需要下载并安装Code::Blocks。您可以在官方网站https://www.codeblocks.org/上找到适用于您的操作系统的安装程序。安装过程相对简单,只需按照向导指示完成即可。 一旦安
    的头像 发表于 11-26 10:28 467次阅读