2013-04-05 93 views
0

我想基准itertools针对生成器和列表解析的几种方法。这个想法是,我想通过从基本列表中过滤一些条目来构建迭代器。这个基准看起来有关吗?

这里是我想出了(编后接受的答案)代码:

from itertools import ifilter 
import collections 
import random 
import os 
from timeit import Timer 
os.system('cls') 

# define large arrays 
listArrays = [xrange(100), xrange(1000), xrange(10000), xrange(100000)] 

#Number of element to be filtered out 
nb_elem = 100 
# Number of times we run the test 
nb_rep = 1000 


def discard(it): 
    collections.deque(it, maxlen=0) 


def testGenerator(arr, sample): 
    discard(x for x in sample if x in arr) 


def testIterator(arr, sample): 
    discard(ifilter(sample.__contains__, arr)) 


def testList(arr, sample): 
    discard([x for x in sample if x in arr]) 


if __name__ == '__main__': 

    for arr in listArrays: 

     print 'Size of array: %s ' % len(arr) 
     print 'number of iterations %s' % nb_rep 
     sample = random.sample(arr, nb_elem) 

     t1 = Timer('testIterator(arr, sample)', 'from __main__ import testIterator, arr, sample') 
     tt1 = t1.timeit(number=nb_rep) 

     t2 = Timer('testList(arr, sample)', 'from __main__ import testList, arr, sample') 
     tt2 = t2.timeit(number=nb_rep) 

     t3 = Timer('testGenerator(arr, sample)', 'from __main__ import testGenerator, arr, sample') 
     tt3 = t3.timeit(number=nb_rep) 

     norm = min(tt1, tt2, tt3) 
     print 'maximum runtime %.6f' % max(tt1, tt2, tt3) 
     print 'normalized times:\n iterator: %.6f \n list: %.6f \n generator: %.6f' % \ 
      (tt1/norm, tt2/norm, tt3/norm) 
     print '=========================================== 

===========”

而且我得到请注意结果经过编辑的版本是不一样的机器(因此可用于具有标准化的结果)上运行,并跑了一个32位的解释与Python 2.7.3:

Size of array: 100 
number of iterations 1000 
maximum runtime 0.125595 
normalized times: 
iterator: 1.000000 
list: 1.260302 
generator: 1.276030 
====================================================== 
Size of array: 1000 
number of iterations 1000 
maximum runtime 1.740341 
normalized times: 
iterator: 1.466031 
list: 1.010701 
generator: 1.000000 
====================================================== 
Size of array: 10000 
number of iterations 1000 
maximum runtime 17.033630 
normalized times: 
iterator: 1.441600 
list: 1.000000 
generator: 1.010979 
====================================================== 
Size of array: 100000 
number of iterations 1000 
maximum runtime 169.677963 
normalized times: 
iterator: 1.455594 
list: 1.000000 
generator: 1.008846 
====================================================== 

你能给出一些改进的意见和评论无论这个基准能否给出准确的结果?

我知道我的装饰器中的条件可能会偏差结果。我希望就此提出一些建议。

谢谢。

+5

首先,由于['time'](http://docs.python.org/2/library/time.html)模块在文档中明确指出,您通常不希望使用'time.time ()'或'time.clock()'来进行性能测试。这就是['timeit'](http://docs.python.org/2/library/timeit.html)模块的用途。 (另外,你的代码中有一半以上是试图再现'timeit'的作用,当然除了'timeit'的方式没有经过严格的测试。) – abarnert 2013-04-05 21:33:07

回答

6

首先,而不是试图复制一切timeit确实,只是使用它。 time函数可能没有足够的准确性来使用,编写数十行脚手架代码(特别是如果它不得不嘲笑诸如切换func.__name__之类的东西),您不需要的只是无故邀请错误。

假设没有错误,它可能不会显着影响结果。您正在做一些额外的工作,并将其充电至testIterator,但每个外循环只有一次。但是,这样做没有好处,所以我们不要。

def testGenerator(arr,sample): 
    for i in (x for x in sample if x in arr): 
     k = random.random() 

def testIterator(arr,sample): 
    for i in ifilter(lambda x: x in sample, arr): 
     k = random.random() 

def testList(arr,sample): 
    for i in [x for x in sample if x in arr]: 
     k = random.random() 

tests = testIterator, testGenerator, testList 

for arr in listArrays: 
    print 'Size of array: %s ' % len(arr) 
    print 'number of iterations %s' % nb_rep 
    sample = random.sample(arr, nb_elem) 
    funcs = [partial(test, arr, sample) for test in tests] 
    times = [timeit.timeit(func, number=nb_rep) for func in funcs] 
    norm = min(*times) 
    print 'maximum runtime %.6f' % max(*times) 
    print 'normalized times:\n iterator: %.6f \n list: %.6f \n generator: %.6f' % (times[0]/norm,times[1]/norm,times[2]/norm) 
    print '======================================================' 

接下来,你为什么这样做在那里,k = random.random()?从一个快速测试中,只需执行该行N次而没有复杂的循环就是整个事件的0.19倍。所以,你要给每个数字加20%,这就无缘无故地稀释了它们之间的差异。


一旦你摆脱的是,for循环服务不到任何目的,消耗的迭代器,而这增加了额外的开销,以及。由于2.7.3和3.3.0,以最快的方式消费,而不定制的C代码的迭代器deque(it, maxlen=0),所以,让我们试试这个:

def discard(it): 
    collections.deque(it, maxlen=0) 

def testGenerator(arr,sample): 
    discard(x for x in sample if x in arr) 

def testIterator(arr,sample): 
    discard(ifilter(sample.__contains__, arr)) 

def testList(arr,sample): 
    discard([x for x in sample if x in arr]) 

,或者,只是有函数返回一个发电机/ IFilter的/列表,然后让脚手架在结果上调用discard(这两者无关)。


同时,针对testIterator情况下,你想测试拉姆达与内嵌式的费用,或ifilter与发电机的成本是多少?如果你想测试前者,这是正确的;如果后者,你可能想优化。例如,传递sample.__contains__而不是lambda x: x in sample似乎在64位Python 3.3.0中速度提高了20%,在32位2.7.2中速度提高了30%(尽管由于某些原因在64位2.7.2中速度并不快) 。


最后,除非您只测试一个执行/平台/版本,否则请确保尽可能多地运行它。例如,对于64位CPython 2.7.2,listgenerator总是不分上下,而iterator随着列表增长逐渐从1.0x上升到1.4x,但在PyPy 1.9.0中,iterator始终是最快的, generatorlist的起始速度是2.1x和1.9x,但随着列表的增长,接近1.2x。因此,如果你决定反对迭代器,因为它“很慢”,你可能会在PyPy上进行大幅度的减速,从而在CPython上实现小得多的加速。

当然,这可能是可以接受的,例如,因为即使最慢的PyPy运行速度非常快,或者因为您的用户都没有使用PyPy或其他什么。但这绝对是“这个基准是否相关?”答案的一部分。

+1

我问为什么它是downvoted,有人(我不不知道如何)回答了这样的问题:“不要生气,这是一个不好的答案”,我问如何改进它......现在评论已经消失了?无论如何,downvote仍然在这里,所以如果有人认为答案是不正确的,不完整的,误导性的,混乱的或任何其他的,请解释原因。 – abarnert 2013-04-05 23:32:48

相关问题