Python multiprocessing无法运行结果排查

(1). 先排除主进程无法运行的情况
p = Pool(cpu_core)
parallel_warp = partial(download_oneimg, 
                    id2file=pkl_dic,
                    save_root=save_root)

while line_idx < npy_array_len:
    ann_count = npy_array[line_idx][12]
    img_lines = npy_array[line_idx : line_idx+ann_count].tolist() # array 多进程不能用
    
    # 不要直接传字典,要传元素
    img_id = img_lines[0][1]
    img_ceph_path = pkl_dic[img_id]
    
    # 多进程
    p.apply_async(parallel_warp, args=(img_lines, img_ceph_path)
    # 单步调试
    # parallel_warp( img_lines )
    line_idx += ann_count

我一般习惯用 Pool.apply_async(并行函数, args=(并行函数参数们)) 来进行并行化操作

所以可以很方便的进行串行程序的调试,直接改成 并行函数(并行函数参数们) 即可

(2). 并行进程数太多导致无法并行的情况
from multiprocessing import Pool
p = Pool(cpu_core).  # 尝试将 cpu_core 改成一个较小的值,比如2

我之前并行32进程不行,但是改成16却可以,当然也有可能是各个进程没有准备好的问题,比如将各变量序列化反序列化后拷贝的自己的进程

在这里插入图片描述

(3). 是否存在某些难以序列化的数据

假如我们的主进程有一个超级大的 ndarray,如果我们的 Pool 并行进程数是4,则会复制4份同样的 array 到相应的子进程,进程间的数据传输需要做对应数据的 pickle 和 unpickle 操作,同时这个数据也变成了原来的4倍,太占用哥的内存了

如果你暂时无法将各个进程的数据解耦合,只能用相同数据的话,可以用一下Python3.8之后的 multiprocessing.shared_memory ,如果版本小于 3.8 可以参考这位大佬的文章:
Python 多进程共享内存、NumPy 数组
本小节大部分都是摘自此文章,给大佬点赞!!
https://www.7forz.com/3408/

a. Value 与 Array

在 multiprocessing 包中,提供了一些可共享的对象:Value、Array、RawValue 与 RawArray。基本上,前者没有 Raw 的,可以加锁以进行进程间同步,后面 Raw 的没有锁。项目中用到的 numpy 数组都是只读的,子进程只需要读不需要写,所以选择使用 RawArray。


下面的代码会使用一个 numpy array 创建一个 RawArray,然后把它转回 numpy array:

import multiprocessing
import numpy as np
arr = np.zeros(5)
arr_shared = multiprocessing.RawArray('d', arr) # 'd' for double, which is float64
arr_new = np.frombuffer(arr_shared, dtype=np.double)
print(arr_new) # 返回 [0. 0. 0. 0. 0.]

如果 array 是多维的,直接用上面的代码会报错,因为 RawArray 只支持一维。可以这样解决:

import multiprocessing
import numpy as np
SHAPE = (2, 3)
arr = np.zeros(SHAPE)
arr_shared = multiprocessing.RawArray('d', arr.ravel())
arr_new = np.frombuffer(arr_shared, dtype=np.double).reshape(SHAPE)
print(arr_new)
# [[0. 0. 0.]
#  [0. 0. 0.]]
b. 传给进程池

思路很清晰:在主进程生成 array,转成 RawArray,再传给 Pool。


然而,直接把 RawArray 对象作为参数是会报错的(RuntimeError: c_double_Array_x objects should only be shared between processes through inheritance)。


在网上找到了答案:通过 pool 的 initializer 实现子进程的初始化。这在官方文档里面只有轻描淡写的一句😂。


具体来说,在创建进程池时,需要传入 initializer 函数与 initargs 参数。
initargs 包含了 RawArray 对象,也可以把它的 shape 也传进去(我下面的参考代码懒就不传了)。
initializer 函数会在子进程创建时被调用,并且把 RawArray 对象变为该子进程的全局变量。


initializer 函数及其对应的变量共享,可以用全局变量或全局的字典来实现:

global_arr_shared = None
def init_pool(arr_shared):
    global global_arr_shared
    global_arr_shared = arr_shared

而进程池是这样创建的:

with multiprocessing.Pool(processes=2, 
                          initializer=init_pool, 
                          initargs=(arr_shared,)) as pool:

Pool 的 worker 函数中,把 RawArray 转回 numpy array之后,就可以当作普通的 ndarray 操作了。如果修改了数组的内容,也会反映到原数组中,只是需要注意锁的问题。下面是一个很简单的例子。

def worker(i):
    arr = np.frombuffer(global_arr_shared, np.double).reshape(SHAPE)
    time.sleep(1)  # some other operations
    return np.sum(arr * i)

总体的程序如下,可以直接运行:

import multiprocessing 
import time
import numpy as np 
SHAPE = (2, 3)
global_arr_shared = None

def init_pool(arr_shared):
    global global_arr_shared
    global_arr_shared = arr_shared
    
def worker(i):
    arr = np.frombuffer(global_arr_shared, np.double).reshape(SHAPE)
    time.sleep(1)  # some other operations
    return np.sum(arr * i)
    
if __name__ == '__main__':
    arr = np.random.randn(*SHAPE)
    arr_shared = multiprocessing.RawArray('d', arr.ravel())
    with multiprocessing.Pool(processes=2, 
                              initializer=init_pool, 
                              initargs=(arr_shared,)) as pool:  # initargs传入tuple
        for result in pool.map(worker, [1,2,3]):
            print(result)

总体来说,就是要先变成 RawArray,然后给 Pool 加上初始化函数以传递 RawArray 给子进程,最后用的时候把 RawArray 转回 numpy array。还是有点麻烦的。
初步测试,性能基本没有受到影响,肯定比 multiprocessing.Manager 快。

值得第一提的是,还可以通过这样来获取结果:

for result in pool.map(worker, [1,2,3]):
	print(result)
(4). 将难以序列化的数据变成Python的类型

算是(3)的一个拓展,比如(1)中的程序:

img_lines = npy_array[line_idx : line_idx+ann_count]

替换为:

img_lines = npy_array[line_idx : line_idx+ann_count].tolist() # array 多进程不能用
(5). 如果运行过程中出现bug,如何kill

一般运行多进程的程序,我一般会挂在后台:

nohup python download.py &

但是要是这样,子进程报错了,怎么kill呢?
linux批量结束进程指南:

ps -ef | grep "python download.py" | grep -v grep | awk '{print $2}' | xargs kill -9

参考自:
https://unix.stackexchange.com/questions/368639/passing-a-9-to-xargs-kill-command

我就说一下 awk '{print $2}' 这个怎么用:
举个例子:

$ ls -al

在这里插入图片描述
我想要第2列数数字怎么搞?
就可以这样:

ls -al | awk '{print $2}'

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值