多进程间共享mainTIFFImageDatasetContents,避免重复序列化开销
解决Python多进程栅格处理中大数据集重复pickle的问题
针对多进程传递栅格大数据集导致pickle开销过高的问题,这里有几个实用的优化方案:
方案1:进程池初始化时注入全局变量(兼容所有系统)
通过进程池的initializer和initargs参数,把大数据集一次性传入每个子进程并初始化为全局变量,后续任务只传递处理所需的小参数(比如栅格单元格索引、范围),无需携带整个数据集。
示例代码:
import multiprocessing # 子进程全局变量,用于存储栅格数据集 _global_tiff_dataset = None def init_worker(dataset): """进程池初始化函数,将数据集存入子进程全局空间""" global _global_tiff_dataset _global_tiff_dataset = dataset class GridCellInnerLoopsIteratorsForNoneZeroCoverageModel: def __init__(self, cell_params): # 仅保存处理单元格所需的小参数,不再持有大数据集 self.cell_params = cell_params def process(self): # 直接调用子进程全局的栅格数据集 dataset = _global_tiff_dataset # 此处编写具体栅格处理逻辑,比如根据cell_params提取对应单元格数据 ... class DecoupleGridCellsProfilerLoopsPool: def run(self, main_tiff_dataset, cell_params_list): # 创建进程池时传入初始化函数与数据集 with multiprocessing.Pool(initializer=init_worker, initargs=(main_tiff_dataset,)) as pool: # 生成仅包含小参数的任务对象列表 tasks = [GridCellInnerLoopsIteratorsForNoneZeroCoverageModel(params) for params in cell_params_list] # 执行多进程任务 results = pool.map(lambda task: task.process(), tasks) return results
方案2:Unix系统下利用fork写时复制(性能最优)
如果代码运行在Linux/macOS这类支持fork的系统上,multiprocessing默认用fork启动子进程,父进程的全局变量会以写时复制的方式被子进程继承,无需额外序列化。直接将数据集设为模块级全局变量即可:
import multiprocessing # 模块级全局变量,父进程中初始化 _main_tiff_dataset = None class GridCellInnerLoopsIteratorsForNoneZeroCoverageModel: def __init__(self, cell_params): self.cell_params = cell_params def process(self): # 直接调用全局数据集 dataset = _main_tiff_dataset # 处理逻辑 ... class DecoupleGridCellsProfilerLoopsPool: def run(self, dataset, cell_params_list): global _main_tiff_dataset _main_tiff_dataset = dataset # 创建进程池,子进程自动继承全局变量 with multiprocessing.Pool() as pool: tasks = [GridCellInnerLoopsIteratorsForNoneZeroCoverageModel(params) for params in cell_params_list] results = pool.map(lambda task: task.process(), tasks) return results
注意:该方案仅适用于fork启动模式,Windows系统默认用spawn模式,不会继承父进程全局变量,此时需使用方案1。
方案3:重构类结构,分离数据集与任务参数
彻底拆分类职责,让GridCellInnerLoopsIteratorsForNoneZeroCoverageModel仅封装任务逻辑和小参数,数据集通过进程池初始化注入,避免对象持有大数据集:
import multiprocessing _global_tiff_dataset = None def init_worker(dataset): global _global_tiff_dataset _global_tiff_dataset = dataset def process_cell(cell_params): """独立处理函数,直接调用全局数据集""" dataset = _global_tiff_dataset # 处理逻辑 ... class DecoupleGridCellsProfilerLoopsPool: def run(self, main_tiff_dataset, cell_params_list): with multiprocessing.Pool(initializer=init_worker, initargs=(main_tiff_dataset,)) as pool: # 直接传递小参数,无需创建带数据集的对象 results = pool.map(process_cell, cell_params_list) return results
核心原理
Python多进程传递对象时默认用pickle序列化,大数据集反复序列化会产生极高的IO和CPU开销。通过将数据集放入子进程全局空间(要么通过进程池初始化,要么通过fork继承),可让所有子进程共享同一份数据集(fork下为写时复制,内存利用率更高;初始化方式则仅序列化一次),彻底避免重复序列化的损耗。
内容的提问来源于stack exchange,提问作者Amrmsmb
相关产品推荐
相关产品推荐

