Python多进程反复处理大图片列表:避免重复拷贝列表的优化方案
问题场景
我有一个包含约20000张图片的大列表list_img,需要用参数列表params中的不同参数反复处理它。要求用多进程实现,且所有进程先完成arg1对图片分片的处理,再用arg2处理,依此类推。由于params中各参数的处理时间差异极大,如果按参数分配进程(比如核心1处理arg1、核心2处理arg2),会出现多数进程提前闲置、仅少数进程仍在运算的情况。
当前的可行实现代码如下:
from multiprocessing import Pool import numpy as np def calc_image(argument, image): val = argument * image # 仅为演示,非真实处理逻辑 return val if __name__ == "__main__": pool = Pool(processes=8) list_img = [np.ones((100, 100))] * 20000 # 演示用数据 params = list(range(100)) # 演示用参数列表 for par in params: par_list = [par] * len(list_img) return_vals = pool.starmap(calc_image, zip(par_list, list_img)) pool.close()
需要解决的问题:如何避免循环中每次更换参数par时都拷贝list_img?同时尽可能不使用全局变量。
方案1:利用进程池初始化器加载本地图片列表
通过Pool的initializer和initargs参数,在子进程启动时一次性将list_img加载到每个进程的本地内存,后续处理仅传递参数,无需重复拷贝图片列表。
修改后的代码:
from multiprocessing import Pool import numpy as np # 子进程本地存储的图片列表,仅在子进程内有效 local_imgs = None def init_worker(imgs): global local_imgs local_imgs = imgs def calc_image(argument): # 直接使用子进程本地的图片列表处理 return [argument * img for img in local_imgs] if __name__ == "__main__": list_img = [np.ones((100, 100))] * 20000 params = list(range(100)) # 初始化进程池时传入图片列表,每个子进程仅加载一次 with Pool(processes=8, initializer=init_worker, initargs=(list_img,)) as pool: results = pool.map(calc_image, params)
- 优势:仅在进程启动时完成一次图片列表拷贝,后续参数循环无额外拷贝开销;子进程内的全局变量不影响主进程,符合“尽可能不使用全局变量”的要求。
方案2:使用共享内存存储图片(适用于numpy数组场景)
如果图片以numpy数组形式存储,可通过multiprocessing.Array创建共享内存对象,让所有子进程共享同一块内存区域的图片数据,彻底避免拷贝。
示例代码:
from multiprocessing import Pool, Array import numpy as np def calc_image(argument, shape, dtype): # 从共享内存中恢复完整的图片数组 shared_arr = np.frombuffer(shared_imgs.get_obj(), dtype=dtype).reshape(shape) return argument * shared_arr if __name__ == "__main__": # 构造演示用的批量图片数组(实际对应20000张图片) img_shape = (20000, 100, 100) img_dtype = np.float64 list_img = np.ones(img_shape, dtype=img_dtype) # 创建共享内存数组,lock=False表示无需进程同步(只读场景) shared_imgs = Array('d', list_img.size, lock=False) # 将图片数据写入共享内存 np.frombuffer(shared_imgs.get_obj(), dtype=img_dtype)[:] = list_img.flatten() params = list(range(100)) with Pool(processes=8) as pool: # 仅传递参数、图片形状和数据类型,子进程从共享内存读取图片 results = pool.starmap(calc_image, [(par, img_shape, img_dtype) for par in params])
- 优势:完全无拷贝,内存利用率最高;适合大规模数值型图片数据处理。
方案3:用functools.partial绑定图片列表(轻量场景可选)
通过functools.partial将图片列表绑定到处理函数上,进程池初始化时完成一次拷贝,后续仅传递参数。
示例代码:
from multiprocessing import Pool import numpy as np from functools import partial def calc_image(argument, imgs): return [argument * img for img in imgs] if __name__ == "__main__": list_img = [np.ones((100, 100))] * 20000 params = list(range(100)) # 将图片列表绑定到处理函数,生成新的函数 bound_calc = partial(calc_image, imgs=list_img) with Pool(processes=8) as pool: results = pool.map(bound_calc, params)
- 优势:无需使用全局变量,代码简洁;但本质是进程启动时的一次性拷贝,内存占用与方案1相当,适合轻量场景。
内容的提问来源于stack exchange,提问作者RaJa
相关产品推荐
相关产品推荐

