当前位置: 首页 > 面试题库 >

使用multiprocessing.Manager.list而不是真实列表会使计算耗时

郭子航
2023-03-14
问题内容

我想尝试multiprocessing从本示例开始的不同使用方式:

$ cat multi_bad.py 
import multiprocessing as mp
from time import sleep
from random import randint

def f(l, t):
#   sleep(30)
    return sum(x < t for x in l)

if __name__ == '__main__':
    l = [randint(1, 1000) for _ in range(25000)]
    t = [randint(1, 1000) for _ in range(4)]
#   sleep(15)
    pool = mp.Pool(processes=4)
    result = pool.starmap_async(f, [(l, x) for x in t])
    print(result.get())

这里l是一个列表,当生成4个进程时,该列表将被复制4次。为了避免这种情况,文档页面提供了使用队列,共享数组或使用创建的代理对象的信息multiprocessing.Manager。对于最后一个,我更改了的定义l

$ diff multi_bad.py multi_good.py 
10c10,11
<     l = [randint(1, 1000) for _ in range(25000)]
---
>     man = mp.Manager()
>     l = man.list([randint(1, 1000) for _ in range(25000)])

结果看起来仍然正确,但是执行时间却大大增加,以至于我做错了什么:

$ time python multi_bad.py 
[17867, 11103, 2021, 17918]

real    0m0.247s
user    0m0.183s
sys 0m0.010s

$ time python multi_good.py 
[3609, 20277, 7799, 24262]

real    0m15.108s
user    0m28.092s
sys 0m6.320s

文档确实说这种方法比共享数组要慢,但这感觉很不对。我也不确定如何配置此文件,以获取有关正在发生的事情的更多信息。我想念什么吗?

PS与共享数组,我得到的时间低于0.25s。

PPS这是在Linux和Python 3.3上。


问题答案:

ed子进程时,Linux使用写时复制os.fork。展示:

import multiprocessing as mp
import numpy as np
import logging
import os

logger = mp.log_to_stderr(logging.WARNING)

def free_memory():
    total = 0
    with open('/proc/meminfo', 'r') as f:
        for line in f:
            line = line.strip()
            if any(line.startswith(field) for field in ('MemFree', 'Buffers', 'Cached')):
                field, amount, unit = line.split()
                amount = int(amount)
                if unit != 'kB':
                    raise ValueError(
                        'Unknown unit {u!r} in /proc/meminfo'.format(u = unit))
                total += amount
    return total

def worker(i):
    x = data[i,:].sum()    # Exercise access to data
    logger.warn('Free memory: {m}'.format(m = free_memory()))

def main():
    procs = [mp.Process(target = worker, args = (i, )) for i in range(4)]
    for proc in procs:
        proc.start()
    for proc in procs:
        proc.join()

logger.warn('Initial free: {m}'.format(m = free_memory()))
N = 15000
data = np.ones((N,N))
logger.warn('After allocating data: {m}'.format(m = free_memory()))

if __name__ == '__main__':
    main()

产生了

[WARNING/MainProcess] Initial free: 2522340
[WARNING/MainProcess] After allocating data: 763248
[WARNING/Process-1] Free memory: 760852
[WARNING/Process-2] Free memory: 757652
[WARNING/Process-3] Free memory: 757264
[WARNING/Process-4] Free memory: 756760

这表明最初大约有2.5GB的可用内存。分配15000x15000的数组后float64,有763248 KB的可用空间。因为15000 ** 2 * 8个字节= 1.8GB,而内存的下降2.5GB-0.763248GB也大约是1.8GB,所以这大概是有道理的。

现在,在生成每个进程之后,再次报告可用内存为〜750MB。可用内存没有明显减少,因此我得出结论,系统必须使用写时复制。

结论:如果不需要修改数据,则在__main__模块的全局级别定义数据是一种方便的(至少在Linux上)内存友好的方式,可以在子进程之间共享数据。



 类似资料:
  • e1文本转换器 @重写公共无效后文本更改(可编辑){ et2文本转换器 et3 textchanger@Override public void afterTextChanged(可编辑的s){ 不计算编辑文本的值 即使尝试在et1和et2中使用文本观察程序执行计算,应用程序也会崩溃

  • 我有一张布尔人的名单: 我正在寻找一种方法来计算列表中的数量(因此在上面的示例中,我希望返回值为)我已经找到了查找特定元素出现次数的示例,但是有没有更有效的方法,因为我正在使用布尔值?我在想一些类似于或的东西。

  • 我试图访问MyModelClass上的getter方法,但我的代码返回

  • 问题内容: 在这里看到讨论之后:Python-产生时差,我很好奇。我最初还以为生成器比列表快,但是当涉及sorted()时我不知道。将生成器表达式发送到sorted()而不是列表有什么好处吗?生成器表达式最终是否仍要在sorted()内放入列表中? 编辑:我只能接受一个答案让我感到悲伤,因为我感到很多答复都有助于澄清这个问题。再次感谢大家。 问题答案: 首先要做的是将数据转换为列表。基本上,实现的

  • 我发现不推荐使用,所以我想用另一种方法获取图像的真实路径。 这里是我获得图像的uri和标题的新方法,但我不知道如何获得真实的路径。 打印uri、标题、路径: Photouri:内容://Media/External/Images/Media/1605 标题:IMG_20191223143418 但我只想要这样:/storage/emulated/0/dcim/1733/img_2019122314

  • 我有以下列表: 我需要计算累计总和-列表含义 我想使用Java流API进行计算,以便我可以使用Spark实现它以进行大数据计算。我在JavaStreams中很天真,我尝试了几个表达式,但没有一个是有效的,等效的结构代码应该是这样的: