我正在设计一个bloom过滤器,我想知道Python中性能最高的位数组实现是什么。
Python的优点是它可以处理开箱即用的任意长度整数,这就是我现在使用的,但我不太了解Python内部,知道这是否是在Python中执行它的最高性能方式。
我找到了bitarray
,但它处理了很多其他的事情,比如切片,我不需要。我只需要&
和|
以及<<
操作。
答案 0 :(得分:24)
内置的int
已经过优化,已经支持&
,|
和<<
。
至少有一个基于GMP的任意长度整数的替代实现,称为gmpy2
。 (还有原始的gmpy
,PyGMP
,Sophie
和其他几个包装器围绕同一个库,但我怀疑它们是否有任何真正的性能差异。)
“位数组”理念有两个主要实现,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 | x
和x & 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