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



我在python中有以下代码:

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

有没有办法使用线程或任何其他方法来使这段代码运行得更快?目前它只需要永远,超过20分钟。

由于 GIL,这里的线程并没有太多好处,但这是一个可以用numpy解决的问题,它可以完全在 C 层执行工作,节省大量时间和内存来启动。可以在不到一秒钟的时间内创建具有给定范围内值的 7M x 6 大小的 2D 数组:

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

一般来说,这将更快,并且内存效率更高;一个7M长的六tuplelist将(在64位版本的Python上)占用大约700 MB的绝对最小值(考虑到分配器开销,可能更多)。numpy阵列将占用大约 40 MB。也很容易表明,创建具有所有内在tuplelist具有不可避免的成本;仅对numpy阵列进行微基准测试表明,所有随机数生成只需要大约 420 毫秒,但以最有效的方式从numpy数组转换为 6 秒tuplelist会使成本高达 12.5 秒;如果你的机器和我的机器相似,那本质上是任何纯Python解决方案的性能上限,因为它是Python为创建tuple和填充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 级加速器辅助而成为可能(并且由于list的内存效率较低,它会使用更多的内存)。

如果没有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模块在包装 1-2 个 C 级随机性生成器时做了大量的 Python 级工作,而且 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)

最新更新