仅使用 20 个内核中的 2 个的 Python 多处理程序

问题描述 投票:0回答:1

我是多重处理新手,所以这可能是一个愚蠢的问题。

我使用的是 Ubuntu 20.04.6 LTS(64 位),配备第 12 代 Intel(R) Core(TM) i7-12700K 处理器和 16GB RAM,运行 Python 3.9.19。当我运行以下代码时,

htop
命令显示所有 10 个内核均按预期使用:

import time

def fun(ele):
    ele ** 1000

if __name__ == '__main__':
    import multiprocessing

    input_list = [1000] * 10000000
    start_time = time.time()
    
    pool = multiprocessing.Pool(10)
    result = pool.map(func=fun, iterable=input_list)
    pool.close()
    pool.join()
    
    end_time = time.time()
    print(end_time - start_time)

但是,当我导入一些额外的库(如

PIL
numpy
等)时,即使创建了11个进程(1个主进程和10个子进程),似乎只有2个核心在工作。这是修改后的代码:

import time
from PIL import Image
import numpy as np
import pickle
import os
import torch

def fun(ele):
    ele ** 1000

if __name__ == '__main__':
    import multiprocessing

    input_list = [1000] * 1000000
    start_time = time.time()
    
    pool = multiprocessing.Pool(10)
    result = pool.map(func=fun, iterable=input_list)
    pool.close()
    pool.join()
    
    end_time = time.time()
    print(end_time - start_time)

使用命令

ps aux | grep [p]ython | grep $name_of_program$ | wc -l
,我确认确实创建了11个进程(1个主进程和10个子进程)。然而,这些进程似乎并未分布在所有 CPU 核心上。

spawn
将启动方式设置为
multiprocessing.set_start_method('spawn')
后,所有10个核心再次正常工作。

import time 

from PIL import Image

import numpy as np

import time
import numpy as np
from PIL import Image

import pickle
import os

import torch

def fun(ele): 
    ele**1000 

if __name__ == '__main__':
    import multiprocessing 
    multiprocessing.set_start_method('spawn')
    input_list = [1000]*1000000
    n = 250000 
    start_time = time.time() 
    pool = multiprocessing.Pool(10) 
    result = pool.map(func=fun, iterable=input_list) 
    pool.close()
    pool.join()
    end_time = time.time() 
    print(end_time-start_time)
  1. 为什么第二种情况只使用了 2 个核心?
  2. 此问题是否与我导入的额外库导致的内存限制有关?
  3. 在这种情况下,我可以采取什么步骤来确保进程均匀分布在CPU核心上(使用fork方法)?
python linux parallel-processing multiprocessing
1个回答
0
投票

为了测试您的计时,我建议您从可能使用多个 CPU 的最简单的代码开始。像这样的东西:

import time
import multiprocessing as mp

def fun(ele: int) -> int:
    return ele * ele

if __name__ == "__main__":
    start_time = time.time()
    with mp.Pool() as pool:
        pool.map_async(fun, range(250_000_000))
        pool.close()
        pool.join()
    duration = time.time() - start_time
    print(f"{duration=:.4f}s")

在 Apple M2 上,运行时间约为 35 秒 - 即有足够的时间运行 top 来查看发生了什么。

然后我会一一添加您的导入(即使您没有使用它们),看看哪些对您的观察影响最大。

注意:在 MacOS 上,“spawn”是默认值。从 start 方法的角度来看,您的观察很有趣,因为 fork (Linux 上的默认设置)通常被认为比 spawn 更快。所以你所看到的(对我来说)是反直觉的。

© www.soinside.com 2019 - 2024. All rights reserved.