您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

【Cython】Cython三分钟入门教程

5b51 2022/1/14 8:25:21 python 字数 28111 阅读 836 来源 www.jb51.cc/python

<pstyle=\"font-size:14px;font-family:tahoma,arial,\'宋体\';\">我最喜欢的是Python,它的代码优雅而实用,可惜纯粹从速度上来看它比大多数语言都要慢。大多数人也认为的速度和易于使用是两极对立

概述

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

<code class="language-python">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@H_<a href="https://www.jb51.cc/tag/502/" target="_blank" class="keywords">502</a>_6@</pre><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;让我们<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a>它 50 万次并测定它的时间 :</p><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;</p><pre><code class="language-python"&gt;import timeit 

lon1,lat2 = -72.345,34.323,-61.823,54.826
num = 500000

t = timeit.Timer("p1.greatcircle(%f,%f,%f)" % (lon1,lat2),"import p1")
print "Pure python function",t.timeit(num),"sec"@H
502_6@

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@H_<a href="https://www.jb51.cc/tag/502/" target="_blank" class="keywords">502</a>_6@</pre><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;让我们<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a>它 50 万次并测定它的时间 :</p><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;</p><pre><code class="language-python"&gt;import timeit 

def great_circle(lon1,lat1,lon2,lat2):
radius = 3956 #miles
x = math.pi/180.0

lon1,lat2 = -72.345,34.323,-61.823,54.826
num = 500000

t = timeit.Timer("p1.greatcircle(%f,%f,%f)" % (lon1,lat2),"import p1")
print "Pure python function",t.timeit(num),"sec"@H
502_6@

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@H_<a href="https://www.jb51.cc/tag/502/" target="_blank" class="keywords">502</a>_6@</pre><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;让我们<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a>它 50 万次并测定它的时间 :</p><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;</p><pre><code class="language-python"&gt;import timeit 

lon1,lat2 = -72.345,34.323,-61.823,54.826
num = 500000

t = timeit.Timer("p1.greatcircle(%f,%f,%f)" % (lon1,lat2),"import p1")
print "Pure python function",t.timeit(num),"sec"@H
502_6@

<code class="language-python">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@H_<a href="https://www.jb51.cc/tag/502/" target="_blank" class="keywords">502</a>_6@</pre><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;请注意,我们仍然import math——cython让您在一定程度上混搭Python和C数据类型在。转换是<a href="https://www.jb51.cc/tag/zidong/" target="_blank" class="keywords">自动</a>的,但并非没有代价。<a href="https://www.jb51.cc/tag/zaizhe/" target="_blank" class="keywords">在这</a>个例子中我们所做的就是定义<a href="https://www.jb51.cc/tag/yige/" target="_blank" class="keywords">一个</a>Python<a href="https://www.jb51.cc/tag/hanshu/" target="_blank" class="keywords">函数</a>,声明它的输入参数是浮点数类型,并为所有变量声明类型为C浮点数据类型。计算部分它仍然使用了Python的 math 模块。</p><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;现在我们需要将其转换为C<a href="https://www.jb51.cc/tag/daima/" target="_blank" class="keywords">代码</a>再编译为Python扩展。完成这一部的最好的办法是编写<a href="https://www.jb51.cc/tag/yige/" target="_blank" class="keywords">一个</a>名为setup.py发布脚本。但是,现在我们用手工方式 ,以了解其中的巫术:</p><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;</p><pre><code class="language-objc"&gt;# 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@H_502_6@

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@H_<a href="https://www.jb51.cc/tag/502/" target="_blank" class="keywords">502</a>_6@</pre><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;请注意,我们仍然import math——cython让您在一定程度上混搭Python和C数据类型在。转换是<a href="https://www.jb51.cc/tag/zidong/" target="_blank" class="keywords">自动</a>的,但并非没有代价。<a href="https://www.jb51.cc/tag/zaizhe/" target="_blank" class="keywords">在这</a>个例子中我们所做的就是定义<a href="https://www.jb51.cc/tag/yige/" target="_blank" class="keywords">一个</a>Python<a href="https://www.jb51.cc/tag/hanshu/" target="_blank" class="keywords">函数</a>,声明它的输入参数是浮点数类型,并为所有变量声明类型为C浮点数据类型。计算部分它仍然使用了Python的 math 模块。</p><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;现在我们需要将其转换为C<a href="https://www.jb51.cc/tag/daima/" target="_blank" class="keywords">代码</a>再编译为Python扩展。完成这一部的最好的办法是编写<a href="https://www.jb51.cc/tag/yige/" target="_blank" class="keywords">一个</a>名为setup.py发布脚本。但是,现在我们用手工方式 ,以了解其中的巫术:</p><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;</p><pre><code class="language-objc"&gt;# this will create a c1.c file - the C source code to build a python extension

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

cython c1.pyx

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

gcc -shared c1.o -o c1.so@H_502_6@

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@H_<a href="https://www.jb51.cc/tag/502/" target="_blank" class="keywords">502</a>_6@</pre><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;请注意,我们仍然import math——cython让您在一定程度上混搭Python和C数据类型在。转换是<a href="https://www.jb51.cc/tag/zidong/" target="_blank" class="keywords">自动</a>的,但并非没有代价。<a href="https://www.jb51.cc/tag/zaizhe/" target="_blank" class="keywords">在这</a>个例子中我们所做的就是定义<a href="https://www.jb51.cc/tag/yige/" target="_blank" class="keywords">一个</a>Python<a href="https://www.jb51.cc/tag/hanshu/" target="_blank" class="keywords">函数</a>,声明它的输入参数是浮点数类型,并为所有变量声明类型为C浮点数据类型。计算部分它仍然使用了Python的 math 模块。</p><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;现在我们需要将其转换为C<a href="https://www.jb51.cc/tag/daima/" target="_blank" class="keywords">代码</a>再编译为Python扩展。完成这一部的最好的办法是编写<a href="https://www.jb51.cc/tag/yige/" target="_blank" class="keywords">一个</a>名为setup.py发布脚本。但是,现在我们用手工方式 ,以了解其中的巫术:</p><p style="font-size:14px;font-family:tahoma,'宋体';"&gt;</p><pre><code class="language-objc"&gt;# this will create a c1.c file - the C source code to build a python extension

cython c1.pyx

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

gcc -shared c1.o -o c1.so@H_502_6@

<code class="language-python">    t = timeit.Timer("c1.greatcircle(%f,"import c1")
print "Cython function (still using python math)","s@H
502_6@

<code class="language-python">cdef extern from "math.h":
float cosf(float theta)
float sinf(float theta)
float acosf(float theta)

def great_circle(float lon1,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*cec"

与 import math 相应,我们使用cdef extern 的方式使用从指定头文件声明函数(在此就是使用C标准库的math.h)。我们替代了代价高昂的的Python函数,然后建立新的共享库,并重新测试:
t = timeit.Timer("c2.great_circle(%f,"import c2")
print "Cython function (using trig function from math.h)",'宋体';">现在有点喜欢它了吧?0.4秒 -比纯Python函数有5倍的速度增长。我们还有什么方法可以再提高速度?c2.great_circle()仍是一个Python函数调用,这意味着它产生Python的API的开销(构建参数元组等),如果我们可以写一个纯粹的C函数的话,我们也许能够加快速度。

<p style="font-size:14px;font-family:tahoma,'宋体';">c3.pyx

<p style="font-size:14px;font-family:tahoma,'宋体';">

<code class="language-python">cdef extern from "math.h":
float cosf(float theta)
float sinf(float theta)
float acosf(float theta)

cdef float _great_circle(float lon1,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 lat2,int num):
cdef int i
cdef float x
for i from 0 < = i < num:
x = _greatcircle(lon1,lat2)
return x@H
502_6@

<p style="font-size:14px;font-family:tahoma,'宋体';">请注意,我们仍然有一个Python函数( def ),它接受一个额外的参数 num。这个函数里的循环使用for i from 0 < = i < num: ,而不是更Pythonic,但慢得多的for i in range(num):。真正的计算工作是在C函数(cdef)中进行的,它返回float类型。这个版本只要0.2秒——比原先的Python函数速度提高10倍。

<p style="font-size:14px;font-family:tahoma,'宋体';">为了证明我们所做的已经足够优化,可以用纯C写一个小应用,然后测定时间:

<p style="font-size:14px;font-family:tahoma,'宋体';">

<code class="language-python">#include <math .h>

include <stdio .h>

define NUM 500000

float great_circle(float lon1,float lat2){
float radius = 3956.0;
float pi = 3.14159265;
float x = pi/180.0;
float a,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)));
return radius*c;

}

int main() {
int i;
float x;
for (i=0; i < = NUM; i++)
x = greatcircle(-72.345,54.826);
printf("%f",x);
}@H
502_6@

<p style="font-size:14px;font-family:tahoma,'宋体';">用gcc -lm -o ctest ctest.c编译它,测试用time ./ctest ...大约0.2秒 。这使我有信心,我Cython扩展相对于我的C代码也极有效率(这并不是说我的C编程能力很弱)。

<p style="font-size:14px;font-family:tahoma,'宋体';">能够用 cython 优化多少性能通常取决于有多少循环,数字运算和Python函数调用,这些都会让程序变慢。已经有一些人报告说在某些案例上 100 至 1000 倍的速度提升。至于其他的任务,可能不会那么有用。在疯狂地用 Cython 重写 Python 代码之前,记住这一点:

<p style="font-size:14px;font-family:tahoma,'宋体';">"我们应该忘记小的效率,过早的优化是一切罪恶的根源,有 97% 的案例如此。"——Donald Knuth

<p style="font-size:14px;font-family:tahoma,'宋体';">换句话说,先用 Python 编写程序,然后看它是否能够满足需要。大多数情况下,它的性能已经足够好了……但有时候真的觉得慢了,那就使用分析器找到瓶颈函数,然后用cython重写,很快就能够得到更高的性能

<p style="font-size:14px;font-family:tahoma,'宋体';">外部链接
WorldMill(<a href="http://trac.gispython.org/projects/PCL/wiki/WorldMill" rel="nofollow" style="color:rgb(0,102,153);">http://trac.gispython.org/projects/PCL/wiki/WorldMill)——由Sean Gillies 用 Cython 编写的一个快速的,提供简洁的 python 接口的模块,封装了用以处理矢量地理空间数据的 libgdal 库。

<p style="font-size:14px;font-family:tahoma,'宋体';">编写更快的 Pyrex 代码(<a href="http://www.sagemath.org:9001/WritingFastPyrexCode" rel="nofollow" style="color:rgb(0,153);">http://www.sagemath.org:9001/WritingFastPyrexCode)——Pyrex,是 Cython 的前身,它们有类似的目标和语法。

<p style="font-size:14px;font-family:tahoma,'宋体';">

<p style="font-size:14px;font-family:tahoma,'宋体';">作者:perrygeo

<p style="font-size:14px;font-family:tahoma,'宋体';">译者:赖勇浩(http://laiyonghao.com

<p style="font-size:14px;font-family:tahoma,'宋体';">原文:http://www.perrygeo.net/wordpress/?p=116

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*cec"
<code class="language-python">cdef extern from "math.h":
float cosf(float theta)
float sinf(float theta)
float acosf(float theta)

cdef float _great_circle(float lon1,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 lat2,int num):
cdef int i
cdef float x
for i from 0 < = i < num:
x = _greatcircle(lon1,lat2)
return x@H
502_6@

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
<code class="language-python">#include <math .h>

include <stdio .h>

define NUM 500000

float great_circle(float lon1,float lat2){
float radius = 3956.0;
float pi = 3.14159265;
float x = pi/180.0;
float a,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)));
return radius*c;

}

int main() {
int i;
float x;
for (i=0; i < = NUM; i++)
x = greatcircle(-72.345,54.826);
printf("%f",x);
}@H
502_6@

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

def great_circle(float lon1,c

与 import math 相应,我们使用cdef extern 的方式使用从指定头文件声明函数(在此就是使用C标准库的math.h)。我们替代了代价高昂的的Python函数,然后建立新的共享库,并重新测试:
t = timeit.Timer("c2.great_circle(%f,"import c2")
print "Cython function (using trig function from math.h)",'宋体';">现在有点喜欢它了吧?0.4秒 -比纯Python函数有5倍的速度增长。我们还有什么方法可以再提高速度?c2.great_circle()仍是一个Python函数调用,这意味着它产生Python的API的开销(构建参数元组等),如果我们可以写一个纯粹的C函数的话,我们也许能够加快速度。

cdef float _great_circle(float lon1,c

def great_circle(float lon1,float lat2,int num):
cdef int i
cdef float x
for i from 0 < = i < num:
x = _greatcircle(lon1,lat2)
return x@H
502_6@

float great_circle(float lon1,float lat2){
float radius = 3956.0;
float pi = 3.14159265;
float x = pi/180.0;
float a,c;

}

int main() {
int i;
float x;
for (i=0; i < = NUM; i++)
x = greatcircle(-72.345,54.826);
printf("%f",x);
}@H
502_6@

def great_circle(float lon1,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*cec"

与 import math 相应,我们使用cdef extern 的方式使用从指定头文件声明函数(在此就是使用C标准库的math.h)。我们替代了代价高昂的的Python函数,然后建立新的共享库,并重新测试:
t = timeit.Timer("c2.great_circle(%f,"import c2")
print "Cython function (using trig function from math.h)",'宋体';">现在有点喜欢它了吧?0.4秒 -比纯Python函数有5倍的速度增长。我们还有什么方法可以再提高速度?c2.great_circle()仍是一个Python函数调用,这意味着它产生Python的API的开销(构建参数元组等),如果我们可以写一个纯粹的C函数的话,我们也许能够加快速度。

<code class="language-python">cdef extern from "math.h":
float cosf(float theta)
float sinf(float theta)
float acosf(float theta)

cdef float _great_circle(float lon1,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 lat2,int num):
cdef int i
cdef float x
for i from 0 < = i < num:
x = _greatcircle(lon1,lat2)
return x@H
502_6@

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

cdef float _great_circle(float lon1,c

def great_circle(float lon1,float lat2,int num):
cdef int i
cdef float x
for i from 0 < = i < num:
x = _greatcircle(lon1,lat2)
return x@H
502_6@

cdef float _great_circle(float lon1,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 lat2,int num):
cdef int i
cdef float x
for i from 0 < = i < num:
x = _greatcircle(lon1,lat2)
return x@H
502_6@

<code class="language-python">#include <math .h>

include <stdio .h>

define NUM 500000

float great_circle(float lon1,float lat2){
float radius = 3956.0;
float pi = 3.14159265;
float x = pi/180.0;
float a,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)));
return radius*c;

}

int main() {
int i;
float x;
for (i=0; i < = NUM; i++)
x = greatcircle(-72.345,54.826);
printf("%f",x);
}@H
502_6@

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

float great_circle(float lon1,float lat2){
float radius = 3956.0;
float pi = 3.14159265;
float x = pi/180.0;
float a,c;

}

int main() {
int i;
float x;
for (i=0; i < = NUM; i++)
x = greatcircle(-72.345,54.826);
printf("%f",x);
}@H
502_6@

float great_circle(float lon1,float lat2){
float radius = 3956.0;
float pi = 3.14159265;
float x = pi/180.0;
float a,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)));
return radius*c;

}

int main() {
int i;
float x;
for (i=0; i < = NUM; i++)
x = greatcircle(-72.345,54.826);
printf("%f",x);
}@H
502_6@

总结

以上是编程之家为你收集整理的【Cython】Cython三分钟入门教程全部内容,希望文章能够帮你解决【Cython】Cython三分钟入门教程所遇到的程序开发问题。


如果您也喜欢它,动动您的小指点个赞吧

除非注明,文章均由 laddyq.com 整理发布,欢迎转载。

转载请注明:
链接:http://laddyq.com
来源:laddyq.com
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


联系我
置顶