基于cython提升python运算性能

问题

假设一个计算密集型任务,计算的函数是形如:

z = \sqrt{x}+ln(y)+x^{y}

计算N遍,N很大,那么在python中有多种实现方式最简单的两种方式:

1、基于numpy库

import numpy as np
def original_complex_calc(x, y):
    z = np.sqrt(x) * np.log(y) * np.power(x, y)
    return z

2、基于math库

import math
def original_complex_calc_math(x, y):
    z = math.sqrt(x) * math.log(y) * math.pow(x, y)
    return z

测试一下两者都循环运行1000w次的性能:

original_complex_calc 26.025455951690674
original_complex_calc_math 5.273261070251465

可以看到math库基础运算性能要比numpy要好,那有没有方法再提升一下性能呢,用cython掉c的库

cython

python容易调用c/c++的代码,使用cython可以有两个好处:

  • 首先可以提升python性能
  • 可以条用c/c++的库,比如opencv,做图像处理基本上都会用到

1、首先需要写一个pyx文件,内部完成调用c库逻辑和计算逻辑 

例如:calculation.pyx

# @Time : 2020/7/7
# @Author : 大太阳小白
# @Software: PyCharm
# @blog:https://blog.csdn.net/weixin_41579863
cdef extern from "math.h" nogil:
    double sqrt(double)
    double pow(double, double)
    double log(double)

cdef double _complex_calc(double a, double b):
    cdef double c = sqrt(a) * log(b) * pow(a,b)
    return c

def complex_calc(a, b):
    return _complex_calc(a, b)
  • Cython 程序的扩展名是 .pyx
  • Cython 的函数使用 cdef 定义,并且他可以给所有参数以及返回值指定类型。
  • 在 Python 程序中,是看不到 cdef 的函数的,需要借助complex_calc调用cdef函数

2、其次写一个setup.py文件对calculation.pyx进行编译生成动态链接库,setup.py内容如下:

 

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules = [Extension("calculation_cython", ["calculation.pyx"])]

setup(
    name='calculation app',
    language='c',
    cmdclass={'build_ext': build_ext},
    ext_modules=ext_modules,
)
  • Cython 编译器把 Cython 代码编译成调用了 Python 源码的 C/C++ 代码
  • 把生成的代码编译成动态链接库
  • Python 解释器载入动态链接库

使用命令生成链接库:

python setup.py build_ext --inplace 

linux系统上生成.so文件,windows系统上生成.pyd文件

3.直接使用import调用编译好的动态链接库

import calculation_cython

if __name__ == '__main__':
    a = 10.0
    b = 10
    print(calculation_cython.complex_calc(a, b))

最终性能测试一下

# @Time : 2020/7/7
# @Author : 大太阳小白
# @Software: PyCharm
# @blog:https://blog.csdn.net/weixin_41579863
import numpy as np
import calculation_cython
import time
import math


def original_complex_calc(x, y):
    z = np.sqrt(x) * np.log(y) * np.power(x, y)
    return z


def original_complex_calc_math(x, y):
    z = math.sqrt(x) * math.log(y) * math.pow(x, y)
    return z


if __name__ == '__main__':
    a = 10.0 # 设置成浮点防止numpy的power出现溢出错误
    b = 10
    print(original_complex_calc_math(a, b))
    print(original_complex_calc(a, b))
    print(calculation_cython.complex_calc(a, b))
    test_funs = [original_complex_calc, original_complex_calc_math, calculation_cython.complex_calc]
    for func in test_funs:
        start = time.time()
        for _ in range(1000 * 10000):
            func(a, b)
        print(func.__name__, time.time() - start)

结果:

72814134002.11803
72814134002.11803
72814134002.11803
original_complex_calc 26.025455951690674
original_complex_calc_math 5.273261070251465
complex_calc 1.5478901863098145

性能再次提升!

 

你可能感兴趣的:(python,python,numpy)