利用 fork 系统调用来避免 read/writing 或完全序列化?

Taking advantage of fork system call to avoid read/writing or serializing altogether?

我正在使用 mac 书,因此,多处理将使用 fork 系统调用而不是生成新进程。此外,我正在使用 Python(使用多处理或 Dask)。

我有一个非常大的 pandas 数据框。我需要让许多并行子流程处理这个大数据框的一部分。假设我有 100 个需要并行处理的 table 分区。我想避免必须为这个大数据帧制作 100 个副本,因为这会占用内存。所以我目前采用的方法是对其进行分区,将每个分区保存到磁盘,然后让每个进程将它们读入以处理它们各自负责的部分。但是这个 read/write 对我来说非常昂贵,我想避免它。

但是如果我为这个数据帧创建一个全局变量,那么由于 COW 行为,每个进程都可以从这个数据帧中读取数据而无需创建它的实际物理副本(只要它不修改它) .现在我的问题是,如果我制作一个全局数据框并将其命名为:

global my_global_df
my_global_df = one_big_df

然后在我执行的子流程之一中:

a_portion_of_global_df_readonly = my_global_df.iloc[0:10]
a_portion_of_global_df_copied = a_portion_of_global_df_readonly.reset_index(drop=True)
# reset index will make a copy of the a_portion_of_global_df_readonly

do something with a_portion_of_global_df_copied

如果我执行上述操作,我会创建整个 my_global_df 的副本还是只创建 a_portion_of_global_df_readonly 的副本,从而避免制作 100 [=] 的副本16=]?

另一个更普遍的问题是,当(假设人们使用的是 UNIX)设置时,为什么人们必须处理 Pickle 序列化 and/or read/write 到磁盘以跨多个进程传输数据作为全局变量的数据将如此容易地有效地使其在所有子进程中可用?使用 COW 作为使任何数据可用于一般子流程的手段是否存在危险?

[来自以下线程的可重现代码]

from multiprocessing import Process, Pool
import contextlib
import pandas as pd

def my_function(elem):

    return id(elem)

num_proc = 4
num_iter = 10
df = pd.DataFrame(np.asarray([1]))
print(id(df))

with contextlib.closing(Pool(processes=num_proc)) as p:
    procs = [p.apply_async(my_function, args=(df, )) for elem in range(num_iter)]
    results = [proc.get() for proc in procs]
    p.close()
    p.join()

print(results)

总结评论,在 Mac 或 Linux 等分叉系统上,child 进程具有 [=31] 的 copy-on-write (COW) 视图=] 地址 space,包括它可能持有的任何 DataFrame。在 child 进程中使用和修改数据帧是安全的,而无需更改 parent 或其他兄弟 child 进程中的数据。

这意味着无需序列化数据帧即可将其传递给 child。您所需要的只是对数据框的引用。对于Process,直接传引用即可

p = multiprocessing.Process(target=worker_fctn, args=(my_dataframe,))
p.start()
p.join()

如果您使用 Queue 或其他工具,例如 Pool,那么数据可能会被序列化。您可以使用工作人员已知但实际未传递给工作人员的全局变量来解决该问题。

剩下的是 return 数据。它仅在 child 中,仍然需要序列化才能 returned 到 parent。