Влияет ли нарезка кадров данных в многопроцессорной / многопроцессорной среде на производительность? - PullRequest
1 голос
/ 22 апреля 2020

Мне нужно сделать несколько вычислений на разных срезах больших фреймов данных.

Предположим, у меня есть 3 больших фрейма данных df1, df2 и df3.
Каждый из которых имеет "Date" столбец.
Мне нужно выполнить некоторые вычисления на этих фреймах данных, основываясь на срезах даты, и, поскольку каждая итерация не зависит от другой итерации, мне нужно выполнять эти итерации одновременно.

df1              # a big dataframe
df2              # a big dataframe
df3              # a big dataframe

enter image description here

Итак, я определяю желаемую функцию, и в каждом дочернем процессе сначала создается срез df1, df2, df3, внутри процесса, затем другие вычисления go on.

Поскольку df1, df2 и df3 являются глобальными фреймами данных, мне нужно указать их в качестве аргументов в моей функции. В противном случае он не будет распознан.

Что-то вроде ниже:

slices = [ '2020-04-11', '2020-04-12', '2020-04-13', ]
# a list of dates to get sliced further

def my_func(slice,df1=df1,df2=df2,df3=df3):
    sliced_df1 = df1[df1.Date >  slice]
    sliced_df2 = df2[df2.Date <  slice]
    sliced_df3 = df3[df3.Date >= slice]
    #
    # other computations
    # ...
    #
    return desired_df

Параллельная обработка настроена следующим образом:

import psutil
pool = multiprocess.Pool(psutil.cpu_count(logical=False))

final_df = pool.map(my_func,[slice for slice in slices])
pool.close()
final_df = pd.concat(final_df, ignore_index = True)

Однако, похоже, что только одно ядро ​​идет вверх при исполнении.

Я полагаю, что поскольку каждый дочерний процесс хочет получить доступ к глобальным фреймам данных df1, df2 и df3, должна быть общая память для дочернего процесса, и при поиске по net я думаю, мне нужно использовать multiprocessing.manager(), но я не уверен, как его использовать, или я прав в его использовании?

Я на самом деле новичок в концепции параллельной обработки, и я ценю, если кто-то может help.

PS: Похоже, мой вопрос похож на этот пост . Тем не менее, он не имеет принятого ответа.

1 Ответ

1 голос
/ 22 апреля 2020

Q : "... я (I) прав насчет , используя его? "


A) multiprocess != multiprocessing

Ну, вы, возможно, уже заметили, что multiprocessing -модуль не то же самое, что и multiprocess -модуль Майка МакКернса. (Обещания и свойства последних остаются в наши дни скрытыми, так как сопровождающие модуля опубликовали документацию модуля на RTFM сайте (да, ReadTheDocs первоначально использовал Read-The- F ***** g-Manual идентификатор сайта, так что я не представляю) , который в настоящее время недоступен, так как перевод адреса на https://multiprocess.readthedocs.io/en/latest/ вообще не отображает контент, относящийся к модулю multiprocess (было бы замечательно, если бы @Mike McKerns вмешался и исправил бы этот дефицит, так как иначе Pathos, Dill и другие части его опубликованной работы известны своей невероятно великие части программного обеспечения, не так ли?).


B) Вы платите намного больше, чем когда-либо получите обратно

Если вы пытаетесь избежать дублирования DataFrame -инстанций, мотивированный потолком оперативной памяти, вы не можете использовать многопроцессорность на основе процессов. Это приводит к ловушке наличия, но pure- [SERIAL], центральной диктатуры GIL-замка, предписывающей один за другим один шаг за шагом (без увеличения производительности, но совсем наоборот ... оплачивая дополнительные расходы на мультиплексирование потоков, все ожидают как собственного удержания GIL-блокировки, так и свободного оперативного ввода-вывода, чтобы получить некоторые данные для выборки / жевания / хранения (процесс)).

Если вы пытаетесь войти в процессы, порожденные linux (несущими риск при этом - посеять документы / вопросы по деталям) и использовать Manager() - экземпляр (эй, да, снова GIL-блокировка блокирует только одно ядро ​​процессора - мы уже слышали об этом, не так ли?) или другие подобные синтаксические приемы для доступа или организации доступа к global -s или "общие" объекты из основного, опять же, затраты на это будут go, что дико против вашей желаемой производительности, что (за исключением использования маскировки задержки ввода-вывода в сети- случаи) никогда не будет ощутимого преимущества, часто совсем наоборот - вы заплатите намного больше с точки зрения дополнительных накладных расходов, чем когда-либо будете получать обратно с точки зрения желаемого увеличения производительности.

Если передать все DataFrame -экземпляры через Передача параметров (как пытается сделать ваш синтаксис), ваши процессы (будь то разветвленные или порожденные) будут платить еще больше дополнительных накладных расходов (и все будут иметь полномасштабные копии оригинальных ( df1, df2, df3, ) ... да , RAM докажет это) плюс ваши накладные расходы, связанные с многопроцессорностью, будут включать огромные и разрушительные затраты SER / DES , так как эти куски мяса BIG-FAT должны будут go через SER / DES (чаще всего реализуется через SER-фазу в pickle.dumps() + XFER-фазе через p2p-передачу данных (поток данных) + DES-фазу в pickle.loads() ... все это выполнено 3 * len ( [ slice for slice in slices ] ) -процессов, имея предельное значение для конфликтов ОЗУ, имеющее дросселирование конфликтов ЦП с помощью центрального GIL-замка на стороне __main__, из-за необходимости сначала выполнить все три ( SER, XFER, ) - фазы на его стороне, если позволяет оперативная память, до того, как «удаленные» фазы -DES go начнут продвигаться вперед, то, скорее всего, уже начался тяжелый переброс подкачки.

Так что любой из них больше похож на anti-pattern для любых высокопроизводительных вычислений, не так ли?

Если бы не все эти проблемы, связанные с Python / GIL, наука о производительности идет против вас - см. ловушки speedup в пересмотренном Законе Амдала для подробностей и количественных методов.


В сомнениях?

Ориентир, бенчмарк, эталон.

Факты имеют значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...