我正在尝试并行化for loop
以加快我的代码速度,因为循环处理操作都是独立的。在在线教程之后,Python 中的标准multiprocessing
库似乎是一个良好的开端,我已经将其用于基本示例。
但是,对于我的实际用例,我发现并行处理(使用双核机器(在Windows上运行时实际上慢了一点(<5%(。然而,与串行执行相比,在 Linux 上运行相同的代码会导致 ~25% 的并行处理速度。
从文档中,我相信这可能与 Window 缺少 fork(( 函数有关,这意味着该过程每次都需要重新初始化。但是,我不完全理解这一点,想知道是否有人可以确认这一点?
特别
--> 这是否意味着调用 python 文件中的所有代码都会针对 Windows 上的每个并行进程运行,甚至初始化类和导入包?
--> 如果是这样,可以通过以某种方式将类的副本(例如使用deepcopy(传递到新进程中来避免这种情况吗?
--> 是否有任何技巧/其他策略可以有效地并行化Unix和Windows的代码设计。
我的确切代码很长并且使用许多文件,因此我创建了一个 pseucode 样式的示例结构,希望能显示问题。
# Imports
from my_package import MyClass
imports many other packages / functions
# Initialization (instantiate class and call slow functions that get it ready for processing)
my_class = Class()
my_class.set_up(input1=1, input2=2)
# Define main processing function to be used in loop
def calculation(_input_data):
# Perform some functions on _input_data
......
# Call method of instantiate class to act on data
return my_class.class_func(_input_data)
input_data = np.linspace(0, 1, 50)
output_data = np.zeros_like(input_data)
# For Loop (SERIAL implementation)
for i, x in enumerate(input_data):
output_data[i] = calculation(x)
# PARALLEL implementation (this doesn't work well!)
with multiprocessing.Pool(processes=4) as pool:
results = pool.map_async(calculation, input_data)
results.wait()
output_data = results.get()
编辑:我不认为这个问题是建议的问题的重复,因为这与Windows和Linunx的差异有关,这在建议的重复问题中根本没有提到。
NT操作系统缺少UNIXfork
原语。创建新进程时,它将作为空白进程启动。父级有责任指导新进程如何引导。
Pythonmultiprocessing
API抽象了进程创建,试图为fork
、forkserver
和spawn
启动方法提供相同的感觉。
当您使用spawn
启动方法时,这就是引擎盖下发生的事情。
- 创建空白流程
- 空白进程启动一个全新的 Python 解释器
- Python 解释器获得通过
Process
类初始值设定项指定的 MFA(模块函数参数( - Python 解释器加载给定的模块解析所有导入
target
函数在模块中查找,并使用给定的args
和kwargs
调用
上面的流程几乎没有带来什么影响。
正如您自己所注意到的,与fork
相比,这是一项更费力的操作。这就是为什么您会注意到性能的这种差异。
当模块在子进程中从头开始导入时,所有导入副作用都会重新执行。这意味着常量、全局变量、装饰器和第一级指令将再次执行。
另一方面,在父进程执行期间进行的初始化不会传播到子进程。请参阅此示例。
这就是为什么他们在multiprocessing
文档中为编程指南中的Windows添加了特定段落的原因。我强烈建议阅读编程指南,因为它们已经包含了编写可移植多处理代码所需的所有信息。