感谢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 模块可以做的任何事情都慢很多。