性能优化

运行计时

演示代码,用于计算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))

性能优化

一般的想法和策略,具体的性能优化还要看代码的逻辑

  1. 使用内置数据类型,内置数量类型是用C语言编写的
  2. 使用lru_cache缓存数据
  3. 使用局部变量,与在每个作用域内查找变量的速度有关(局部变量>类级属性>全局变量)
  4. 使用函数,调用函数会将更多的东西放在堆栈中,从函数返回时会产生开销,但是与前面的局部变量有关系
  5. 不要访问属性,.操作符用于访问属性,这个操作符使用getattribute触发字典查找
  6. 替换字符串,在循环中使用模数(%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))

要不要优化

上面那些优化技巧其实对性能影响微乎其微,只是列出来简单了解下

不用遵循上面的原则,因为很多扩展应该也没有遵循这些规则,你总不能把扩展也改了吧

而且真正性能的瓶颈是在于你的代码逻辑或者网络连接等

综上所述,优化的第一原则是不做优化

results matching ""

    No results matching ""