14.13 给你的程序做性能测试

    如果你只是简单的想测试下你的程序整体花费的时间,通常使用Unix时间函数就行了,比如:

    如果你还需要一个程序各个细节的详细报告,可以使用 模块:

    1. bash % python3 -m cProfile someprogram.py
    2. 859647 function calls in 16.016 CPU seconds
    3.  
    4. Ordered by: standard name
    5.  
    6. ncalls tottime percall cumtime percall filename:lineno(function)
    7. 263169 0.080 0.000 0.080 0.000 someprogram.py:16(frange)
    8. 513 0.001 0.000 0.002 0.000 someprogram.py:30(generate_mandel)
    9. 262656 0.194 0.000 15.295 0.000 someprogram.py:32(<genexpr>)
    10. 1 0.036 0.036 16.077 16.077 someprogram.py:4(<module>)
    11. 262144 15.021 0.000 15.021 0.000 someprogram.py:4(in_mandelbrot)
    12. 1 0.000 0.000 0.000 0.000 os.py:746(urandom)
    13. 1 0.000 0.000 0.000 0.000 png.py:1056(_readable)
    14. 1 0.000 0.000 0.000 0.000 png.py:1073(Reader)
    15. 1 0.227 0.227 0.438 0.438 png.py:163(<module>)
    16. 512 0.010 0.000 0.010 0.000 png.py:200(group)
    17. ...
    18. bash %

    不过通常情况是介于这两个极端之间。比如你已经知道代码运行时在少数几个函数中花费了绝大部分时间。对于这些函数的性能测试,可以使用一个简单的装饰器:

    1.  
    2. import time
    3. from functools import wraps
    4.  
    5. def timethis(func):
    6. @wraps(func)
    7. def wrapper(*args, **kwargs):
    8. start = time.perf_counter()
    9. r = func(*args, **kwargs)
    10. end = time.perf_counter()
    11. print('{}.{} : {}'.format(func.__module__, func.__name__, end - start))
    12. return r
    13. return wrapper

    要测试某个代码块运行时间,你可以定义一个上下文管理器,例如:

    1. from contextlib import contextmanager
    2.  
    3. @contextmanager
    4. def timeblock(label):
    5. start = time.perf_counter()
    6. try:
    7. yield
    8. finally:
    9. print('{} : {}'.format(label, end - start))

    下面是使用这个上下文管理器的例子:

    1. >>> with timeblock('counting'):
    2. ... n = 10000000
    3. ... while n > 0:
    4. ... n -= 1
    5. ...
    6. counting : 1.5551159381866455
    7. >>>

    对于测试很小的代码片段运行性能,使用 timeit 模块会很方便,例如:

    1. >>> timeit('math.sqrt(2)', 'import math', number=10000000)
    2. 1.434852126003534
    3. >>> timeit('sqrt(2)', 'from math import sqrt', number=10000000)
    4. 1.0270336690009572
    5. >>>

    当执行性能测试的时候,需要注意的是你获取的结果都是近似值。 函数会在给定平台上获取最高精度的计时值。不过,它仍然还是基于时钟时间,很多因素会影响到它的精确度,比如机器负载。如果你对于执行时间更感兴趣,使用 time.process_time() 来代替它。例如:

    1. from functools import wraps
    2. def timethis(func):
    3. @wraps(func)
    4. def wrapper(*args, **kwargs):
    5. start = time.process_time()
    6. r = func(*args, **kwargs)
    7. end = time.process_time()
    8. print('{}.{} : {}'.format(func.__module__, func.__name__, end - start))
    9. return wrapper

    最后,如果你想进行更深入的性能分析,那么你需要详细阅读 、timeit 和其他相关模块的文档。这样你可以理解和平台相关的差异以及一些其他陷阱。还可以参考13.13小节中相关的一个创建计时器类的例子。

    原文: