内置的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'])
)中,它的速度比int
我进行的每次测试都慢14到70倍,因此我也已将其驳回。
因此,让我们尝试另外三个代表:
ba 6.385123810963705
gm 1.5937359740491956
py 2.129726824001409
和数字保持。bitarray
没有内置的那么快int
。使用起来也比较笨拙(请注意,应该是“替代构造函数”的类方法是实例方法,没有快速简便的方法可以将int或int转换为int,我之所以进行测试x | x
,x & x
是因为存在局限性在运营商上)。如果需要整数作为位数组,那很好。如果您需要对整数进行C风格的位计算,那不是最好的方法。
至于gmpy2
,它似乎比本地人快三分之一int
。如果我们使数字大得多,例如1.5kbit,该怎么办?
gm 0.19562570203561336
py 0.29293217696249485
以下是Apple Python 2.7.5的数字:
('gm', 0.2890629768371582)
('py', 0.36592698097229004)
那么,值得吗?它使用起来不太友好,在您没有要求的其他一些操作上,它的运行速度较慢而不是更快,它需要LGPL许可的第三方C库,在内存溢出情况下错误处理行为要差得多(例如,您的应用可能会出现段错误而不是提高错误),并且StackOverflow上至少会有一个人出现并告诉您,只要提及GMP,它就很烂(我相信是因为旧版本中存在错误)。
但是,如果您需要这种额外的速度,也许值得。
另一方面,这是PyPy,3.2.3 / 2.1.0b1和PyPy 2.7.3 / 2.1.0,使用本机int
类型- 与上述gmpy2的结果0.19562570203561336和0.2890629768371582进行了比较:
py 0.2135779857635498
('py', 0.20878291130065918)
因此,从cpython切换到PyPy所带来的好处几乎int
与gmpy2.mpz
在Python 3中从切换到PyPy一样多,并且在Python 2中带来的好处更多。几乎可以肯定的是,它也将加速其余代码。