使用Cython加速Python

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

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值