10

長いビット文字列に対して多くのビット単位の操作を実行する Python ライブラリに取り組んでおり、その速度を最大化するビット文字列型を見つけたいと考えています。組み込みの Python int 型である numpy、bitstring、およびbitarrayを試してみましたが、驚くべきことに、ビット演算に関しては Python の int が圧倒的に勝っているようです。私がグーグルで調べたものはすべて、このようなベクトル化された操作ではnumpyがはるかに高速になるはずだと言っています。どういうわけか間違ってnumpyを使用していますか? Python の組み込みの int 型を実際に改善するために使用できる別の Python ライブラリはありますか?

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 の int/long に対する単一の操作が、numpy ビット配列全体に対するベクトル操作にどのように匹敵するかについて、多くの混乱があるようです。10,000 ビットの Python の int/long 値は、ビット マスクとして扱われる場合 (C/C++ で int や long と同じように & 演算子を使用)、長さ 10,000 の numpy bool 配列と直接比較できます。 2 つの異なる方法で表されますが、同じ数のビットが含まれます。numpy パック ビット配列、numpy int 配列、および他のライブラリのビット配列/文字列型の使用など、私が試した 10,000 ビットを表す他の方法にも同じことが当てはまります。それらはすべて、同じビットシーケンスで同じ関数を計算しているため、すべて比較可能です。ここで重要なのは、10,000 ビットすべてを表現できることと、それらに対してビット演算を実行できることです。

int_to_bitsPython の int/long 値が numpy bool 配列または numpy バ​​イナリ値の int 配列と同じ情報を格納する方法についてまだ混乱している場合は、上記のコードの関数を参照してください。これは、Python int/long からビットを抽出する方法を示しています。これは、2 つの 10,000 ビット int に対して & 操作を実行することは、10,000 のブール値のリストまたは配列に対して要素ごとに実行することと基本的に同じであることを示しています。

4

2 に答える 2

10

私が知る限り、組み込みの Python 3は、一度に複数のバイトのチャンクintを計算する、テストしたオプションの唯一のオプションです。(この操作のNumPyソース&のすべてが何をするのか完全には理解していませんが、dtypeよりも大きなチャンクでこれを計算するための最適化があるようには見えません。)

  • bitarrayバイト単位で進み、
  • bool および 1-bit-per-int NumPy の試行は少しずつ進み、
  • パックされた NumPy 試行はバイト単位で行われ、
  • bitstringソースはバイトごとに進み、Cython を介して速度を上げようとする試みを台無しにするいくつかのことを行い、はるかに遅くなります。

対照的に、操作は、コンパイル時のパラメーターintの値に応じて、15 ビットまたは 30 ビットの数字で行われます。どの設定がデフォルトなのかわかりません。PYLONG_BITS_IN_DIGIT

パックされた表現とより大きな dtype を使用することで、NumPy の試行を高速化できます。私のマシンでは、32 ビットの dtype が最も速く動作し、Python の int を上回っているようです。あなたの設定でそれがどのようなものかわかりません。各フォーマットで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 つの操作の速度を比較しようとしているだけで、numpy 配列や bitarray をセットアップする必要がないため、単純な python が勝つでしょう。

以下を試してみてはいかがでしょうか?

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 に答える