Python性能测试实战:从简单计时到深度剖析

Ada
Ada
发布时间:2026-10-14 | 4人浏览过
原创

本文详细介绍Python中进行程序性能测试的多种方法。涵盖使用Unix时间函数进行整体耗时测试,利用cProfile模块生成详细调用报告,以及通过自定义装饰器和上下文管理器针对特定函数或代码块进行精准计时。此外,还解析了timeit模块在微基准测试中的应用及参数配置,并对比了perf_counter与process_time的区别,帮助开发者选择合适的工具优化代码性能。

Python性能测试概述

问题

在开发过程中,评估程序运行所消耗的时间并进行性能分析是常见需求。

解决方案

整体耗时测试方法

若仅需获取程序整体的执行耗时,通常直接调用Unix系统的时间函数即可实现,例如通过命令行工具观察运行结果。


bash % time python3 someprogram.py
real 0m13.937s
user 0m12.162s
sys 0m0.098s
bash %

使用cProfile进行详细剖析

若需获取程序内部各部分的详细性能报告,可以借助cProfile模块进行剖析。


bash % python3 -m cProfile someprogram.py
     859647 function calls in 16.016 CPU seconds

  Ordered by: standard name

  ncalls tottime percall cumtime percall filename:lineno(function)
  263169  0.080  0.000  0.080  0.000 someprogram.py:16(frange)
   513  0.001  0.000  0.002  0.000 someprogram.py:30(generate_mandel)
  262656  0.194  0.000  15.295  0.000 someprogram.py:32(<genexpr>)
    1  0.036  0.036  16.077  16.077 someprogram.py:4(<module>)
  262144  15.021  0.000  15.021  0.000 someprogram.py:4(in_mandelbrot)
    1  0.000  0.000  0.000  0.000 os.py:746(urandom)
    1  0.000  0.000  0.000  0.000 png.py:1056(_readable)
    1  0.000  0.000  0.000  0.000 png.py:1073(Reader)
    1  0.227  0.227  0.438  0.438 png.py:163(<module>)
   512  0.010  0.000  0.010  0.000 png.py:200(group)
  ...
bash %

针对特定函数的性能测试

实际场景中,需求往往介于上述两种极端之间。例如,已知代码绝大部分耗时集中在少数几个函数中。针对这类特定函数的性能测试,可以通过编写一个简单的装饰器来简化操作。


# timethis.py

import time
from functools import wraps

def timethis(func):
  @wraps(func)
  def wrapper(*args, **kwargs):
    start = time.perf_counter()
    r = func(*args, **kwargs)
    end = time.perf_counter()
    print('{}.{} : {}'.format(func.__module__, func.__name__, end - start))
    return r
  return wrapper

使用该装饰器时,只需将其应用到目标性能测试函数的定义之前即可生效。


>>> @timethis
... def countdown(n):
...   while n > 0:
...       n -= 1
...
>>> countdown(10000000)
__main__.countdown : 0.803001880645752
>>>

代码块运行时间测试

若需测试某段具体代码块的运行时间,可以定义一个上下文管理器来实现。


from contextlib import contextmanager

@contextmanager
def timeblock(label):
  start = time.perf_counter()
  try:
    yield
  finally:
    end = time.perf_counter()
    print('{} : {}'.format(label, end - start))

以下是使用上述上下文管理器的具体示例。


>>> with timeblock('counting'):
...   n = 10000000
...   while n > 0:
...       n -= 1
...
counting : 1.5551159381866455
>>>

微基准测试:timeit模块应用

对于测试极小代码片段的运行性能,使用timeit模块会更为便捷。


>>> from timeit import timeit
>>> timeit('math.sqrt(2)', 'import math')
0.1432319980012835
>>> timeit('sqrt(2)', 'from math import sqrt')
0.10836604500218527
>>>

timeit模块默认会执行第一个参数中的语句100万次并计算总耗时。第二个参数用于配置测试前的运行环境。若需调整循环执行的次数,可通过设置number参数的值来指定,例如将执行次数设定为1000万次。


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

性能测试注意事项与建议

讨论

执行性能测试时,需注意所得结果均为近似值。time.perf_counter()函数能在给定平台上获取最高精度的计时数据,但因其基于系统时钟时间,精确度仍受机器负载等多种因素影响。若更关注CPU执行时间而非墙钟时间,建议使用time.process_time()函数替代。


from functools import wraps
def timethis(func):
  @wraps(func)
  def wrapper(*args, **kwargs):
    start = time.process_time()
    r = func(*args, **kwargs)
    end = time.process_time()
    print('{}.{} : {}'.format(func.__module__, func.__name__, end - start))
    return r
  return wrapper

最后,若需进行更深入的性能分析,建议详细阅读time、timeit及其他相关模块的官方文档。这有助于理解平台相关的差异及潜在陷阱。此外,还可参考13.13小节中关于创建计时器类的示例代码。

以上就是Python如何给你的程序做性能测试的详细内容,更多关于Python做性能测试的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表