前言
一些Python用户对了解同一问题的不同方法的相对性能产生了浓厚的兴趣。 Python提供了一种可以立即回答这些问题的测量工具。
1.timeit 模块
from timeit import Timer
Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()
0.020554084330797195
Timer('a,b = b,a', 'a=1; b=2').timeit()
0.01771862991154194
提供两种方式:
1.代码方式
2.命令行方式
也可以自己实现,封装在装饰器中使用,打上注解@timethis就可以随便用了。
from functools import wraps
import time
def timethis(func):
"""
用于统计业务层各个函数花费多少时间
:param func:
:return:
"""
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
r = func(*args, **kwargs)
end = time.time()
print(func.__module__, func.__name__, end - start)
return r
return wrapper
@timethis
def func1(a:int, b:int)->int:
time.sleep(1)
return a+b
func1(10,20)
输出:
__main__ func1 1.0010700225830078
ref:https://docs.python.org/zh-cn/dev/library/timeit.html#module-timeit
2. cProfile 和 Profile
cProfile 和 profile 提供了 Python 程序的 确定性性能分析 。 profile 是一组统计数据,描述程序的各个部分执行的频率和时间。这些统计数据可以通过 pstats 模块格式化为报表。
Python 标准库提供了同一分析接口的两种不同实现:
对于大多数用户,建议使用 cProfile ;这是一个 C 扩展插件,因为其合理的运行开销,所以适合于分析长时间运行的程序。该插件基于 lsprof ,由 Brett Rosen 和 Ted Chaotter 贡献。
profile 是一个纯 Python 模块(cProfile 就是模拟其接口的 C 语言实现),但它会显著增加配置程序的开销。如果你正在尝试以某种方式扩展分析器,则使用此模块可能会更容易完成任务。该模块最初由 Jim Roskind 设计和编写。
import cProfile
import re
cProfile.run('re.compile("foo|bar")')
结果:
214 function calls (207 primitive calls) in 0.002 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.002 0.002 {built-in method builtins.exec}
1 0.000 0.000 0.001 0.001 <string>:1(<module>)
1 0.000 0.000 0.001 0.001 __init__.py:250(compile)
1 0.000 0.000 0.001 0.001 __init__.py:289(_compile)
1 0.000 0.000 0.000 0.000 _compiler.py:759(compile)
1 0.000 0.000 0.000 0.000 _parser.py:937(parse)
1 0.000 0.000 0.000 0.000 _compiler.py:598(_code)
1 0.000 0.000 0.000 0.000 _parser.py:435(_parse_sub)
命令行方式:
python -m cProfile [-o output_file] [-s sort_order] (-m module | myscript.py)
还有一个是stats, profile和stats都在下面这个ref
ref:https://docs.python.org/zh-cn/dev/library/profile.html#module-profile
print("----------------------------------------------------------------------------------------")
import cProfile, pstats, io
from pstats import SortKey
pr = cProfile.Profile()
pr.enable()
for i in range(10):
time.sleep(1)
pr.disable()
s = io.StringIO()
sortby = SortKey.CUMULATIVE
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print(s.getvalue())
11 function calls in 10.010 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
10 10.010 1.001 10.010 1.001 {built-in method time.sleep}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
import cProfile
with cProfile.Profile() as pr:
# ... do something ...
pr.print_stats()
3.质量控制
开发高质量软件的一种方法是在开发过程中为每个函数编写测试,并在开发过程中经常运行这些测试。
doctest 模块提供了一个工具,用于扫描模块并验证程序文档字符串中嵌入的测试。测试构造就像将典型调用及其结果剪切并粘贴到文档字符串一样简单。这通过向用户提供示例来改进文档,并且它允许doctest模块确保代码保持对文档的真实:
def average(values):
"""Computes the arithmetic mean of a list of numbers.
>>> print(average([20, 30, 70]))
40.0
"""
return sum(values) / len(values)
import doctest
doctest.testmod() # automatically validate the embedded tests
import unittest
class TestStatisticalFunctions(unittest.TestCase):
def test_average(self):
self.assertEqual(average([20, 30, 70]), 40.0)
self.assertEqual(round(average([1, 5, 7]), 1), 4.3)
with self.assertRaises(ZeroDivisionError):
average([])
with self.assertRaises(TypeError):
average(20, 30, 70)
unittest.main() # Calling from the command line invokes all tests
unittest 模块不像 doctest 模块那样易于使用,但它允许在一个单独的文件中维护更全面的测试集:
总结
与 timeit 的精细粒度级别相反, profile 和 pstats 模块提供了用于在较大的代码块中识别时间关键部分的工具。
手册上就有很好的性能测试工具,profile,stats,都是linux的命令,查看函数调用、耗时的命令有很多,这方便我也在学习,等有了成果在写blog