web-dev-qa-db-ja.com

大きなランダムバイトアレイを作成する効率的な方法

特定のサイズの大きなバイトリーを作成する必要がありますが、実行前にサイズがわかりません。バイトはかなりランダムである必要があります。バイトアレイのサイズは、数KBから数MBまでです。バイトごとに繰り返したくありません。これは遅すぎます-numpy.randomと同様のパフォーマンスが必要です。ただし、このプロジェクトで使用できるnumpyモジュールはありません。これを行う標準のpythonインストールの一部はありますか?または Cを使用して自分でコンパイルする

タイミングを求める人のために:

>>> timeit.timeit('[random.randint(0,128) for i in xrange(1,100000)]',setup='import random', number=100)
35.73110193696641
>>> timeit.timeit('numpy.random.random_integers(0,128,100000)',setup='import numpy', number=100)
0.5785652013481126
>>> 
30
Paul

Osモジュールは、Windowsでもurandomを提供します。

bytearray(os.urandom(1000000))

これは必要なだけ速く実行されるようですが、実際、私はあなたのnumpyよりも良いタイミングを取得します(私たちのマシンは大きく異なる可能性がありますが):

timeit.timeit(lambda:bytearray(os.urandom(1000000)), number=10)
0.0554857286941
39
Ned Batchelder

Numpyを含めるだけの何が問題になっていますか?とにかく、これはランダムなNビット整数を作成します。

_import random
N = 100000
bits = random.getrandbits(N)
_

したがって、j番目のビットの値が設定されているかどうかを確認する必要がある場合は、bits & (2**j)==(2**j)を実行できます。

編集:彼はビット配列ではなくバイト配列を要求しました。ネッドの答えはより良いです:your_byte_array= bytearray((random.getrandbits(8) for i in xrange(N))

7
dr jimbob

いくつかの可能性がありますが、_os.urandom_よりも速いものもあります。また、データをランダムシードから決定論的に生成する必要があるかどうかも検討してください。これは、障害を再現可能にする必要がある単体テストにとって非常に貴重です。

短くてピチピチ:

lambda n:bytearray(map(random.getrandbits,(8,)*n))

上記を単体テストに使用しましたが、十分に高速でしたが、より高速に実行できますか?

itertoolsの使用:

lambda n:bytearray(itertools.imap(random.getrandbits,itertools.repeat(8,n))))

itertoolsとstructは、反復ごとに8バイトを生成します

_lambda n:(b''.join(map(struct.Struct("!Q").pack,itertools.imap(
    random.getrandbits,itertools.repeat(64,(n+7)//8)))))[:n]
_

_b''.join_に基づくものはすべて、サブストリングを結合する前にすべてのサブ文字列をキューに入れ、pythonオブジェクトには- 大量のストレージオーバーヘッド

特殊な関数を使用して大きなチャンクを生成すると、パフォーマンスが向上し、メモリがいっぱいになるのを防ぎます。

_import random,itertools,struct,operator
def randbytes(n,_struct8k=struct.Struct("!1000Q").pack_into):
    if n<8000:
        longs=(n+7)//8
        return struct.pack("!%iQ"%longs,*map(
            random.getrandbits,itertools.repeat(64,longs)))[:n]
    data=bytearray(n);
    for offset in xrange(0,n-7999,8000):
        _struct8k(data,offset,
            *map(random.getrandbits,itertools.repeat(64,1000)))
    offset+=8000
    data[offset:]=randbytes(n-offset)
    return data
_

パフォーマンス

  • 。84 MB/srandintを使用した元のソリューション:
  • 4.8 MB/sbytearray(getrandbits(8) for _ in xrange(n)) :(他の投稿者による解決策)
  • 6.4MB /秒bytearray(map(getrandbits,(8,)*n))
  • 7.2 MB/sitertoolsおよびgetrandbits
  • 10 MB/s:_os.urandom_
  • 23 MB/sitertoolsおよびstruct
  • 35 MB/s:最適化された関数(len = 100MB ... 1KBを保持)

注:すべてのテストでは、文字列サイズとして10KBを使用しました。結果は、中間結果がメモリを満たすまで一貫していました。

注:_os.urandom_は、安全なランダムシードを提供することを目的としています。アプリケーションは、独自の高速PRNGでそのシードを拡張します。次に、カウンターモードでAESをPRNGとして使用する例を示します。

_import os
seed=os.urandom(32)

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
backend = default_backend()
cipher = Cipher(algorithms.AES(seed), modes.CTR(b'\0'*16), backend=backend)
encryptor = cipher.encryptor()

nulls=b'\0'*(10**5) #100k
from timeit import timeit
t=timeit(lambda:encryptor.update(nulls),number=10**5) #1GB, (100K*10k)
print("%.1f MB/s"%(1000/t))
_

これにより、180 MB/sで疑似乱数データが​​生成されます。 (ハードウェアAESアクセラレーションなし、シングルコア)それは〜5x上記の純粋なpythonコードの速度)だけです。

補遺

純粋なpython暗号ライブラリが書き込まれるのを待っています。上記の手法をhashlibとストリーム暗号手法と組み合わせると、有望に見えます。これがティーザー、高速文字列xor(42MB/s)。

_def xor(a,b):
    s="!%iQ%iB"%divmod(len(a),8)
    return struct.pack(s,*itertools.imap(operator.xor,
        struct.unpack(s,a),
        struct.unpack(s,b)))
_
5
_import random
def randbytes(n):
    for _ in xrange(n):
        yield random.getrandbits(8)

my_random_bytes = bytearray(randbytes(1000000))
_

Itertoolsにはおそらくここで役立つ何かがありますが、常にあります...

私のタイミングは、これが[random.randint(0,128) for i in xrange(1,100000)]よりも約5倍速くなることを示しています

3
Ned Batchelder