自定义迭代器性能



在使用自定义迭代器对小容器进行迭代时,我遇到了一些非常令人惊讶的性能差异。我希望有人能帮助我理解这些差异是从哪里来的。

首先是一些上下文;我正在使用boost::python编写许多python扩展模块,其中一个模块包含到实现getitem的3d浮点向量类型的绑定。由于它已经确定了可以对其进行迭代,但它似乎很慢,但原因并不明显,所以我决定在python中使用一些简单的自定义迭代器,以更好地了解事情是如何工作的。这就是这些迭代器的来源。。。

class MyIterator1(object):
    __slots__ = ['values', 'popfn']
    def __init__(self):
        self.values = ['x', 'y', 'z']
        self.popfn = self.values.pop
    def __length_hint__(self):
        return 3
    def __iter__(self):
        return self
    def next(self):
        try:
            return self.popfn()
        except IndexError:
            raise StopIteration
class MyIterator2(object):
    __slots__ = ['values', 'itfn']
    def __init__(self):
        self.values = ['x', 'y', 'z']
        it = iter(self.values)
        self.itfn = it.next
    def __length_hint__(self):
        return 3
    def __iter__(self):
        return self
    def next(self):
        return self.itfn()
class MyIterator3(object):
    __slots__ = ['values', 'i']
    def __init__(self):
        self.values = ['x', 'y', 'z']
        self.i = 0
    def __length_hint__(self):
        return 3
    def __iter__(self):
        return self
    def next(self):
        if self.i >= 3:
            raise StopIteration
        value = self.values[self.i]
        self.i += 1
        return value
def MyIterator4():
    val = ['x', 'y', 'z']
    yield val[0]
    yield val[1]
    yield val[2]

接下来,我用timeit模块(假设上面的代码在一个名为testter的模块中)运行了这些脚本

import timeit
timer1 = timeit.Timer('r = list(testiter.MyIterator1())', 'import testiter')
timer2 = timeit.Timer('r = list(testiter.MyIterator2())', 'import testiter')
timer3 = timeit.Timer('r = list(testiter.MyIterator3())', 'import testiter')
timer4 = timeit.Timer('r = list(testiter.MyIterator4())', 'import testiter')
timer5 = timeit.Timer('r = list(iter(["x", "y", "z"]))', 'import testiter')
print 'list(testiter.MyIterator1())'
print timer1.timeit()
print "n"
print 'list(testiter.MyIterator2())'
print timer2.timeit()
print "n"
print 'list(testiter.MyIterator3())'
print timer3.timeit()
print "n"
print 'list(testiter.MyIterator4())'
print timer4.timeit()
print "n"
print 'list(iter(["x", "y", "z"]))'
print timer5.timeit()

这会打印出以下

list(testiter.MyIterator1())
8.57359290123

list(testiter.MyIterator2())
5.28959393501

list(testiter.MyIterator3())
6.11230111122

list(testiter.MyIterator4())
2.31263613701

list(iter(["x", "y", "z"]))
1.26243281364

不出所料,python列表迭代器是最快的,有相当大的优势。我认为这是由于python中的一些神奇的优化。生成器也比MyIterator类快得多,我对此并不感到惊讶,并认为这是由于所有的工作都是在c中完成的,但这只是猜测。现在其他的更令人困惑/支持。try/except语句是否像在这种情况下看起来那么昂贵,还是发生了其他事情?

如有任何帮助解释这些差异,我们将不胜感激!很抱歉发了这么长的帖子。

我脑海中浮现的一些想法;很抱歉,如果它们不够明显:

  • 第一个迭代器使用列表的pop来实现next,这意味着在检索到每个元素后,列表都会发生变化。也许这种动态分配的复合数据结构的突变正在降低性能。这一切都取决于列表的实现细节,所以这可能完全无关
  • 最后一个迭代器是在一个简单的上下文中使用连接到语言中的特性(yield)来产生结果。我想,这表明与试图实现相同结果的自定义迭代器类相比,还有更大的优化空间
  • 第五个定时器没有使用自定义迭代器,而是直接使用为列表提供的迭代器。列表的迭代器可能优化得很好,而且这些自定义类没有使用间接层,其中一些类在内部使用这样的列表迭代器

最新更新