2024-03-29 15:50:55 发布
网友
我理解timeit做什么的概念,但我不确定如何在代码中实现它。
timeit
我如何比较两个函数,比如insertion_sort和tim_sort,与timeit?
insertion_sort
tim_sort
timeit的工作方式是运行一次安装程序代码,然后重复调用一系列语句。因此,如果您想测试排序,就需要一些注意,以便就地排序的一个过程不会影响已排序数据的下一个过程(当然,这会使Timsort真正发光,因为它在数据已部分排序时执行得最好)。
下面是如何设置排序测试的示例:
>>> import timeit >>> setup = ''' import random random.seed('slartibartfast') s = [random.random() for i in range(1000)] timsort = list.sort ''' >>> print min(timeit.Timer('a=s[:]; timsort(a)', setup=setup).repeat(7, 1000)) 0.334147930145
请注意,这一系列语句在每次传递时都会生成未排序数据的新副本。
另外,请注意运行测量套件七次并只保留最佳时间的计时技术——这确实有助于减少由于系统上运行的其他进程而导致的测量失真。
这些是我正确使用时间的提示。希望这有帮助:-)
如果要在交互式Python会话中使用timeit,有两个方便的选项:
使用IPython外壳。它具有方便的特殊功能:
In [1]: def f(x): ...: return x*x ...: In [2]: %timeit for x in range(100): f(x) 100000 loops, best of 3: 20.3 us per loop
在标准的Python解释器中,可以通过从setup语句中的__main__导入在交互会话期间先前定义的函数和其他名称来访问它们:
__main__
>>> def f(x): ... return x * x ... >>> import timeit >>> timeit.repeat("for x in range(100): f(x)", "from __main__ import f", number=100000) [2.0640320777893066, 2.0876040458679199, 2.0520210266113281]
我将告诉您一个秘密:使用timeit的最佳方法是在命令行上。
在命令行上,timeit进行正确的统计分析:它告诉您最短的运行时间。这很好,因为所有计时错误都是正的。所以最短的时间误差最小。没有办法得到负误差,因为计算机永远不能计算得比它能计算得快!
因此,命令行界面:
%~> python -m timeit "1 + 2" 10000000 loops, best of 3: 0.0468 usec per loop
很简单,嗯?
你可以设置:
%~> python -m timeit -s "x = range(10000)" "sum(x)" 1000 loops, best of 3: 543 usec per loop
这也很有用!
如果需要多行,可以使用shell的自动延续或使用单独的参数:
%~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)" 1000 loops, best of 3: 554 usec per loop
这样就有了
x = range(1000) y = range(100)
和时代
sum(x) min(y)
如果您想拥有更长的脚本,您可能会尝试在Python脚本中移动到timeit。我建议避免这样做,因为命令行的分析和计时更简单。相反,我倾向于制作shell脚本:
SETUP=" ... # lots of stuff " echo Minmod arr1 python -m timeit -s "$SETUP" "Minmod(arr1)" echo pure_minmod arr1 python -m timeit -s "$SETUP" "pure_minmod(arr1)" echo better_minmod arr1 python -m timeit -s "$SETUP" "better_minmod(arr1)" ... etc
由于多次初始化,这可能需要更长的时间,但通常这不是什么大事。
但是如果你想在你的模块中使用timeit呢?
好吧,简单的方法是:
def function(...): ... timeit.Timer(function).timeit(number=NUMBER)
这给了你累加(不是最小值!)是时候跑这么多次了。
要获得良好的分析,请使用.repeat,并取最小值:
.repeat
min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))
通常,您应该将它与functools.partial而不是lambda: ...结合使用,以降低开销。这样你就可以得到这样的东西:
functools.partial
lambda: ...
from functools import partial def to_time(items): ... test_items = [1, 2, 3] * 100 times = timeit.Timer(partial(to_time, test_items)).repeat(3, 1000) # Divide by the number of repeats time_taken = min(times) / 1000
您还可以执行以下操作:
timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)
这将使您更接近命令行中的接口,但方式不那么酷。"from __main__ import ..."允许您在由timeit创建的人工环境中使用来自主模块的代码。
"from __main__ import ..."
值得注意的是,这是一个方便的Timer(...).timeit(...)包装器,因此不太擅长计时。我个人更喜欢使用Timer(...).repeat(...),如上面所示。
Timer(...).timeit(...)
Timer(...).repeat(...)
有一些关于timeit的警告在任何地方都适用。
不计入间接费用。假设您想计时x += 1,以了解加法需要多长时间:
x += 1
>>> python -m timeit -s "x = 0" "x += 1" 10000000 loops, best of 3: 0.0476 usec per loop
好吧,它是而不是0.0476微秒。你只知道它比这个小。所有的错误都是肯定的。
所以试着找出纯开销:
>>> python -m timeit -s "x = 0" "" 100000000 loops, best of 3: 0.014 usec per loop
这是一个很好的30%从时间上来说的开销!这会严重影响相对计时。但是您只关心添加计时;还需要将x的查找计时包括在开销中:
x
>>> python -m timeit -s "x = 0" "x" 100000000 loops, best of 3: 0.0166 usec per loop
差别并不大,但确实存在。
变异方法是危险的。
>>> python -m timeit -s "x = [0]*100000" "while x: x.pop()" 10000000 loops, best of 3: 0.0436 usec per loop
但那是完全错误的!x是第一次迭代后的空列表。您需要重新初始化:
>>> python -m timeit "x = [0]*100000" "while x: x.pop()" 100 loops, best of 3: 9.79 msec per loop
但是你有很多开销。单独说明。
>>> python -m timeit "x = [0]*100000" 1000 loops, best of 3: 261 usec per loop
注意,这里减去开销是合理的,因为开销只是时间的一小部分。
对于您的示例,值得注意的是,插入排序和Tim排序对于已经排序的列表都具有完全不寻常的计时行为。这意味着您需要在排序之间使用random.shuffle,以避免破坏您的计时。
random.shuffle
timeit的工作方式是运行一次安装程序代码,然后重复调用一系列语句。因此,如果您想测试排序,就需要一些注意,以便就地排序的一个过程不会影响已排序数据的下一个过程(当然,这会使Timsort真正发光,因为它在数据已部分排序时执行得最好)。
下面是如何设置排序测试的示例:
请注意,这一系列语句在每次传递时都会生成未排序数据的新副本。
另外,请注意运行测量套件七次并只保留最佳时间的计时技术——这确实有助于减少由于系统上运行的其他进程而导致的测量失真。
这些是我正确使用时间的提示。希望这有帮助:-)
如果要在交互式Python会话中使用
timeit
,有两个方便的选项:使用IPython外壳。它具有方便的特殊功能:
在标准的Python解释器中,可以通过从setup语句中的
__main__
导入在交互会话期间先前定义的函数和其他名称来访问它们:我将告诉您一个秘密:使用
timeit
的最佳方法是在命令行上。在命令行上,
timeit
进行正确的统计分析:它告诉您最短的运行时间。这很好,因为所有计时错误都是正的。所以最短的时间误差最小。没有办法得到负误差,因为计算机永远不能计算得比它能计算得快!因此,命令行界面:
很简单,嗯?
你可以设置:
这也很有用!
如果需要多行,可以使用shell的自动延续或使用单独的参数:
这样就有了
和时代
如果您想拥有更长的脚本,您可能会尝试在Python脚本中移动到
timeit
。我建议避免这样做,因为命令行的分析和计时更简单。相反,我倾向于制作shell脚本:由于多次初始化,这可能需要更长的时间,但通常这不是什么大事。
但是如果你想在你的模块中使用
timeit
呢?好吧,简单的方法是:
这给了你累加(不是最小值!)是时候跑这么多次了。
要获得良好的分析,请使用
.repeat
,并取最小值:通常,您应该将它与
functools.partial
而不是lambda: ...
结合使用,以降低开销。这样你就可以得到这样的东西:您还可以执行以下操作:
这将使您更接近命令行中的接口,但方式不那么酷。
"from __main__ import ..."
允许您在由timeit
创建的人工环境中使用来自主模块的代码。值得注意的是,这是一个方便的
Timer(...).timeit(...)
包装器,因此不太擅长计时。我个人更喜欢使用Timer(...).repeat(...)
,如上面所示。警告
有一些关于
timeit
的警告在任何地方都适用。不计入间接费用。假设您想计时
x += 1
,以了解加法需要多长时间:好吧,它是而不是0.0476微秒。你只知道它比这个小。所有的错误都是肯定的。
所以试着找出纯开销:
这是一个很好的30%从时间上来说的开销!这会严重影响相对计时。但是您只关心添加计时;还需要将
x
的查找计时包括在开销中:差别并不大,但确实存在。
变异方法是危险的。
但那是完全错误的!
x
是第一次迭代后的空列表。您需要重新初始化:但是你有很多开销。单独说明。
注意,这里减去开销是合理的,因为开销只是时间的一小部分。
对于您的示例,值得注意的是,插入排序和Tim排序对于已经排序的列表都具有完全不寻常的计时行为。这意味着您需要在排序之间使用
random.shuffle
,以避免破坏您的计时。相关问题 更多 >
编程相关推荐