Cython模块

Cython入门
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1. 理解Cpython和Cython的区别
Cpython:C语言实现的Python解释器,类似Jpython,IronPython,代表标识>>>
Cython:用于编写Python的C扩展的语言,用C高效实现某些程序,再给python调用.简单的认为就是给Python加上了静态类型后的语法。
2. 使用Cython的优点
综合了Python的优势语言简短,写起来方便,和C的速度,编译性语言,将代码编译成机器码形式运行.Python最大的缺点就是运行慢(解释性语言).
3. Cython用户指南
http://docs.cython.org/en/latest/src/userguide/language_basics.html#cython-file-types
4.Cython使用(pycharm\vs等IDE)
1>.创建名为.pyx后缀的文件,文件内写符合C格式的代码(较难)
2>.创建setup.py文件
3>.python setup.py install 生成.c文件
4>.进入cpython,import 模块
使用案例:Helloworld
1.创建名为Helloworld.pyx的文件
2.不需要创建setup.py文件,直接在函数里导入
import pyximport
pyximport.install()
import 模块名
导入函数:模块名.函数名(参数)
编译说明:
.pyx文件将要被cython编译成.c文件
.c文件然后被C编译器编译成.pyd文件(可被Python直接调用)
5.要点
1>.在 Cython 中,类型标注对于提升速度是至关重要的
2>.C语言在定义函数的时候,参数是要设定类型的,变量使用前需要定义类型
3>.难点在代码转换,如何将Python形式的代码转换成C语言形式
4>在进行整形计算的时候注意可能会溢出,c的int类型由大小限制
6.特点
是支持作为语言一部分的可选静态类型声明。将源代码转换为优化的C / C ++代码,并编译为Python扩展模块。

#####Jupyter 中使用Cython

1
2
1.首先加载cython,%load_ext Cython
2.在写函数的时候需要加上%%cython即可对其进行编译

15952142114321595214448387

Cython语句和表达式
1
2
3
4
5
6
7
8
9
10
缺点:需要会C语言相关知识,能够将Python代码通过Python+C实现(重计算的部分\频繁调用)
总体速度:纯C>C+Python>纯Python
1.Cython的语句和表达式遵循Python的语法
2.Cython里没有->操作符,用·替代;Cython里没有指针的取值操作符,用P[0]替代*p;Cython里取变量的地址操作符&;在Cython里空指针用Null表示,而且Null是保留关键字,不能用0表示
3.Cython里用<>进行强制转换,而非() eg: a = <int>b
4.Cython的for循环:
①同样支持Python的for in range
②处于性能考虑,Cython对for循环进行了优化,在循环前先定义变量
eg: cdef int i
for i in range(10)
Cython语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
1. 定义一个C变量
cdef int n = 123 声明整形
cdef float score 声明浮点型(单精度)
cdef int an[20] 声明一维数组,长度为20,以0填充
cdef int an[10][10] 声明二维数组
cdef list particles 声明列表
cdef dict events 声明字典
cdef set unique_names 声明集合
cdef (double, int, float) d = (1.0, 2, 3.0)
cdef bint a = True 或者 cdef bint a = 1 声明bool类型
cdef str mystring = 'foo' 声明字符串
cdef char* data = "snfxjh" 声明字符串,当字符串中含有中文时候不适用
2. 强制类型转换 <> eg: a = <int>b
3. cdef/def/cpdef/ctypedef
def在python和cython中均可以调用,cdef在c系列可以调用,cpdef在两者都可以调用,但要知道返回的类型,且丧失了cython的类型安全,不推荐这么做.也就是说我们想要在Python中调用的函数使用def定义,不需要调用的函数使用cdef定义.
ctypedef用它来为类型取一个新的名字
python程序中看不到cdef函数,python只能直接调用def的函数,cdef的函数只能通过Python调用def的函数来调用
4.import 和 cimport
cimport可以访问导入模块下的C函数或属性,而import可以访问Python函数或属性
import numpy as np
cimport numpy as np #并没有真正导入,只是允许访问模块下的C函数或属性
5.cython中创建矩阵
cdef int carr[3][3][3] #创建一个三维矩阵,类型为int
cdef np.ndarray h = np.zeros([xmax, ymax], dtype=int)
6.调用C函数
可以在cython包里的cython/includes中找到可以导入哪些包
7.边界检查(比如列表的索引)
在函数前添加装饰器
cimport cython
@cython.boundscheck(False)
@cython.wraparound(False)
去掉边界检查可以提升速度,每次都进行边界检查非常耗时,而且有些代码是不可能产生越界的问题的,所以这个操作并不总是需要被执行,但是如果不进行边界检查,如果碰巧超出了边界,则在最佳情况下将使程序崩溃,在最坏的情况下将破坏数据.
8.数组(array)
from cpython cimport array
import array
cdef array.array a = array.array('i', [1, 2, 3])
cdef int[:] ca = a

img

img

1595234872493

#####Cython代码样例

######1.样例一号(求质数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
首先,创建.pyx文件,创建prime.pyx,写代码,C特性变量使用前必须先声明
def primes(int nb_primes):
cdef int n, i, len_p
cdef int p[1000]
if nb_primes > 1000:
nb_primes = 1000
len_p = 0
n = 2
while len_p < nb_primes:
for i in p[:len_p]:
if n % i == 0:
break
else:
p[len_p] = n
len_p += 1
n += 1
result_as_list = [prime for prime in p[:len_p]]
return result_as_list
然后,在代码里导入模块名
import pyximport
pyximport.install()
import prime
print(prime.primes(100))
2.样例二号(比较C程度不同的代码速度

https://www.jianshu.com/p/9410db8fbf50 计算沿地球表面两点之间的距离

 wps4

1594973454534

img

img

速度对比综合:大多数情况下,Python的性能是足够好的,一旦循环、数字运算和Python函数调用上去了,性能就会相应地下降,在这种情况下,建议使用Cython进行优化

3.样例三号(传矩阵参数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
一.
cdef int sum3d(int[:, :, :] arr) nogil:
cdef size_t i, j, k, I, J, K
cdef int total = 0
I = arr.shape[0]
J = arr.shape[1]
K = arr.shape[2]
for i in range(I):
for j in range(J):
for k in range(K):
total += arr[i, j, k]
return total
说明:传的arr为三维矩阵,且矩阵内数据为int类型

二.
import numpy as np
cimport numpy as np
DTYPE = np.int
ctypedef np.int_t DTYPE_t
def naive_convolve(np.ndarray f, np.ndarray g):
if g.shape[0] % 2 != 1 or g.shape[1] % 2 != 1:
raise ValueError("Only odd dimensions on filter supported")
assert f.dtype == DTYPE and g.dtype == DTYPE
cdef int vmax = f.shape[0]
cdef int wmax = f.shape[1]
cdef int smax = g.shape[0]
cdef int tmax = g.shape[1]
cdef int smid = smax // 2
cdef int tmid = tmax // 2
cdef int xmax = vmax + 2 * smid
cdef int ymax = wmax + 2 * tmid
cdef np.ndarray h = np.zeros([xmax, ymax], dtype=DTYPE)
cdef int x, y, s, t, v, w
cdef int s_from, s_to, t_from, t_to
cdef DTYPE_t value
for x in range(xmax):
for y in range(ymax):
s_from = max(smid - x, -smid)
s_to = min((xmax - x) - smid, smid + 1)
t_from = max(tmid - y, -tmid)
t_to = min((ymax - y) - tmid, tmid + 1)
value = 0
for s in range(s_from, s_to):
for t in range(t_from, t_to):
v = x - smid + s
w = y - tmid + t
value += g[smid - s, tmid - t] * f[v, w]
h[x, y] = value
return h
或者使用 def naive_convolve(object[DTYPE_t, ndim=2] f,object[DTYPE_t, ndim=2] g)也可以,速度稍微慢点
或者使用 def naive_convolve(np.ndarray[DTYPE_t, ndim=2] f,np.ndarray[DTYPE_t, ndim=2] g):
分析Cython程序的好坏
1
2
3
1.cython -a yfxl.pyx (yfxl.pyx为cython文件名,会生成的.html文件
2.在jupyter中使用 %%cython --annotate
3.通过生成的报告可以看出来转换的程度,白色线条转换为纯C,黄色的为与Python有交互

######优化(由简单到复杂)

1
2
3
1. 将数据类型由动态转换成静态(变量使用前先cdef定义一下)----->这一步就对速度有显著提升
2. Python加C混合使用,尽可能多的使用C(如import仍使用Python)
3. 完全使用C,比如import math 转换成使用C的标准库实现

######总结

1
2
1. 数据类型必须使用前定义,但不是全部,Cython根据其分配推导局部变量的类型,这也可以减少在任何地方显式指定类型的需要(如循环的i),添加类型过多会使代码的可读性降低,因此请适度使用它们
2. 循环和重计算的代码尽量使用Cython代码实现