在 python 中线程化随机数生成器函数

Threading a random number generator function in python

我在python中有以下代码:

results=[]
for i in range(1,7000000):
    a=(random.sample(range(1, 45), 6))
    results.append(a)

有没有一种方法可以使用线程或任何其他方法来使此代码 运行 更快?目前它只需要很长时间,超过 20 分钟。

多亏了the GIL,这里的线程没有太多收获,但这是一个可以用numpy解决的问题,它可以完全在C层完成工作,节省了大量的启动时间和内存。可以在不到一秒的时间内创建一个 7M x 6 大小的二维数组,其值在给定范围内:

import numpy as np

results = np.random.randint(1, 45, (7000000, 6), np.uint8)

这通常会更快,并且内存效率更高; 6-tuple 的 7M 长 list 将(在 Python 的 64 位版本上)占用绝对最小值大约 700 MB(可能更多,给定分配器开销)。 numpy 数组将占用大约 40 MB。也很容易证明创建这个 list 和所有内部 tuple 是不可避免的成本;单独对 numpy 数组进行微基准测试表明,所有随机数生成只需要大约 420 毫秒,但是从 numpy 数组转换为 list 的 6-tuples最有效的方式将成本提高到 12.5 秒;如果您的机器与我的机器相似,那基本上是 any 纯 Python 解决方案的性能上限,因为这是 Python 为创建tuples 并填充 list:

>>> %timeit -r5 arr = np.random.randint(1, 45, (7000000, 6), np.uint8)
420 ms ± 875 µs per loop (mean ± std. dev. of 5 runs, 1 loop each)

>>> %timeit -r5 arr = list(map(tuple, np.random.randint(1, 45, (7000000, 6), np.uint8)))
12.5 s ± 254 ms per loop (mean ± std. dev. of 5 runs, 1 loop each)

np.random.randint(1, 45, (7000000, 6), np.uint8).tolist()list(map(tuple, ...)) 走得更快(大约需要 2.5 秒),但同样,这只有在 C 级加速器的帮助下才有可能(而且它会使用更多内存,多亏了 lists 内存效率稍低)。

没有 numpy,我能建议的最好的办法是避免一遍又一遍地重新创建 range,方法是在循环外创建一次并重新使用它,例如:

choices = tuple(range(1, 45))  # tuple is generally the fastest structure to index
results = []
for i in range(1, 7000000):
    a = random.sample(choices, 6)
    results.append(a)

虽然这不太可能节省很多; random 模块做了很多 Python 级别的工作,包装了 1-2 个 C 级别的随机生成器,而 Python 级别的工作将是 很多 比完全加速的 C 模块可以做的任何事情都慢。

mtalg 实现了多线程随机数生成,虽然在这种情况下开销起着很大的作用,因为我们谈论的是毫秒,但是对于更大的数组,加速更大。

import numpy as np
import mtalg
rng = np.random.default_rng(seed=1)
mrng = mtalg.random.MultithreadedRNG(seed=1, num_threads=8)

%timeit -r5 rng.integers(1, 45, (7000000, 6), np.uint8)
# 211 ms ± 2.87 ms per loop (mean ± std. dev. of 5 runs, 10 loops each)

%timeit -r5 mrng.integers(1, 45, (7000000, 6), np.uint8)
# 179 ms ± 3.01 ms per loop (mean ± std. dev. of 5 runs, 10 loops each)