最快的Python相当于切换整数数组

时间:2011-12-04 17:31:48

标签: python optimization

我想找到在C中完成switch工作的最快方法。我正在编写一些Python代码来替换C代码,除了瓶颈外,它们都运行良好。这段代码用于紧密循环,因此获得最佳性能确实非常重要。

受害者尝试1: 首先尝试,例如this之前的问题建议使用哈希表进行查找。这最终变得异常缓慢。

恐惧症尝试2 我做的另一个优化是创建一系列if ... return语句,这使我的速度提升了13%。它仍然令人失望地缓慢。

受害者尝试3 我创建了所有可能输入值的array.array,并进行了索引查找。这导致总体上加速43%,这是可观的。

我正在使用array.arraymap上运行并向其传递转换函数。这个函数正在进行查找。我的开关正在处理短整数(它是一个类型化的数组)。如果这是GCC C,编译器将创建一个跳转表。令人沮丧的是,要知道Python要么在查找​​表条目时要哈希值,要么在if的情况下进行大量的比较。我从分析中得知慢速函数恰好是正在查找的函数。

将一个整数映射到另一个整数的绝对最快方法是什么,如果相关,则映射到array.array。还有比上面更快的东西吗?

修改

虽然它让我看起来只是一个白痴,只是为了实现,我会说它anwyay!请记住,在分析器中运行代码会使代码慢下来 lot 。就我而言,慢了19倍。突然间,我的瓶颈并不是那么糟糕!非常感谢大家的所有答案。这个问题仍然有效。我会稍微提出这个问题,因为可能会有一些有趣的答案。

使用分析器,我的测试数据集:

real    0m37.309s
user    0m33.263s
sys     0m4.002s

real    0m2.595s
user    0m2.526s
sys     0m0.028s

2 个答案:

答案 0 :(得分:2)

我认为其他人提出numpy或pure c是正确的;但对于纯蟒蛇来说,这里有一些时间,因为它们是值得的。基于这些,我有点惊讶array.array表现得比dict好得多。你是在循环内动态创建这些表吗?或者我误解了你的问题?无论如何,这表明list实际上是最好的方式。

>>> def make_lookup_func(table):
...     def lookup(val, t=table):
...         return t[val]
...     return lookup
... 
>>> lookup_tuple = make_lookup_func(tuple(range(10)))
>>> lookup_list = make_lookup_func(list(range(10)))
>>> lookup_array = make_lookup_func(array.array('i', range(10)))
>>> lookup_dict = make_lookup_func(dict(zip(range(10), range(10))))
>>> %timeit lookup_tuple(9)
10000000 loops, best of 3: 177 ns per loop
>>> %timeit lookup_list(9)
10000000 loops, best of 3: 158 ns per loop
>>> %timeit lookup_array(9)
10000000 loops, best of 3: 181 ns per loop
>>> %timeit lookup_dict(9)
10000000 loops, best of 3: 166 ns per loop

缩放行为:

>>> lookup_tuple = make_lookup_func(tuple(range(10000)))
>>> lookup_list = make_lookup_func(list(range(10000)))
>>> lookup_array = make_lookup_func(array.array('i', range(10000)))
>>> lookup_dict = make_lookup_func(dict(zip(range(10000), range(10000))))
>>> %timeit lookup_tuple(9000)
10000000 loops, best of 3: 177 ns per loop
>>> %timeit lookup_list(9000)
10000000 loops, best of 3: 158 ns per loop
>>> %timeit lookup_array(9000)
10000000 loops, best of 3: 186 ns per loop
>>> %timeit lookup_dict(9000)
10000000 loops, best of 3: 195 ns per loop

答案 1 :(得分:1)

当在这种类型的应用程序中使用时,分支逻辑在python中通常会非常缓慢,并且您基本上可以采用一种更好的方法来执行此操作,以便在整数之间进行转换。还有一些要试验的东西:

您可能会尝试使用np.array或使用Cython(或直接使用C)进行紧密循环。这些需要一些额外的设置(并且可能在C中编写内部循环),但也可以为这种类型的应用程序提供巨大的加速,并且可以让您利用优秀的C优化器。

可以采用任何一种方式而且更多是微优化的东西是你可以尝试使用列表理解而不是地图,或者确保你没有在地图中使用lambda。在map()中不使用lambda实际上是一个非常大的lambda,而列表理解和map之间的差异往往相对较小。