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

2023-05-16

文章目录

    • 1. 什么是 Cython?
    • 2. 用 Cython 编写1个函数
      • 1) 安装 cython
      • 2) 先编写1个纯python函数
      • 3)使用cython重写该函数
      • 4) 编译 .pyx 文件
    • 3. 运行cython 函数
      • 1) 导入 cython 模块
      • 2) 运行cython 函数
    • 4. 与纯python函数进行性能比较
      • 1) 运行纯python函数
      • 2) 比较结果
    • 5. 总结

听过很多人说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)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python使用Cython实例,速度提升150倍以上 的相关文章

随机推荐

  • element-ui表格中复选框只能选中一个

    代码 xff1a span class token operator lt span el span class token operator span table ref span class token operator 61 span
  • python操作鼠标进行点击

    python中的pyautogui库可以操作鼠标 安装 xff1a pip install pyautogui span class token keyword import span time span class token keywo
  • go中的bcrypt加密

    1 bcrypt是不可逆的加密算法 xff0c 无法通过解密密文得到明文 2 bcrypt和其他对称或非对称加密方式不同的是 xff0c 不是直接解密得到明文 xff0c 也不是二次加密比较密文 xff0c 而是把明文和存储的密文一块运算得
  • go gRPC 服务端推送给客户端流demo

    具体文件目录看上一篇的grpc xff0c 这个demo演示的是服务端以流的形式推送给客户端 pb hello proto syntax span class token operator 61 span span class token
  • docker常用命令

    打包镜像 docker build span class token operator span t demo v1 span class token punctuation span 运行镜像 docker run span class
  • docker镜像加载原理

    docker的镜像实际上是由一层一层的文件系统组成 xff0c 这种层级的文件系统叫做UnionFS bootfs boot file system 主要包含bootloade和kernel xff0c bootloader主要是引导加载k
  • docker网络

    docker network常见的四种模式 桥接模式 bridge xff1a 为每一个容器分配 设置ip等 xff0c 并将容器连接到一个叫做docker0的虚拟网桥 xff0c docker网络默认为该模式 xff0c 使用 netwo
  • 玩客云刷armbian更新源报错The repository ‘http://apt.armbian.com stretch Release‘ does not have a Release file

    玩客云刷armbian系统更新源报错的解决方法 xff08 E The repository 39 http apt armbian com stretch Release 39 does not have a Release file x
  • GPT-4工具是软件工程师工作效率的倍增器

    1 xff0c 你现在正在哪个领域学习或工作呢 xff1f 你用过哪些AI智能工具 xff1f 主要从事AI算法数据集处理 xff0c 模型部署工具开发 xff0c 以及低代码工具开发 使用 Github的 Copilot 编程伴侣超过1个
  • HDFS Java API操作(IDEA版)

    目标 通过Java API来操作HDFS xff0c 完成的操作有 xff1a 文件上传 文件下载 新建文件夹 查看文件 删除文件 前提条件 1 Windows下安装好jdk1 8 2 Windows下安装好maven xff0c 这里使用
  • Ubuntu20.04 安装 CUDA10.1 和 CUDNN7.6.5

    说明 xff1a 本人的实验环境为 xff1a ubuntu20 04 xff0c 显卡 xff1a GTX1060 xff0c 已安装Nvidia驱动 查看你的NVIDIA显卡驱动是否支持cuda10 1版本 查看显卡驱动命令 xff1a
  • C++ 20 新特性 ranges 精讲

    C 43 43 20 新特性 ranges 精讲 C 43 43 20 中的 ranges 库使得使用 STL 更加舒适和强大 ranges 库中的算法是惰性的 xff0c 可以直接在容器上工作 xff0c 并且可以很容易地组合 简而言之
  • C语言学习篇(概念题)

    关键字static的作用是什么 1 xff09 在模块内 xff08 在函数内 xff09 xff0c 则此静态变量只能在该函数内使用 超出范围不能使用 但是它还占用内存 还存在 2 xff09 在模块内 xff08 但在函数体外 xff0
  • DMA控制器

    DMA控制器 DMA 简介 直接存储器访问 DMA 用于在外设与存储器之间以及存储器与存储器之间提供高速数据传 输 可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据 这样节省的 CPU 资源可 供其它操作使用 DMA 控制器基
  • STM32 软硬件调试

    调试IO口占用 JTMS SWDIO PA13 JTCK SWCLK PA14 JTDI PA15 JTDO PB3 JNTRST PB4 STM32 软硬件调试 硬件调试 硬件调试通常是通过JTAT或者SWD调试下载器来进行调试 首先需要
  • Linux 音频驱动

    Linux 音频驱动 硬件介绍 WM8960与IMX6ULL之间有两个通信接口 xff1a I2C和I2S 其中I2C用于配置WM8960 I2S用于音频数据传输 修改设备树文件 编写I2C子节点设备树 codec span class t
  • RK3399android源码编译ninja: build stopped: subcommand failed报错原因

    ninja build stopped subcommand failed 编译RK3399 android源码的时候报错 xff1a ninja build stopped subcommand failed 发现网上很多解决方法都不行
  • android7.1固定usb转串口设备节点名称

    使能ch340驱动 修改源码路径下mklinux sh添加make menuconfig图形配置一下 使能ch340驱动 配置环境变量 单独编译内核文件 打包镜像烧写文件 ubuntu固定USB串口设备端口号 参考链接 xff1a http
  • RK3568-GPIO

    参考链接 https wenku baidu com view 3313c154f142336c1eb91a37f111f18583d00c33 html 接口号 GPIOn xy n 32 x 1 8 y 其中对应关系 A 1 B 2 C
  • Python使用Cython实例,速度提升150倍以上

    文章目录 1 什么是 Cython xff1f 2 用 Cython 编写1个函数1 xff09 安装 cython2 xff09 先编写1个纯python函数3 xff09 使用cython重写该函数4 编译 pyx 文件 3 运行cyt