python位数组(performant)

时间:2013-12-30 18:58:16

标签: python performance bitarray bloom-filter

我正在设计一个bloom过滤器,我想知道Python中性能最高的位数组实现是什么。

Python的优点是它可以处理开箱即用的任意长度整数,这就是我现在使用的,但我不太了解Python内部,知道这是否是在Python中执行它的最高性能方式。

我找到了bitarray,但它处理了很多其他的事情,比如切片,我不需要。我只需要&|以及<<操作。

3 个答案:

答案 0 :(得分:24)

内置的int已经过优化,已经支持&|<<

至少有一个基于GMP的任意长度整数的替代实现,称为gmpy2。 (还有原始的gmpyPyGMPSophie和其他几个包装器围绕同一个库,但我怀疑它们是否有任何真正的性能差异。)

“位数组”理念有两个主要实现,bitarray(你链接的那个)和bitstring,还有一些像intbitset这样的库给你一个类似于集合的界面(它也适用于您的用途)。

所以,让我们把它们一起扔掉并进行比较:

import random
import struct
import timeit
import bitarray
import bitstring
import gmpy2

n = random.randrange((1<<31)+1, 1<<32)

bs = bitstring.pack('<q', n)
ba = bitarray.bitarray(64)
ba.frombytes(struct.pack('<q', n))
gm = gmpy2.mpz(n)
py = n

for x in 'bs', 'ba', 'gm', 'py':
    def f(x=locals()[x]): x | x; x & x
    t = timeit.timeit(f, number=10000)
    print(x, t)

在我的Mac上,运行Python.org 64位CPython 3.3.2,这是我得到的:

bs 0.7623525890521705
ba 0.006623028079047799
gm 0.0016346259508281946
py 0.002280334010720253

这似乎足以立即解雇bitstring

此外,虽然我没有在这里显示intbitset,但因为我发现它和我发现的任何类似的库都没有使用Python 3,从各种比较(使用intbitset.intbitset([i for i, bit in enumerate(bin(n)[2:]) if bit != '0']))它可以从14到14在每次测试中,它都比int慢70倍,所以我也顺便解雇了它。


所以让我们尝试其他三个更多的代表:

ba 6.385123810963705
gm 1.5937359740491956
py 2.129726824001409

这些数据仍然存在。 bitarray远不及内置int。使用它也更笨拙(请注意,什么应该是“替代构造函数”classmethod是一个实例方法,没有快速简便的方法来转换或转换为int,以及我只测试x | xx & x是运营商的限制)。如果你需要一个整数作为位数组,那就太棒了;如果你需要对一个整数进行C风格的位置修改,那就不是它最好的了。


对于gmpy2,它似乎比原始int快三分之一。如果我们将数字变得更大,比如1.5kbits会怎么样?

gm 0.19562570203561336
py 0.29293217696249485

以下是Apple Python 2.7.5的数字:

('gm', 0.2890629768371582)
('py', 0.36592698097229004)

那么,值得吗?它使用起来不太友好,它在你没有问过的其他一些操作上比较慢而不是更快,它需要第三方C库,这是LGPL许可的,它在内存溢出情况下有更糟糕的错误处理行为(例如,你的应用程序可能是段错误而不是提升),并且StackOverflow上至少有一个人会出现并告诉你GMP在被提及时很糟糕(我相信是因为旧版本中的错误)。 / p>

但如果你需要额外的速度,也许这是值得的。


另一方面,这里是PyPy,3.2.3 / 2.1.0b1和PyPy 2.7.3 / 2.1.0,使用原生int类型 - 与0.19562570203561336和0.2890629768371582上面的gmpy2结果比较:< / p>

py 0.2135779857635498
('py', 0.20878291130065918)

因此,从CPython切换到PyPy几乎可以获得与在Python 3中从int切换到gmpy2.mpz几乎一样多的好处,并且在Python 2中获得更多的好处。并且它几乎肯定会加速其余的你的代码也是如此。

答案 1 :(得分:6)

免责声明:我是其中一条评论中提到的intbitset :-)的主要开发者。这只是为了让您知道,因为几个星期intbitset现在与Python 3.3和3.4兼容。此外,看起来它的WRT几乎是原始int功能的两倍:

import random
from intbitset import intbitset
x = random.sample(range(1000000), 10000)
y = random.sample(range(1000000), 10000)
m = 0
for i in x:                 
    m += 1 << i
n = 0
for i in x:                 
    n += 1 << i
mi = intbitset(x)
ni = intbitset(y)

%timeit m & n ## native int
10000 loops, best of 3: 27.3 µs per loop

%timeit mi & ni ## intbitset
100000 loops, best of 3: 13.9 µs per loop

%timeit m | n ## native int
10000 loops, best of 3: 26.8 µs per loop

%timeit mi | ni ## intbitset
100000 loops, best of 3: 15.8 µs per loop

## note the above were just tested on Python 2.7, Ubuntu 14.04.

此外intbitset支持一些独特的功能,例如无限集,这些功能非常有用,例如构建具有 universe 概念的搜索引擎(例如,使用常规集合获取无限集的并集将返回无限集等)。

有关intbitset性能的更多信息,请参阅WRT Python集:http://intbitset.readthedocs.org/en/latest/#performance

答案 2 :(得分:1)

也许看看这个。它是纯python,并使用int的数组: http://stromberg.dnsalias.org/svn/bits/trunk/

此外,已有几个Python bloom过滤器。检查Pypi: https://pypi.python.org/pypi?%3Aaction=search&term=bloom+filter&submit=search

HTH