【问题标题】:Threading a random number generator function in python在python中线程化一个随机数生成器函数
【发布时间】:2018-04-17 01:31:48
【问题描述】:

我在python中有以下代码:

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

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

【问题讨论】:

  • 这是 Python 2 还是 3?在任何一种情况下,一遍又一遍地调用range(1, 45) 会增加不小的开销;在循环之外创建它可能会有所帮助(特别是在 Py2 上,一遍又一遍地重新创建它涉及一遍又一遍地制作 44 个元素列表,而不仅仅是在 Py3 上使用的虚拟序列)。考虑到 GIL,线程可能不会为您带来任何好处,并且多处理可能会花费与并行保存的时间一样多的时间将值从工作人员传输到父进程。你可以考虑使用numpy;它可以很快生成一个二维随机数组。
  • @ShadowRanger 我正在使用 python 3.6。我如何调用循环外的范围,因为我需要它重复 7000000 次

标签: python python-multithreading


【解决方案1】:

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

import numpy as np

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

这通常会更快,并且内存效率更高; 6-tuples 中的 7M 长 list 将(在 64 位版本的 Python 上)占用绝对最小值约 700 MB(可能更多,给定分配器开销)。 numpy 数组将占用大约 40 MB。也很容易证明,创建这个 list 与所有内部 tuples 具有不可避免的成本;单独对numpy 数组进行微基准测试表明,所有随机数生成只需要大约 420 毫秒,但以最有效的方式从numpy 数组转换为六个tuples 的list 会使成本高达12.5 秒;如果您的机器与我的相似,那么这实际上是对任何纯 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 模块可以做的任何事情都慢很多

【讨论】:

  • 太棒了!非常感谢,使用 numpy 在几秒钟内得到结果。有没有一种内置方法可以确保结果不会重复,或者我必须单独编写代码?
  • @West:我确信有办法,但我承认我在此方面对numpy 的经验有限;我找不到任何明显的方法来做到这一点。
  • 好的,会调查的。干杯
  • 避免重复将是 非常 cpu 密集型的。你最好的选择可能是将所有结果放入一个哈希集中,查看你有多少冲突,然后在纯 Python 代码中重做你需要的任何数量,随时检查冲突。
  • 特别是如果订购很重要(即如果(1, 2, 3, 4, 5, 6) == (6, 5, 4, 3, 2, 1)),那么您将有很多冲突。
【解决方案2】:

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)

【讨论】:

    猜你喜欢
    • 2023-01-03
    • 1970-01-01
    • 1970-01-01
    • 2011-09-18
    • 1970-01-01
    • 2018-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多