Использовать системный вызов fork, чтобы избежать чтения / записи или сериализации вообще? - PullRequest
0 голосов
/ 23 февраля 2020

Я использую ma c book, и поэтому многопроцессорная обработка будет использовать системный вызов fork вместо порождения нового процесса. Также я использую Python (с многопроцессорной обработкой или Dask).

У меня очень большой pandas фрейм данных. Мне нужно, чтобы много параллельных подпроцессов работали с частью этого одного большого кадра данных. Допустим, у меня есть 100 разделов этой таблицы, над которыми нужно работать параллельно. Я хочу избежать необходимости делать 100 копий этого большого фрейма данных, так как это переполнит память. Поэтому текущий подход, который я использую, состоит в том, чтобы разбить его на разделы, сохранить каждый раздел на диске и заставить каждый процесс прочитать их, чтобы обработать часть, за которую отвечает каждый из них. Но это чтение / запись очень дорого для меня, и я хотел бы избежать этого.

Но если я сделаю одну глобальную переменную этого фрейма данных, то из-за поведения 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 one_big_df?

Одна дополнительная, более общая Вопрос в том, почему людям приходится иметь дело с сериализацией Pickle и / или чтением / записью на диск для передачи данных между несколькими процессами, когда (при условии, что люди используют UNIX), установка данных в качестве глобальной переменной фактически сделает их доступными вообще. дочерние процессы так легко? Есть ли опасность в использовании 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)

1 Ответ

0 голосов
/ 23 февраля 2020

Суммируя комментарии, в системе разветвления, такой как Ma c или Linux, дочерний процесс имеет представление копирования-при-записи (COW) родительского адресного пространства, включая любые DataFrame s, которые он может держать. Безопасно использовать и изменять фрейм данных в дочерних процессах без изменения данных в родительском или других дочерних процессах.

Это означает, что нет необходимости сериализовать фрейм данных, чтобы передать его дочернему процессу. Все, что вам нужно, это ссылка на фрейм данных. Для Process вы можете просто передать ссылку непосредственно

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

Если вы используете Queue или другой инструмент, такой как Pool, тогда данные, скорее всего, будут сериализованы. Вы можете использовать глобальную переменную, известную работнику, но фактически не переданную работнику, чтобы обойти эту проблему.

Остаются только возвращаемые данные. Он только для потомка и все еще должен быть сериализован, чтобы быть возвращенным родителю.

...