为什么生成 400,000,000 个随机数的速度会下降?



我在具有 8 GB RAM 的 macOS 上并行生成大约 400,000,000(4 亿个)随机数,该 i7 具有 4 个内核(8 个线程超线程)。

但是,我也在 DigitalOcean 服务器上生成了 400,000,000 个随机数,该服务器在 Debian 上具有 20 个内核,具有 64 GB RAM。

代码如下:

import multiprocessing
import random
rangemin = 1
rangemax = 9
def randomGenPar_backend(backinput):
return random.randint(rangemin, rangemax)
def randomGenPar(num):
pool = multiprocessing.Pool()
return pool.map(randomGenPar_backend, range(0, num))
randNum = 400000000
random.seed(999)
randomGenPar(randNum)

这些是基准测试的结果:

5,000,000 Random Numbers:
1 Core: 5.984
8 Core: 1.982
50,000,000 Random Numbers:
1 Core: 57.28
8 Core: 19.799
20 Core: 18.257
Times Benefit (20 core vs. 8 core) = 1.08
100,000,000 Random Numbers:
1 Core: 115
8 Core: 40.434
20 Core: 31.652
Times Benefit (20 core vs. 8 core) = 1.28
200,000,000 Random Numbers:
8 Core: 87
20 Core: 60
Times Benefit (20 core vs. 8 core) = 1.45
300,000,000 Random Numbers:
8 Core: 157
20 Core: 88
Times Benefit (20 core vs. 8 core) = 1.78
400,000,000 Random Numbers:
8 Core: 202
20 Core: 139
Times Benefit (20 core vs. 8 core) = 1.45 (DIP!)
500,000,000 Random Numbers:
8 Core: 280
20 Core: 171
Times Benefit (20 core vs. 8 core) = 1.64 (INCREASE!)
600,000,000 Random Numbers:
8 Core: 342
20 Core: 198
Times Benefit (20 core vs. 8 core) = 1.73
700,000,000 Random Numbers:
8 Core: 410
20 Core: 206
Times Benefit (20 core vs. 8 core) = 1.99
800,000,000 Random Numbers:
8 Core: 482
20 Core: 231
Times Benefit (20 core vs. 8 core) = 2.09

通常,生成的随机数越多,可以使用的 20 核 CPU 的并行性就越多。因此,速度从 8 核到 20 核的"倍数增加"随着时间的推移而增加。

但是,在 3 亿个随机数之后,这个数字会减少,并再次增加,直到 8 亿(我没有进一步测试)。

这是为什么呢?有具体原因吗?只是随机的吗?(我已经重复了两次,两次都得到了相同的结果)

编辑:如果有任何区别,我正在使用time函数来计时脚本的执行。此外,两台机器上的操作系统也不相同(8 核 - macOS,20 核 - Debian)。

我想到了两种可能的解释。

这可能是垃圾收集启动的人工制品。 一个简单的实验是关闭GC,看看"下降"是否持续存在:

>>> import gc
>>> gc.disable()

另一种可能性是,这是在引擎盖下使用realloc()的列表增长的工件。 实现的列表是固定长度的指针数组。 当map() 使用append() 增长列表时,C 函数调用realloc()会定期调用以调整指针数组的大小。 通常,此调用非常便宜,因为无需移动任何数据。 但是,即使内存中的单个字节"阻碍"调整大小,也必须重新定位所有数据。 这是非常昂贵的,如果在执行多处理的那个点上正在创建一个阻塞字节,可能会导致你成为"下降"。

为了验证这个假设,你可以使用imap() 而不是 map(),并将结果输入到collections.deque() 而不是list()。 deque 实现不使用relloc,因此它在面对碎片内存时的性能是一致的(在内部,它只是重复调用malloc()以获得固定长度的内存块)。

相关内容

  • 没有找到相关文章

最新更新