我正在优化相当复杂的算法,遗憾的是,它非常依赖于使用set
和frozenset
数据类型(因为in
运算符更快)。这意味着每次运行测试时都会得到不同的执行时间,即使对于完全相同的输入数据也是如此。由于我需要(非常)优化算法,我希望每次都有一个常量(尽可能)执行时间。
我做了一个简化的例子,希望能够证明这个问题:
import timeit
class A(object):
def __init__(self, val):
self.val = val
def run_test():
result = []
for i in xrange(100):
a = {A(j) for j in xrange(100)}
result.append(sorted(v.val for v in a))
return result
N = 10
times = timeit.Timer(run_test).repeat(repeat=3, number=N)
print '%.6f s' % (min(times) / N,)
问题的核心是sets
中对象的排序 - 它取决于(我认为)他们在记忆中的位置,这当然每次都不同。然后,在对值进行排序时,每次sorted
执行速度都会不同。在我的机器上,它的执行时间约为10%。
这不是最好的演示,因为我的真实代码更多地依赖于集合排序,时间差异要高得多,但我希望你能得到它。
number
和repeat
参数 - 除非我想在每次更改后等待一个小时,否则我无法帮助我认为如果我能以某种方式“重置”python解释器以获得“干净的记忆”,它将导致对象的可预测的记忆位置,并且时间测量将是恒定的。但我不知道如何做这样的事情,除了创建一个虚拟机并在每次我想进行测试时重新启动它。 我想
set
和frozenset
之外的任何其他内容进行测试(就像某些有序集合一样),因为它会慢很多,并且测得的速度与生产无关代码set
和frozenset
表现并不重要sets
set
中包含的元素的顺序timeit
衡量标准,我无法衡量我所做的任何变更的影响run_test
函数是我真正问题的一个很好的例子 所以我需要一些方法来临时确保所有set
元素都将在相同的内存位置创建,这将使测试执行速度和函数调用数(分析)具有确定性。
这个例子可能更好地证明了我的问题:
import timeit
import time
class A(object):
def __init__(self, val):
self.val = val
def get_value_from_very_long_computation(self):
time.sleep(0.1)
return self.val
def run_test():
input_data = {A(j) for j in xrange(20)}
for i, x in enumerate(input_data):
value = x.get_value_from_very_long_computation()
if value > 16:
break
print '%d iterations' % (i + 1,)
times = timeit.Timer(run_test).repeat(repeat=1, number=1)
print '%.9f s' % (min(times) / N,)
返回,例如:
$ ./timing-example2.py
4 iterations
0.400907993 s
$ ./timing-example2.py
3 iterations
0.300778866 s
$ ./timing-example2.py
8 iterations
0.801693201 s
(当然,每次运行时都会有所不同,在另一台机器上可能会有所不同或完全不同)
您可以看到每次输入数据保持完全相同时执行速度非常不同。这正是我在测量算法速度时看到的行为。