有几种可能性,比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对象具有大量的存储开销,所以最终的bytearray占用的内存将是其消耗内存的3-7倍。
使用专门的函数生成大块数据可以提高性能并避免填充内存。
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/s:使用
randint
的原始解决方案:
- 4.8 MB/s:
bytearray(getrandbits(8) for _ in xrange(n))
:(其他用户提供的解决方案)
- 6.4MB/s:
bytearray(map(getrandbits,(8,)*n))
- 7.2 MB/s:
itertools
和getrandbits
- 10 MB/s:
os.urandom
- 23 MB/s:
itertools
和struct
- 35 MB/s:优化函数(对于长度为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)
from timeit import timeit
t=timeit(lambda:encryptor.update(nulls),number=10**5)
print("%.1f MB/s"%(1000/t))
这会以每秒180 MB的速度产生伪随机数据。(无硬件AES加速,单核)这仅比上面的纯Python代码快~5倍。
附录
有一个等待编写的纯Python加密库。将上述技术与hashlib
和流密码技术结合起来看起来很有前途。这里有一个引人注目的东西,即快速字符串异或(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)))
/dev/urandom
? - Santa