Cython是Python的超集,可让您显着提高代码速度。 您可以添加可选的类型声明以获得更大的好处。 Cython将您的代码转换为优化的C / C ++,然后将其编译为Python扩展模块。
在本教程中,您将学习如何安装Cython,免费获得Python代码的即时性能提升,以及如何通过添加类型和分析代码来真正利用Cython。 最后,您将学习更多高级主题,例如与C / C ++代码和NumPy的集成,可以进一步探索以获得更大的收益。
数位勾股三元
毕达哥拉斯是希腊的数学家和哲学家。 他以毕达哥拉斯定理而闻名,该定理指出,在直角三角形中,三角形边的平方和等于斜边的平方。 勾股三元组是a,b和c的任意三个正整数,使得a² + b² = c²
。 这是一个程序,该程序查找其成员不超过提供的限制的所有勾股三元组。
import time
def count(limit):
result = 0
for a in range(1, limit + 1):
for b in range(a + 1, limit + 1):
for c in range(b + 1, limit + 1):
if c * c > a * a + b * b:
break
if c * c == (a * a + b * b):
result += 1
return result
if __name__ == '__main__':
start = time.time()
result = count(1000)
duration = time.time() - start
print(result, duration)
Output:
881 13.883624076843262
显然有881个三元组,程序花了不到14秒才找到它。 时间不算太长,但是足够烦人。 如果我们想找到更多的三元组到更高的极限,我们应该找到一种使它更快运行的方法。
事实证明,这里有更好的算法,但是今天,我们专注于使用Cython加快Python的速度,而不是寻找勾股三元组的最佳算法。
pyximport轻松提升
使用Cython的最简单方法是使用特殊的pyximport功能。 这是一条语句,可以即时编译您的Cython代码,让您在没有太多麻烦的情况下享受本机优化的好处。
您需要将代码进行cythonize放在其自己的模块中,在主程序中编写一行设置,然后照常导入。 让我们看看它是什么样子。 我将函数移到了自己的文件pythagorean_triples.pyx。 扩展名对Cython非常重要。 激活Cython的行是import pyximport; pyximport.install()
import pyximport; pyximport.install()
。 然后,它仅使用count()函数导入模块,然后在main函数中调用它。
import time
import pyximport; pyximport.install()
import pythagorean_triples
def main():
start = time.time()
result = pythagorean_triples.count(1000)
duration = time.time() - start
print(result, duration)
if __name__ == '__main__':
main()
Output:
881 9.432806253433228
纯Python函数的运行时间延长了50%。 我们通过添加一行来提高了速度。 一点也不差。
构建自己的扩展模块
尽管pyximport在开发过程中确实很方便,但它仅适用于纯Python模块。 通常在优化代码时,您想引用本机C库或Python扩展模块。
为了支持这些功能,并避免每次运行都动态编译,您可以构建自己的Cython扩展模块。 您需要添加一个小setup.py文件,并记住在运行程序时每次修改Cython代码时都要进行构建。 这是setup.py文件:
from distutils.core import setup
from Cython.Build import cythonize
setup(
ext_modules = cythonize("pythagorean_triples.pyx")
)
然后,您需要构建它:
$ python setup.py build_ext --inplace
Compiling pythagorean_triples.pyx because it changed.
[1/1] Cythonizing pythagorean_triples.pyx
running build_ext
building 'pythagorean_triples' extension
creating build
creating build/temp.macosx-10.7-x86_64-3.6
gcc -Wno-unused-result -Wsign-compare -Wunreachable-code
-DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
-I/Users/gigi.sayfan/miniconda3/envs/py3/include
-arch x86_64 -I/Users/gigi.sayfan/miniconda3/envs/py3/include
-arch x86_64
-I/Users/gigi.sayfan/miniconda3/envs/py3/include/python3.6m
-c pythagorean_triples.c
-o build/temp.macosx-10.7-x86_64-3.6/pythagorean_triples.o
gcc -bundle -undefined dynamic_lookup
-L/Users/gigi.sayfan/miniconda3/envs/py3/lib
-L/Users/gigi.sayfan/miniconda3/envs/py3/lib
-arch x86_64
build/temp.macosx-10.7-x86_64-3.6/pythagorean_triples.o
-L/Users/gigi.sayfan/miniconda3/envs/py3/lib
-o pythagorean_triples.cpython-36m-darwin.so
从输出中可以看到,Cython生成了一个名为pythagorean_triples.c的C文件,并将其编译为特定于平台的.so文件,该文件是Python现在可以像其他任何本机扩展模块一样导入的扩展模块。
如果您好奇,请看一下生成的C代码。 它很长(2789行),很钝,并且包含许多使用Python API所需的额外内容。 让我们删除pyximport并再次运行我们的程序:
import time
import pythagorean_triples
def main():
start = time.time()
result = pythagorean_triples.count(1000)
duration = time.time() - start
print(result, duration)
if __name__ == '__main__':
main()
881 9.507064819335938
结果与pyximport几乎相同。 但是,请注意,我仅在测量cythonized代码的运行时间。 我没有测量pyximport即时编译cythonized代码需要多长时间。 在大型程序中,这可能很重要。
在代码中添加类型
让我们将其带入一个新的高度。 Cython不仅是Python,而且还添加了可选的类型。 在这里,我仅将所有变量定义为整数,并定义性能飞速增长的对象:
# pythagorean_triples.pyx
def count(limit):
cdef int result = 0
cdef int a = 0
cdef int b = 0
cdef int c = 0
for a in range(1, limit + 1):
for b in range(a + 1, limit + 1):
for c in range(b + 1, limit + 1):
if c * c > a * a + b * b:
break
if c * c == (a * a + b * b):
result += 1
return result
----------
# main.py
import time
import pyximport; pyximport.install()
import pythagorean_triples
def main():
start = time.time()
result = pythagorean_triples.count(1000)
duration = time.time() - start
print(result, duration)
if __name__ == '__main__':
main()
Output:
881 0.056414127349853516
是。 没错 通过定义几个整数,程序运行时间不到57毫秒,而使用纯Python则需要13秒以上。 几乎提高了250倍。
分析您的代码
我使用了Python的时间模块,该模块可以测量墙壁时间,并且在大多数情况下都非常不错。 如果要更精确地计时小代码片段,请考虑使用timeit模块。 这是使用timeit衡量代码性能的方法:
>>> import timeit
>>> timeit.timeit('count(1000)', setup='from pythagorean_triples import count', number=1)
0.05357028398429975
# Running 10 times
>>> timeit.timeit('count(1000)', setup='from pythagorean_triples import count', number=10)
0.5446877249924
timeit()
函数采用一条语句来执行,未测量的设置代码以及执行测量的代码的次数。
进阶主题
我只是在这里划伤表面。 您可以通过Cython做更多的事情。 以下是一些可以进一步提高代码性能或允许Cython与其他环境集成的主题:
- 调用C代码
- 与Python C API和GIL交互
- 在Python中使用C ++
- 将Cython代码移植到PyPY
- 使用并行
- Cython和NumPy
- 在Cython模块之间共享声明
结论
Cython只需很少的努力就可以产生两个数量级的性能改进。 如果您使用Python开发非凡的软件,Cython无疑是明智之举。 它的开销很小,您可以逐步将其引入代码库。
此外,不要犹豫,看看我们在市场上有哪些可供出售和研究的东西 ,也不要犹豫,使用下面的提要来问任何问题并提供宝贵的反馈。
翻译自: https://code.tutsplus.com/tutorials/speeding-python-with-cython--cms-29557