10

我正在开发一个 Python 库,该库对长位字符串执行大量按位运算,并且我想找到一种能够最大限度地提高其速度的位字符串类型。我已经尝试过内置的 Python int 类型、numpy、bitstringbitarray,令人惊讶的是,Python int 在按位运算方面似乎更胜一筹。我用谷歌搜索的所有内容都说 numpy 对于像这样的矢量化操作应该更快。我是否以某种方式使用了 numpy 错误?是否有另一个我可以使用的 Python 库实际上改进了 Python 的内置 int 类型?

from timeit import timeit
import random


size = 10000


def int_to_bits(i):
    result = []
    for _ in range(size):
        result.append(i % 2)
        i >>= 1
    return result



x = random.randrange(2**size)
y = random.randrange(2**size)

print(x.bit_length(), y.bit_length())

x_bits = int_to_bits(x)
y_bits = int_to_bits(y)

t = timeit(
    stmt='a & b',
    setup='a = %d; b = %d' % (x, y)
)
print("raw ints:", t)

t = timeit(
    stmt='a & b',
    setup=('import numpy;'
           'a = numpy.array(%r, dtype=int);'
           'b = numpy.array(%r, dtype=int)') % (x_bits, y_bits)
)
print('numpy int array:', t)

t = timeit(
    stmt='a & b',
    setup=('import numpy;'
           'a = numpy.array(%r, dtype=bool);'
           'b = numpy.array(%r, dtype=bool)') % (x_bits, y_bits)
)
print('numpy bool array:', t)

t = timeit(
    stmt='a & b',
    setup=('import numpy;'
           'a = numpy.packbits(%r);'
           'b = numpy.packbits(%r)') % (x_bits, y_bits)
)
print('numpy packed bits:', t)

t = timeit(
    stmt='a & b',
    setup=('import bitstring;'
           'a = bitstring.BitString(%r);'
           'b = bitstring.BitString(%r)') % (x_bits, y_bits)
)
print('bitstring:', t)

t = timeit(
    stmt='a & b',
    setup=('import bitarray;'
           'a = bitarray.bitarray(%r);'
           'b = bitarray.bitarray(%r)') % (x_bits, y_bits)
)
print('bitarray:', t)

结果:

10000 10000
raw ints: 0.29606562735373115
numpy int array: 7.400762747057885
numpy bool array: 1.1108355715984288
numpy packed bits: 1.3064737574273284
bitstring: 380.9796937642803
bitarray: 1.4451143449501842

编辑:

关于 Python ints/longs 上的单个操作如何与整个 numpy 位数组上的向量操作相当,似乎有很多困惑。一个 10,000 位的 Python int/long 值,当被视为位掩码时(使用 & 运算符,就像我们可以在 C/C++ 中使用 ints 或 longs 一样)与长度为 10,000 的 numpy bool 数组直接可比,因为它们都包含相同数量的位,尽管以 2 种不同的方式表示。我尝试过的其他表示 10,000 位的方法也是如此,包括使用 numpy 压缩位数组、numpy int 数组和来自其他库的位数组/字符串类型。它们都是可比较的,因为它们都在相同的位序列上计算相同的函数。这里重要的是我可以表示所有 10,000 位,并且我可以对它们执行按位运算。

如果您仍然对 Python int/long 值如何存储与 numpy bool 数组或 numpy 二进制值 int 数组相同的信息感到困惑,请参考int_to_bits上面代码中的函数;它演示了如何从 Python int/long 中提取位,这表明在两个 10,000 位 int 上执行 & 操作与在 10,000 个布尔值的列表或数组上逐个元素执行它基本相同。

4

2 回答 2

10

据我所知,内置的 Python 3int是您测试的唯一一个一次计算&超过一个字节的块的选项。(我还没有完全弄清楚这个操作的NumPy 源代码中的所有内容是做什么的,但看起来它没有优化以比 dtype 更大的块来计算它。)

  • bitarray逐字节进行,
  • bool 和 1-bit-per-int NumPy 尝试一点一点地进行,
  • 打包的 NumPy 尝试逐字节进行,并且
  • 源代码是bitstring逐字节进行的,并且做一些事情会破坏它通过 Cython 获得速度的尝试,使其成为迄今为止最慢的。

相比之下,根据编译时参数int的值,操作是 15 位还是 30 位数字。我不知道哪个设置是默认设置。PYLONG_BITS_IN_DIGIT

您可以通过使用打包表示和更大的 dtype 来加速 NumPy 尝试。看起来在我的机器上,32 位 dtype 的工作速度最快,超过了 Python 整数;我不知道你的设置是什么样的。使用每种格式的 10240 位值进行测试,我得到

>>> timeit.timeit('a & b', 'import numpy; a = b = numpy.array([0]*160, dtype=num
py.uint64)')
1.3918750826524047
>>> timeit.timeit('a & b', 'import numpy; a = b = numpy.array([0]*160*8, dtype=n
umpy.uint8)')
1.9460716604953632
>>> timeit.timeit('a & b', 'import numpy; a = b = numpy.array([0]*160*2, dtype=n
umpy.uint32)')
1.1728465435917315
>>> timeit.timeit('a & b', 'a = b = 2**10240-1')
1.5999407862400403
于 2015-06-06T06:09:34.527 回答
0

您要测试的内容是这些向量操作吗?您只是想比较 1 次操作的速度,而普通的 python 将获胜,因为它不必设置 numpy 数组或位数组。

试试跟随怎么样?

x = np.array([random.randrange(2**31)]*1000) 
y = np.array([random.randrange(2**31)]*1000) 

%timeit x & y # in ipython

%timeit [ a & b for (a,b) in zip(x,y)] # even though x and y are numpy arrays, we are iterating over them - and not doing any vector operations

有趣的是,如果

xxx = [random.randrange(2**31)] * 1000
yyy = [random.randrange(2**31)] * 1000 

接着

%timeit [a & b for (a,b) in zip(xxx,yyy)]

纯 python 列表,迭代它们比迭代 numpy 数组更快。有点反直觉。不知道为什么。

同样,您可以尝试使用位串和位数组

这是你在看的吗?

于 2015-06-06T05:32:18.033 回答