Python多进程下只读共享数据重复拷贝导致运行变慢如何优化
核心问题原因
性能问题主要来自两方面:
ProcessPoolExecutor提交任务时,会将传入的函数、参数全部做pickle序列化传递给子进程,直接传入实例方法sp.solve_会触发整个SubProblem实例的序列化,若实例内公共数据体积大,6次提交会产生6次重复序列化+拷贝开销,序列化过程内部的锁竞争就是观察到的_thread.Lock.acquire()高耗时的来源- 公共只读数据
dic_每次提交任务都重复传递,也会产生重复序列化开销,没有利用只读数据可共享的特性
优化方案
方案1:利用进程池初始化预加载公共数据(兼容性最好)
通过ProcessPoolExecutor的initializer参数,在每个子进程启动时一次性加载所有只读公共数据,子进程后续执行任务只需要传入独有参数即可,彻底避免公共数据重复序列化拷贝。
修改后的代码示例:
import concurrent.futures import multiprocessing # 全局变量存子进程的公共只读数据,仅在子进程内访问 _COMMON_DIC = None _SUB_PB_COMMON_DATA = None def executor_init(common_dic, sub_common_data): """进程池初始化函数,每个子进程启动时执行一次""" global _COMMON_DIC, _SUB_PB_COMMON_DATA _COMMON_DIC = common_dic _SUB_PB_COMMON_DATA = sub_common_data def sub_solve_wrapper(sub_unique_data, k): """顶级函数作为任务入口,避免序列化实例方法""" # 直接读取预加载的公共数据,无需传参 sp = SubProblem(_SUB_PB_COMMON_DATA, sub_unique_data) return sp.solve_(_COMMON_DIC, k) class Problem(): def __init__(self, data): self.data = data # 拆分SubProblem的公共数据和每个实例的独有数据 self.sub_common_data = self.get_sub_common_data(data) # 只存每个子问题的独有数据,不要存整个SubProblem实例 self.sub_unique_datas = {i: self.get_sub_unique_data(data, i) for i in range(6)} def run(self): dic_ = self.do_some_analysis() with concurrent.futures.ProcessPoolExecutor( max_workers=6, # 固定6个任务直接设6个worker,避免进程调度开销 initializer=executor_init, initargs=(dic_, self.sub_common_data) ) as executor: results = [ executor.submit(sub_solve_wrapper, unique_data, k) for k, unique_data in self.sub_unique_datas.items() ] return [f.result() for f in results]
同时改造SubProblem类,把公共数据和独有数据分开传入,避免公共数据在每个实例里重复存储:
class SubProblem: def __init__(self, common_data, unique_data): self.common_data = common_data self.unique_data = unique_data def solve_(self, dic_, k): solutions = [] # 原有运算逻辑不变 return solutions
方案2:使用写时复制特性(仅Linux/macOS可用)
Linux/macOS默认使用fork模式创建子进程,父进程的所有只读数据在子进程里是共享的,只有修改时才会发生拷贝。可以把所有公共只读数据在父进程创建进程池之前就赋值为全局变量,子进程直接读取即可,完全不需要传参。
显式设置进程启动方式代码:
if __name__ == "__main__": multiprocessing.set_start_method("fork", force=True) # 后续启动进程池的逻辑
方案3:共享内存(超大公共数据场景适用)
如果公共数据体积超过GB级,可以使用multiprocessing.SharedMemory或者multiprocessing.Array等共享内存结构存储数据,所有进程直接访问同一块内存,完全没有拷贝开销。
内容的提问来源于stack exchange,提问作者sos
相关产品推荐
相关产品推荐

