在 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-tuple
s最有效的方式将成本提高到 12.5 秒;如果您的机器与我的机器相似,那基本上是 any 纯 Python 解决方案的性能上限,因为这是 Python 为创建tuple
s 并填充 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
s 内存效率稍低)。
没有 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)
我在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-tuple
s最有效的方式将成本提高到 12.5 秒;如果您的机器与我的机器相似,那基本上是 any 纯 Python 解决方案的性能上限,因为这是 Python 为创建tuple
s 并填充 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
s 内存效率稍低)。
没有 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)