将迭代器传递给任何执行速度和为什么?

Der*_*itz 5 python performance

这里总结了一些问题.是的,我知道其中一些答案;)我可以在其他人身上挥手,但我真的很喜欢这里的细节.

  1. 这甚至是个好主意吗?(这个不在下面)
  2. 我想知道地图是否真的能提高速度?为什么?
  3. 为什么世界上会将迭代器传递给任何使代码更快的代码呢?
  4. 为什么我的Counter对象工作,我的print_true函数失败了?
  5. 是否有一个等效于itertools.imap的东西,它会一遍又一遍地调用一个函数,并且可选择一定次数?
  6. 我的胡萝卜在哪里?!?

我只是看着PYCON 2011:Dropbox的是怎么做的和如何Python的帮助了(当然我完成大部分的零件跳过),但最后真正有趣的东西开始在大约22:23.

演讲者主张在C中制作你的内部循环并且"运行一次"的东西不需要太多的优化(有意义)......然后他继续陈述......释义:

将迭代器的组合传递给任何用于大规模速度改进的组合.

这是代码(希望它是相同的):

import itertools, hashlib, time   
_md5 = hashlib.md5()  
def run():
    for i in itertools.repeat("foo", 10000000):
        _md5.update(i)
a = time.time();  run(); time.time() - a  
Out[118]: 9.44077205657959

_md5 = hashlib.md5() 
def run():
    any(itertools.imap(_md5.update, itertools.repeat("foo", 10000000)))    
a = time.time();  run(); time.time() - a
Out[121]: 6.547091007232666
Run Code Online (Sandbox Code Playgroud)

嗯,看起来更高的速度改进,我可以得到一个更快的电脑!(从他的幻灯片来看.)

然后他做了一堆挥手而没有真正详细说明为什么.

我已经知道迭代器从回答pythonic方式做N次而没有索引变量?感谢Alex Martelli.

然后我想,我想知道地图是否真的增加了速度提升?我最后的想法是WTF ??? 传递到任何?真???当然,因为文档定义了不能正确任何为:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False
Run Code Online (Sandbox Code Playgroud)

为什么世界上会将迭代器传递给任何使代码更快的代码呢?

然后,我使用以下(在许多其他测试中)测试它,但这是让我:

def print_true(x):
    print 'True'
    return 'Awesome'

def test_for_loop_over_iter_map_over_iter_repeat():
    for result in itertools.imap(print_true, itertools.repeat("foo", 5)):
        pass

def run_any_over_iter_map_over_iter_repeat():
    any(itertools.imap(print_true, itertools.repeat("foo", 5)))

And the runs:

    In [67]: test_for_loop_over_iter_map_over_iter_repeat()
    True
    True
    True
    True
    True

    In [74]: run_any_over_iter_map_over_iter_repeat()
    True
Run Code Online (Sandbox Code Playgroud)

耻辱.我宣布这个GUY是完全的. 异教徒!但是,我平静下来并继续测试. 如果这是真的,Dropbox甚至可以在地狱工作!?!?

通过进一步的测试,它确实有用......我最初只使用了一个简单的计数器对象,在这两种情况下它都计数到10000000.

所以问题是为什么我的Counter对象工作并且我的print_true函数失败了?

class Counter(object):
    count = 0
    def count_one(self, none):
        self.count += 1

def run_any_counter():
    counter = Counter()
    any(itertools.imap(counter.count_one, itertools.repeat("foo", 10000000)))
    print counter.count

def run_for_counter():
    counter = Counter()
    for result in itertools.imap(counter.count_one, itertools.repeat("foo", 10000000)):
        pass
    print counter.count
Run Code Online (Sandbox Code Playgroud)

输出:

%time run_for_counter()
10000000
CPU times: user 5.54 s, sys: 0.03 s, total: 5.57 s
Wall time: 5.68 s

%time run_any_counter()
10000000
CPU times: user 5.28 s, sys: 0.02 s, total: 5.30 s
Wall time: 5.40 s
Run Code Online (Sandbox Code Playgroud)

甚至更大的WTF甚至在删除不需要的参数并为我的Counter对象编写最合理的代码之后,它仍然比任何地图版本慢.我的胡萝卜在哪里?!?:

class CounterNoArg(object):
    count = 0
    def count_one(self):
        self.count += 1

def straight_count():
    counter = CounterNoArg()
    for _ in itertools.repeat(None, 10000000):
        counter.count_one()
    print counter.count
Run Code Online (Sandbox Code Playgroud)

输出继电器:

In [111]: %time straight_count()
10000000
CPU times: user 5.44 s, sys: 0.02 s, total: 5.46 s
Wall time: 5.60 s
Run Code Online (Sandbox Code Playgroud)

我问,因为我认为Pythonistas或Pythoneers需要一个胡萝卜,所以我们不会开始将内容传递给任何所有的性能提升,还是已经存在?可能是itertools.imap的等价物,它会一次又一次地调用一个函数,并且可选地调用一定次数.

我管理的最好的是(使用列表理解给出了有趣的结果):

def super_run():
    counter = CounterNoArg()
    for _ in (call() for call in itertools.repeat(counter.count_one, 10000000)):
        pass
    print counter.count

def super_counter_run():
    counter = CounterNoArg()
    [call() for call in itertools.repeat(counter.count_one, 10000000)]
    print counter.count

def run_any_counter():
    counter = Counter()
    any(itertools.imap(counter.count_one, itertools.repeat("foo", 10000000)))
    print counter.count

%time super_run()
10000000
CPU times: user 5.23 s, sys: 0.03 s, total: 5.26 s
Wall time: 5.43 s

%time super_counter_run()
10000000
CPU times: user 4.75 s, sys: 0.18 s, total: 4.94 s
Wall time: 5.80 s

%time run_any_counter()
10000000
CPU times: user 5.15 s, sys: 0.06 s, total: 5.21 s
Wall time: 5.30 s

def run_any_like_presentation():
    any(itertools.imap(_md5.update, itertools.repeat("foo", 10000000)))

def super_run_like_presentation():
    [do_work for do_work in itertools.imap(_md5.update, itertools.repeat("foo", 10000000))]

def super_run_like_presentation_2():
    [_md5.update(foo) for foo in itertools.repeat("foo", 10000000)]


%time run_any_like_presentation()
CPU times: user 5.28 s, sys: 0.02 s, total: 5.29 s
Wall time: 5.47 s

%time super_run_like_presentation()
CPU times: user 6.14 s, sys: 0.18 s, total: 6.33 s
Wall time: 7.56 s

%time super_run_like_presentation_2()
CPU times: user 8.44 s, sys: 0.22 s, total: 8.66 s
Wall time: 9.59 s
Run Code Online (Sandbox Code Playgroud)

啊...

注意:我鼓励您自己运行测试.