性能优化
运行计时
演示代码,用于计算e的x次方
# slow_program.py
from decimal import *
def exp(x):
getcontext().prec += 2
i, lasts, s, fact, num = 0, 0, 1, 1, 1
while s != lasts:
lasts = s
i += 1
fact *= i
num *= x
s += num / fact
getcontext().prec -= 2
return +s
print(exp(Decimal(150)))
print(exp(Decimal(400)))
print(exp(Decimal(3000)))
最懒惰的性能分析,使用Unix time命令,可用于计算整个程序的运行时间
time python slow_program.py
更详细的性能分析,使用cProfile
-s time是指根据内部时间cumtime对代码进行排序
python -m cProfile -s time slow_program.py
对具体的函数计时,可以使用简单的装饰器
# slow_program.py
from decimal import *
import time
from functools import wraps
def timeit_wrapper(func):
@wraps(func)
def wrapper(*args, **kwargs):
# perf_counter返回绝对值,包括Python程序进程不运行的时间,可能会受到机器负载的影响
# time.process_time()只返回用户时间,不包括系统时间,只是进程的时间
# 根据实际场景使用不同计时方式
start = time.perf_counter()
func_return_val = func(*args, **kwargs)
end = time.perf_counter()
print('{0:<10}.{1:<8} : {2:<8}'.format(func.__module__, func.__name__, end - start))
return func_return_val
return wrapper
@timeit_wrapper
def exp(x):
getcontext().prec += 2
i, lasts, s, fact, num = 0, 0, 1, 1, 1
while s != lasts:
lasts = s
i += 1
fact *= i
num *= x
s += num / fact
getcontext().prec -= 2
return +s
print('{0:<10} {1:<8} : {2:^8}'.format('module', 'function', 'time'))
exp(Decimal(150))
exp(Decimal(400))
exp(Decimal(3000))
性能优化
一般的想法和策略,具体的性能优化还要看代码的逻辑
- 使用内置数据类型,内置数量类型是用C语言编写的
- 使用lru_cache缓存数据
- 使用局部变量,与在每个作用域内查找变量的速度有关(局部变量>类级属性>全局变量)
- 使用函数,调用函数会将更多的东西放在堆栈中,从函数返回时会产生开销,但是与前面的局部变量有关系
- 不要访问属性,
.
操作符用于访问属性,这个操作符使用getattribute触发字典查找 - 替换字符串,在循环中使用模数(%s)或.format()会变慢,应该使用生成器f-string,f'{s} {t}'
- 生成器本身并没有更快,因为它们允许延迟计算,这里节省的是内存而不是时间
- 但是节省内存可能会使程序在实际运行时更快
使用lru_cache缓存提升性能
import functools
import time
@functools.lru_cache(maxsize=12)
def slow_func(x):
time.sleep(2)
return x
# 等待2秒才获得结果
print(slow_func(1))
# 结果已缓存,立即返回
print(slow_func(1))
# 等待2秒才获得结果
print(slow_func(3))
使用局部变量提升性能
import random
class FastClass:
value = 0
def do_stuff(self):
# 这可以加速循环中的查找
temp = self.value
for i in range(10000):
temp += 1
self.value = temp
def fast_function():
r = random.random
for i in range(10000):
# 在这里调用`r()`比全局的random.random()要快
print(r())
不访问属性提升性能
import re
from re import findall
def slow_func():
for i in range(10000):
# 慢
print(re.findall(r'\d', 'num1'))
def fast_func():
for i in range(10000):
# 快
print(findall(r'\d', 'num2'))
slow_func()
fast_func()
使用f-string提升性能
from string import Template
s = 's'
t = 't'
# 快
print(f'{s} {t}')
# 慢
print(s + ' ' + t)
print(' '.join((s, t)))
print('%s %s' % (s, t))
print('{} {}'.format(s, t))
print(Template('$s $t').substitute(s=s, t=t))
要不要优化
上面那些优化技巧其实对性能影响微乎其微,只是列出来简单了解下
不用遵循上面的原则,因为很多扩展应该也没有遵循这些规则,你总不能把扩展也改了吧
而且真正性能的瓶颈是在于你的代码逻辑或者网络连接等
综上所述,优化的第一原则是不做优化