Cython学习笔记1:利用Cython加速Python运行速度
- Cython
- Cython 的核心特点:
- 利用Cython加速Python运行速度
- 1. Cython加速Python运行速度原理
- 2. 不使用Cython
- 3. 使用Cython加速
- (1)使用pip安装 cython 和 setuptools 库
- (2)安装c语言编译器
- (3)创建要编译为动态链接库的.py文件
- (4)创建setup.py
- (5)把.py转化为库文件
- (6)python程序调用库
- (7)annotate参数
- (8)定义变量的类型加速python运行速度
Cython
Cython 是一个用于将 Python 代码转换为 C 语言扩展模块的编程语言。它允许你在 Python 中编写 C 风格的代码,从而提高性能,尤其是在需要大量计算的情况下。通过将 Python 代码与 C 代码混合使用,Cython 既保留了 Python 的简洁性,又能提升程序的执行速度。
Cython 的核心特点:
- 提高性能:Cython 可以将 Python 代码编译成 C 语言代码,然后生成高效的 C 扩展模块。它适用于需要频繁计算的任务,尤其是数值计算和循环处理。
- 与 C 代码交互:Cython 允许直接调用 C 函数和访问 C 数据结构,从而减少了 Python 和 C 之间的接口调用开销。
- 简化 C 接口:与直接使用 C 语言不同,Cython 通过 Python 风格的语法简化了 C 接口的使用,开发者可以快速将 Python 代码提升到 C 代码的性能。
- 无缝集成:Cython 代码可以与现有的 Python 代码无缝集成,开发者不需要重写整个程序,通常只需要优化性能瓶颈部分。
利用Cython加速Python运行速度
以计算圆周率 π 为例子,使用Nilakantha 级数来计算 π 值,计算公式如下:
1. Cython加速Python运行速度原理
把 python 代码转化为 c 语言代码,然后再编译成动态链接库,最后使用 python 程序调用这个库
2. 不使用Cython
创建 2 个 .py文件,fun.py
和 Nilakantha_main.py
fun.py
用于写计算的函数
Nilakantha_main.py
用于调用函数,查看结果和运行时间
fun.py
代码如下
def calculate_pi(count: int) -> float:
pi = 3.0
sign = 1
for i in range(2, count * 2, 2):
term = sign * 4.0 / (i * (i + 1) * (i + 2))
pi += term
sign *= -1
return pi
Nilakantha_main.py
代码如下
import time
import fun
def main():
start_time = time.time()
print(fun.calculate_pi(20_000_000))
end_time = time.time()
print('Time:', end_time - start_time)
if __name__ == '__main__':
main()
运行Nilakantha_main.py
文件后,结果如下:
3.141592653589787
Time: 4.7696967124938965
这段代码设置了迭代次数为2千万次,最后耗时4.76秒
下面使用cython加速
3. 使用Cython加速
(1)使用pip安装 cython 和 setuptools 库
使用如下命令进行安装,使用清华镜像源,这样速度更快
pip install cython setuptools -i https://pypi.tuna.tsinghua.edu.cn/simple
安装好后再安装就会出现下面的结果
(2)安装c语言编译器
下面的c语言编译器安装一款就可以了,博主是Windows11系统,安装的是Visiual Studio 2022
- GCC (GNU Compiler Collection)
- Clang
- Microsoft Visual C++ (MSVC)
- MinGW (Minimalist GNU for Windows)
(3)创建要编译为动态链接库的.py文件
新建一个名为fun_compile.py
的文件,用于将它最后编译为动态链接库,让 python 程序调用
fun_compile.py
代码如下
# cython: language_level=3
def calculate_pi(count: int) -> float:
pi = 3.0
sign = 1
for i in range(2, count * 2, 2):
term = sign * 4.0 / (i * (i + 1) * (i + 2))
pi += term
sign *= -1
return pi
# cython: language_level=3
这一行代码的作用是:指示 Cython 编译器使用 Python 3 语法的指令,告诉 Cython 如何解析和编译 Python 代码,确保代码遵循 Python 3 的语法规则,而不是 Python 2
(4)创建setup.py
setup.py
是 Python 项目中用于打包和分发代码的脚本,它包含了项目的元数据和配置,用于指导 Python 工具(setuptools)如何构建、安装和分发项目。
setup.py
中的代码如下
from Cython.Build import cythonize
from setuptools import setup
setup(
ext_modules=cythonize(["fun_compile.py"]),
)
fun_compile.py是编译为动态链接库的 python 代码
(5)把.py转化为库文件
当前目录下的文件如下展示
在此目录下,执行命令
python setup.py build_ext --inplace
过程展示如下
运行完成后,目录里会多出一个 .c 文件和一个库文件
(6)python程序调用库
修改Nilakantha_main.py
,导入fun_compile,如果既存在fun_compile.py,也存在fun_compile.py产生的库,会优先调用动态链接库
。代码如下:
import time
import fun
import fun_compile
def main():
start_time = time.time()
print(fun.calculate_pi(20_000_000))
end_time = time.time()
print('Time:', end_time - start_time)
start_time = time.time()
print(fun_compile.calculate_pi(20_000_000))
end_time = time.time()
print('Time:', end_time - start_time)
if __name__ == '__main__':
main()
运行结果如下:
可以看到确实加速了
(7)annotate参数
修改setup.py
,加入一个参数annotate=True
from Cython.Build import cythonize
from setuptools import setup
setup(
ext_modules=cythonize(["fun_compile.py"], annotate=True),
)
然后执行命令
python setup.py build_ext --inplace
结果会多出来一个.html文件
annotate=True 参数的作用是:生成 Cython 编译时的注释文件,用于查看 Cython 将 Python 代码转换为 C 代码的详细过程和每一步的性能分析。
通过浏览器打开这个.html文件
黄色线条的代码表示翻译后的代码依赖 python 代码,颜色越深,依赖越强
点击“+”开头的行,可查看 Cython 为其生成的 c语言 代码
点开红色框的这一行,可以看到这行代码都是用 python实现的
如果想让这段代码不都用 python 来实现,可以给其中的变量添加类型,这就是这段代码没有用c语言实现的原因,因为cython不知道变量的类型。
(8)定义变量的类型加速python运行速度
修改fun_compile.py
文件,代码如下:
# cython: language_level=3
import cython
def calculate_pi(count: int) -> float:
pi = 3.0
sign = 1
for i in range(2, count * 2, 2):
term = sign * 4.0 / (i * (i + 1) * (i + 2))
pi += term
sign *= -1
return pi
def calculate_pi_annotated(count: int) -> float:
pi: cython.double = 3.0
sign: cython.int = 1
i: cython.int
for i in range(2, count * 2, 2):
term: cython.double = sign * 4.0 / (i * (i + 1) * (i + 2))
pi += term
sign *= -1
return pi
重新编译
python setup.py build_ext --inplace
修改Nilakantha_main.py
,代码如下:
import time
import fun
import fun_compile
def main():
start_time = time.time()
print(fun.calculate_pi(20_000_000))
end_time = time.time()
print('Time:', end_time - start_time)
start_time = time.time()
print(fun_compile.calculate_pi(20_000_000))
end_time = time.time()
print('Time:', end_time - start_time)
start_time = time.time()
print(fun_compile.calculate_pi_annotated(20_000_000))
end_time = time.time()
print('Time:', end_time - start_time)
if __name__ == '__main__':
main()
运行后结果如下
可以看到加入变量类型加入后的代码运行效果更快了。