timeit的强大多行替代方案
项目描述
Timerit
Python内置timeit模块的强大多行替代方案。
文档发布在https://timerit.readthedocs.io/en/latest/,但本README和代码注释包含一个教程。
Github |
|
Pypi |
|
ReadTheDocs |
描述
通过简单地缩进现有代码块,可以轻松地进行健壮的计时。无需将其重构为字符串表示形式或转换为单行。
安装
pip install timerit
交互式使用
timerit库为交互式使用提供了简洁的API
>>> import timerit
>>> for _ in timerit:
... sum(range(100000))
Timed for: 288 loops, best of 5
time per loop: best=616.740 µs, mean=668.933 ± 124.2 µs
与timeit比较
$ python -m timeit 'sum(range(100000))'
500 loops, best of 5: 721 usec per loop
默认情况下,任何在循环中的代码都将重复执行,直到至少200毫秒已过去。然后打印出计时结果。
以下每个数字的含义
“288 loops”:循环中的代码在达到时间限制之前运行了288次。
“5次最快”:在计算平均值和标准差时,只考虑每5次测量中的最快时间。这样做的原因是,如果后台有某个进程正在消耗资源,可能会导致出现较慢的时间,因此你通常只对最快的时间感兴趣。这个想法也已在timeit文档中描述。
“best=616.740 µs”:最快迭代运行所需的时间。如上所述,这通常是数据中最一致的数量,也是你应该关注的重点。
“mean=668.933 ± 124.2 µs”:5次最快迭代中的平均值和标准差。这个统计数据通常不如最快时间稳健或有用,但有时如果存在较大的方差,了解它也是有帮助的。
循环变量可以用作上下文管理器,以只对每次循环的一部分进行计时(例如,为了提高计时精度,或包含不被计时的设置阶段)
>>> for timer in timerit:
... n = 100 * 1000
... with timer:
... sum(range(n))
Timed for: 318 loops, best of 5
time per loop: best=616.673 µs, mean=617.545 ± 0.9 µs
还可以提供控制计时测量方式的参数。有关这些参数的更多信息,请参阅在线文档,但以下代码片段会运行100次迭代,而不是200毫秒内能容纳的任意次数。
>>> for _ in timerit(num=100):
... sum(range(100000))
Timed for: 100 loops, best of 5
time per loop: best=616.866 µs, mean=619.120 ± 5.3 µs
自动导入
如果您想使timerit更易于交互式使用,可以将导入移动到PYTHONSTARTUP文件。如果已定义,则该环境变量给出一个Python脚本的路径,该脚本将在每次交互式会话之前执行。例如
$ export PYTHONSTARTUP=~/.pythonrc
$ cat $PYTHONSTARTUP
import timerit
$ python
>>> for _ in timerit:
... sum(range(100000))
...
Timed for: 59 loops, best of 3
time per loop: best=2.532 ms, mean=3.309 ± 1.0 ms
程序性使用
timerit库还提供了一个Timerit类,它可以被程序性地使用。
>>> import math, timerit
>>> for timer in timerit:
>>> setup_vars = 10000
>>> with timer:
>>> math.factorial(setup_vars)
>>> print('t1.total_time = %r' % (t1.total_time,))
Timing for 200 loops
Timed for: 200 loops, best of 3
time per loop: best=2.064 ms, mean=2.115 ± 0.05 ms
t1.total_time = 0.4427177629695507
一个常见的模式是创建一个单一的Timerit实例,然后通过不同的标签重复“重置”它以测试多个不同的算法。这种方式分配的标签将包含在Timerit实例生成的报告字符串中。下面的“基准配方”显示了这种模式的例子。同样,examples/目录中的所有脚本也是如此。
还有一个类似于timeit的IPython魔法的单行代码
比较timeit版本
>>> %timeit math.factorial(100)
564 ns ± 5.46 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
与Timerit版本
>>> Timerit(100000).call(math.factorial, 100).print()
Timed for: 1 loops, best of 1
time per loop: best=4.828 µs, mean=4.828 ± 0.0 µs
工作原理
timerit模块定义了timerit.Timerit,这是一个可迭代的对象,它产生timerit.Timer上下文管理器。
>>> import math
>>> from timerit import Timerit
>>> for timer in Timerit(num=200, verbose=2):
>>> with timer:
>>> math.factorial(10000)
计时上下文管理器通过在__enter__上“tic”和在__exit__上“toc”来测量其主体所需的时间。父Timerit对象可以访问上下文管理器,因此能够读取其测量值。这些测量值被存储,然后我们对它们进行一些统计计算。值得注意的是,分组(批处理)运行时间的最小值、平均值和标准差。
在循环内使用with语句的好处是,您可以在进入上下文管理器之前运行不受计时的设置代码。
在没有设置代码需求的情况下,还有更简洁的语法版本。
>>> import math
>>> from timerit import Timerit
>>> for _ in Timerit(num=200, verbose=2):
>>> math.factorial(10000)
如果上下文管理器从未被调用,Timerit对象会检测到这一点,并在Timerit对象本身的__iter__方法中进行测量。我相信这种方法比with语句版本的开销略大。(我看到了一些证据表明这实际上可能更准确,但需要进一步测试)。
基准配方
import ubelt as ub
import pandas as pd
import timerit
def method1(x):
ret = []
for i in range(x):
ret.append(i)
return ret
def method2(x):
ret = [i for i in range(x)]
return ret
method_lut = locals() # can populate this some other way
ti = timerit.Timerit(100, bestof=10, verbose=2)
basis = {
'method': ['method1', 'method2'],
'x': list(range(7)),
# 'param_name': [param values],
}
grid_iter = ub.named_product(basis)
# For each variation of your experiment, create a row.
rows = []
for params in grid_iter:
key = ub.repr2(params, compact=1, si=1)
kwargs = params.copy()
method_key = kwargs.pop('method')
method = method_lut[method_key]
# Timerit will run some user-specified number of loops.
# and compute time stats with similar methodology to timeit
for timer in ti.reset(key):
# Put any setup logic you dont want to time here.
# ...
with timer:
# Put the logic you want to time here
method(**kwargs)
row = {
'mean': ti.mean(),
'min': ti.min(),
'key': key,
**params,
}
rows.append(row)
# The rows define a long-form pandas data array.
# Data in long-form makes it very easy to use seaborn.
data = pd.DataFrame(rows)
print(data)
plot = True
if plot:
# import seaborn as sns
# kwplot autosns works well for IPython and script execution.
# not sure about notebooks.
import kwplot
sns = kwplot.autosns()
# Your variables may change
ax = kwplot.figure(fnum=1, doclf=True).gca()
sns.lineplot(data=data, x='x', y='min', hue='method', marker='o', ax=ax)
ax.set_title('Benchmark Name')
ax.set_xlabel('x-variable description')
ax.set_ylabel('y-variable description')
timerit-1.1.0.tar.gz的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | a12a7db82d8cc6a0e1bc3258e6eead7a21c617da5605083118d5c3e99927fb69 |
|
MD5 | 27efd603358cefd2f21a0bab303ba9d6 |
|
BLAKE2b-256 | 63d636252ff3d6a0bfaa3fa54e025d671df949b265b426429e7e8ab0149e65c2 |
timerit-1.1.0-py3-none-any.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 20e330a6da00295e1a1f88b2c89e18f6f55e785486da817de5762c3c06a0c2e2 |
|
MD5 | 2580e25ef80be4b61ad98d30c79dc73e |
|
BLAKE2b-256 | c35e707d015e882426ac8b5e703a242032eafa9df21ce4851d75b867791e1246 |