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

Taj*_*any 9 python performance timing multiprocessing python-multiprocessing

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

但是,我也在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)
Run Code Online (Sandbox Code Playgroud)

这些是基准测试的结果:

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
Run Code Online (Sandbox Code Playgroud)

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

然而,在3亿随机数之后,这个减少了,并且再次增加到8亿(我没有进一步测试).

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

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

Ray*_*ger 1

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

这可能是垃圾收集启动的产物。一个简单的实验是关闭 GC 并查看“下降”是否持续:

>>> import gc
>>> gc.disable()
Run Code Online (Sandbox Code Playgroud)

另一种可能性是,这是在幕后使用realloc()实现列表增长的产物。实现的列表是固定长度的指针数组。当map()使用append()增加列表时,会定期调用C函数realloc()来调整指针数组的大小。通常,此调用的成本非常低,因为无需移动任何数据。但是,如果内存中的单个字节“阻碍”了大小调整,则所有数据都必须重新定位。这是非常昂贵的,如果在执行时多处理正在创建一个阻塞字节,则可能会导致您的“下降”。

为了测试这个假设,您可以使用imap()而不是map()并将结果输入到collections.deque()而不是list()。双端队列实现没有使用relloc,因此面对碎片内存时其性能是一致的(内部只是重复调用malloc()来获得固定长度的内存块)。