Python 2.7 测量代码的执行时间取决于;“设置顺序”; 问题描述:

Python 2.7 测量代码的执行时间取决于;“设置顺序”; 问题描述:,python-2.7,optimization,set,timeit,Python 2.7,Optimization,Set,Timeit,我正在优化非常复杂的算法,不幸的是,它严重依赖于使用set和frozenset数据类型(因为操作符中的更快)。这意味着每次运行测试时,我都会得到不同的执行时间,即使是对于完全相同的输入数据。由于我需要(非常)优化算法,我希望每次都有一个恒定的(尽可能长的)执行时间 示范 我做了一个简化的例子,希望能说明这个问题: import timeit class A(object): def __init__(self, val): self.val = val def run

我正在优化非常复杂的算法,不幸的是,它严重依赖于使用
set
frozenset
数据类型(因为
操作符中的
更快)。这意味着每次运行测试时,我都会得到不同的执行时间,即使是对于完全相同的输入数据。由于我需要(非常)优化算法,我希望每次都有一个恒定的(尽可能长的)执行时间

示范 我做了一个简化的例子,希望能说明这个问题:

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,)
这个问题的核心是
集合中对象的顺序-这取决于(我认为)它们在内存中的位置,当然每次都不同。然后,在对值进行排序时,
sorted
执行速度每次都会不同。在我的机器上,它给出的执行时间公差约为10%。 这不是最好的演示,因为我的实际代码更多地依赖于集合顺序,时间差也更大,但我希望您能理解这一点

我尝试的是:
  • 在算法中对集合进行排序-它提供了恒定的执行时间,但也使整个算法的速度慢了十倍
  • 使用非常大的
    number
    repeat
    参数-除非每次更改后我想等待一个小时,否则这对我没有帮助
我想尝试的是: 我认为,如果我能以某种方式“重置”python解释器,使其拥有“干净的内存”,它将为对象提供可预测的内存位置,并且时间度量将保持不变。但我不知道如何做这样的事情,除了创建一个VM并在每次我想做测试时重新启动它。。 我想

不是问题:
  • 我描述了很多,我知道现在哪些函数最慢-我只需要让它们更快-这些就是我试图测量的速度函数
  • 除了
    set
    frozenset
    之外,我不能使用任何东西进行测试(就像一些有序的集合),因为它会慢得多,而且测量的速度与生产代码没有任何关系
  • set
    frozenset
    性能在这里并不重要
摘要/问题:
  • 我的算法在内部使用
    集合
  • 我想测量执行速度
  • 执行速度取决于检索内部
    集合中包含的元素的顺序
  • 我使用的测试有固定的输入值
  • 基于
    timeit
    测量,我无法测量我所做的任何更改的影响
  • 在上面的测试中,
    run\u 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
(当然,每次运行时都会有所不同,在另一台机器上可能会完全不同,也可能不会完全不同)


您可以看到,在输入数据保持完全相同的情况下,每次执行速度都非常不同。这正是我在测量算法速度时看到的行为。

为所有设置操作编写一个wrap函数,然后配置文件怎么样?然后你可以很容易地减去设定的操作时间。那些集合操作真的是你的瓶颈吗?@HaochenWu问题是,当使用
集合
时,我每次都会得到不同的计时结果,因为放入
集合
的相同元素将以不同的顺序“拉出”,而我的算法对元素的处理顺序非常敏感。因此,根据
timeit
结果,我无法确定算法中的某些更改是使其更快还是更慢。集合操作不是瓶颈,它们只是“随机”执行时间的原因。这绝对是一个特性,而不是一个bug(我是指
集合
)。或者也许我没有完全理解你的建议..只是想澄清一下-元素处理的顺序大大改变了执行时间,但它总是返回正确的结果-只是“路径”对于不同的元素排序,结果需要不同的时间。我建议编写一个包装函数来执行所有的in操作,然后您就可以确切地知道在这些操作上花费了多少时间,从总时间中减去这一时间将得到其余代码的运行时间。@HaochenWu我认为这是不可能的-这与我给出的例子完全一样-排序[5 4 6 3 7 2 8 1]总是比排序[1 2 3 4 5 6 7 8]慢-算法取决于“集合中元素的顺序”(即集合被迭代的顺序)-如果我为集合操作编写包装器,它不会改变对元素检索顺序的依赖性。。