Python使用Cython实例,速度提升150倍以上

听过很多人说python速度慢,难以用于正式项目。本文介绍运用Cython编程,令Python代码的运行速度能提升数倍至数百倍。

1. 什么是 Cython?

Cython是Python编程语言的一个超集,它在Python和C/C++之间起着中间人的作用。Cython 允许python 与 C 风格代码混合编程,将C语句代码编译为静态格式,Python解释器运行时动态调用。当然,这意味着Cython永远不可能比C/C++快,稍微会慢一点。

目前,有许多知名的Python库,如NumPy和Pandas已经使用Cython来提高性能。也说明学习它很值得。下面我将演示如何用cython来改写1个python函数,并编译为二进制文件,测试比较前后性能变化。

2. 用 Cython 编写1个函数

1) 安装 cython

建议使用Python3.9 以上版本

pip3 install cython

2) 先编写1个纯python函数

我们编写1个函数,求圆周率近似值,数学公式如下
在这里插入图片描述
下面代码使用纯python实现此计算,默认n = 10000000, 并导入cProfile 模块来计算各函数消耗时间。

# 文件名: pi.py
import cProfile

def recip_square(i):
    return 1. / i ** 2

def approx_pi(n=10000000):
    val = 0.
    for k in range(1, n + 1):
        val += recip_square(k)
    pi = (6 * val) ** .5
    print("Approximate value of pi is: ", pi)
    return pi

if __name__ == '__main__':
	# 计算PI, 并统计耗时
    cProfile.run('approx_pi(10000000)')

3)使用cython重写该函数

cython 编程就是在python中使用C类型来申明变量,要先申明再使用。允许导入 C++原码。cython 代码的后缀名为 .pyx.
用cython语法来编写计算圆周率的函数,保存在cpi.pyx文件中。

# 文件名 cpi.pyx 
# cython: profile=True
cimport cython

@cython.profile(False)
cdef inline double recip_square(long long i):
    return 1.0 / (i * i)

def approx_pi(int n=10000000):
    cdef double val = 0.
    cdef int k
    for k in range(1, n + 1):
        val += recip_square(k)
    pi = (6 * val) ** .5
    print("Approximate value of pi is: ", pi)
    return pi

下面分析这段代码:

    cdef double val = 0.
    cdef int k

其中 double, int 是 C 语言浮点数、整数类型, cdef 表示使用静态编译

cdef inline double recip_square(long long i)

函数定义时,也用C类型申明参数类型,以及返回值类型.

def approx_pi(int n=10000000):

approx_pi函数名前面无 cdef ,表示这个函数不是纯C函数,在运行时会按python方式运行,解释器执行到 cdef double val = 0. 语句时,会从编译后的pyd动态链接库中调用C语句。
因为循环体内用到的变量与函数都是C语句,所以它的速度是非常快的。 函数的返回值 pi 变量是1个python变量。 但只调用了2次,因此对总体速度影响非常有限。

4) 编译 .pyx 文件

Cython 使用Python最通用的 setuptools 构建工具进行编译,但必须提前安装好C++编译器,linux上安装gcc, Windows 系统安装Visual Studio 或者minGW均可。
第1步,按setuptools 要求,编写setup.py构建脚本,setup.py构建的详细配置不赘述了,仅说明与cython相关的配置

# setup.py
from setuptools import setup
# 导入Cython 构建相关模块
from Cython.Build import cythonize

setup(
    ext_modules=cythonize("cpi.pyx"),
)

其中 ext_modules=cythonize("cpi.pyx") ,告知setuptools ,将cpi.pyx 按c来编译。

第2步,执行编译

python setup.py build_ext --inplace

会先转为 cpi.c 文件,再用C++编译器将 .c文件编译为动态链接库.pyd, linux则为.so。

3. 运行cython 函数

1) 导入 cython 模块

编写1个python测试函数,导入编译后的cython模块, 用 cProfile 模块收集运行时间

# test_cpi.py
import cpi
import cProfile

cProfile.run('cpi.approx_pi()')

2) 运行cython 函数

执行该函数,总耗时 0.042秒

python test_cpi.py
Approximate value of pi is:  3.1415925580959025
         5 function calls in 0.042 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.042    0.042 <string>:1(<module>)
        1    0.042    0.042    0.042    0.042 cpi.pyx:15(approx_pi)
        1    0.000    0.000    0.042    0.042 {built-in method builtins.exec}
        1    0.000    0.000    0.042    0.042 {cython_demo.cpi.approx_pi}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

4. 与纯python函数进行性能比较

1) 运行纯python函数

执行python函数,总耗时 6.287秒,

python pi.py
Approximate value of pi is:  3.1415925580959025
         10000005 function calls in 6.287 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)       
        1    0.000    0.000    6.287    6.287 <string>:1(<module>)
 10000000    3.944    0.000    3.944    0.000 pi.py:2(recip_square)
        1    2.343    2.343    6.287    6.287 pi.py:5(approx_pi)
        1    0.000    0.000    6.287    6.287 {built-in method builtins.exec} 
        1    0.000    0.000    0.000    0.000 {built-in method builtins.print}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

2) 比较结果

结果相比较, 6.287 / 0.042 = 150 倍
这个计算圆周率的函数,用cython语法简单改写后,性能提升了150倍。

5. 总结

从示例可以看出,密集运算的函数经过cython简单添加了C类型申明后,并没有增加更多代码,但速度提升非常明显,可以说,Cython是提升Python程序性能的最佳方式,因此值得深入学习,并在实际项目上运用。

使用cython时的一些建议,供参考:
1) cython语法相比python基本语法,学习难度略微增加,建议理解原理后,多做几遍练习。
2) 只在少数代码中使用cython, 主要是运算很重的函数或类。
3)在I/O密集型函数中使用cytthon效果不明显,或者可能没什么效果,如网络消息收发,大文件读写。

Cython中文文档:在线阅读(Gitee)

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值