提升Python性能,媲美C语言

本篇文章是学习《Python金融大数据分析》第10章“高性能的Python”的笔记~

文章目录

  • 循环
    • numpy
    • numba
    • Cython
  • 递归
    • cython
    • 修饰器

循环

下面以计算平均数为例。原始代码为:

import random
def average_py(n):
	s=0
	for i in range(n):
		s+=random.random()
	return s/n

若n=10000000,大致运行2s。

numpy

numpy的优势在于其向量化能力。从形式上看,Python级别的循环消失了,循环在更深的一级上。平均数可变为:

import random
import numpy as np
def average_np(n):
	s=np.random.random(n)
	return s.mean()

若n=10000000,大致运行224ms。不过需要注意的是,虽然速度提升了,内存占用也更多。

numba

Numba可以动态编译纯Python代码,在简单情况下,它的应用非常直观。

import numba
average_nb=numba.jit(average_py)

n=10000000
average_nb(n) #第一次运行大概278ms
average_nb(n) #第二次运行81.7ms,第二次运行会明显变快。

提升Python性能,媲美C语言_第1张图片

以上运行结果显示,第一次运行较慢,第二次及之后运行速度明显加快。所以对于会重复运行的函数,可以用numba进行编译。(自己总结,非书中观点)
numba对性能的提升效果很明显,但是值得注意的是,许多情况下 Numba 并不适用,性能增进几乎难以察觉,甚至无法实现。

Cython

Cython=Python+C, 可以静态编译python代码,但是它的应用不想Numba那么简单,通常需要更改代码才能明显加速,即向python代码里面加入一些C语言元素。由于比较复杂,笔者就没有举例子了,需要更加深入复杂的学习~
但是一般用法是,在代码开头加上

%load_ext Cython
%%cpython

注意:使用之前要先安装Cython第三方包

递归

Python的常规递归函数实现也相对较慢。递归函数需要多次调用自身,Numba加速效果不明显。下面以斐波那契数列为例,介绍几种加速方法。

'''
原始函数
'''
def fib_rec_py1(n):
	if n<2:
		return n
	else:
		return fib_rec_py1(n-1)+fib_rec_py1(n-2)

当n=35时,执行时间约为3.74s。若用numba,运行时间大约为3.54s。

cython

'''
cython加速
'''
%%cython
def fib_rec_cy(int n):
	if n<2:
		return n
	else:
		return fib_rec_cy(n-1)+fib_rec_cy(n-2)

当n=35时,执行时间约为936ms。

修饰器

递归算法的主要问题是中间结果不会缓存,而是重新计算。为了避免出现这种特有的问题,可以使用一个装饰器(decorator)来负责缓存中间结果。它可以将执行速度提高好几个数量级。

'''
修饰器
'''
from functools import lru_cache as cache 
@cache(maxsize=None)
def fib_rec_py2(n):
	if n<2:
		return n
	else:
		return fib_rec_py2(n-1)+fib_rec_py2(n-2)

当n=35时,执行时间约为98us,几乎瞬间出结果。
递归的效率很低,如果可以,尽量不使用递归。比如斐波那契数列可以用以下方式计算:

def fib_it_py(n):
	x,y=0,1
	for i in range(1,n+1):
		x,y=y,x+y
	return x

当n=80时,运行时间大约为26us,如果用上Numba,cython,会更加迅速

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