【问题标题】:Why the dip in speed increase for generating 400,000,000 random numbers?为什么生成 400,000,000 个随机数的速度会下降?
【发布时间】:2017-11-13 16:33:51
【问题描述】:

我在配备 8 GB RAM 的 macOS 上使用 4 核(8 线程超线程)的 Intel i7 并行生成大约 400,000,000(4 亿)个随机数。

但是,我还在 64 GB RAM 的 Debian 上具有 20 个内核的 D​​igitalOcean 服务器上生成 400,000,000 个随机数。

代码如下:

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)。

【问题讨论】:

  • 您介意根据生成的随机值的数量提供两种速度的图表吗?估计你给出的数字有点困难......
  • 您在本地运行的 OS 是否与在 DigitalOcean 上运行的相同?你看过os.urandom而不是random.randint的表现吗
  • 您如何衡量绩效?是否有可能在服务器上运行的其他一些任务暂时阻塞了 CPU?
  • @Błotosmętek,我在基准测试期间没有运行任何其他任务。
  • 您能否添加有关您拥有多少 RAM 的信息?这很可能是相关的:在 64 位机器上,float 对象的 Python 列表每个浮点数将占用 32 个字节(包括从列表到 float 对象的指针),所以有 4 亿个浮点数,那就是最终列表约为 12.8 GB。这忽略了任何中间列表占用的 RAM。

标签: python performance multiprocessing python-multiprocessing timing


【解决方案1】:

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

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

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

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

要检验这个假设,您可以使用 imap() 代替 map() 并将结果输入 collections.deque()而不是 list()。 deque实现没有使用relloc,所以它在面对碎片化内存时性能是一致的(内部只是重复调用malloc()来获得固定长度的内存块)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-29
    • 1970-01-01
    • 2020-09-17
    • 1970-01-01
    • 1970-01-01
    • 2014-01-29
    • 2018-08-27
    • 1970-01-01
    相关资源
    最近更新 更多