Рубрики
Без рубрики

SA Sociedade Tecnológica.

Понимание и оптимизация Многопроизводства Python Multi-Process Memory Этот пост будет фокусироваться о … Помечено с MediaManagement, Python, Numpy, Performance.

Понимание и оптимизация многопроцессной памяти Python Multi-Process

Этот пост будет сосредоточен на снижении использования памяти и увеличить свой IPC одновременно

Этот пост блога будет сосредоточен на Posix Ориентированные ОС, как Linux или Macos

Чтобы избежать узкого места GIL, вы, возможно, уже использовали многопроводу с Python, будь то использование рабочей модели предварительной формы ( больше на что здесь ) или просто используя Многопроцессор упаковка.

Что это делает, под капотом, использует ОС вилка () Функция, которая создаст дочерний процесс с точной виртуальной копией памяти родителей. ОС действительно умна при этом, поскольку оно не копирует память сразу. Вместо этого он будет раскрыть его каждому процессу в качестве собственной изолированной памяти, сохраняя все предыдущие адреса Intact.

Новый процесс, созданный из FORK (), сохраняет то же адреса памяти

Это возможно благодаря концепции Виртуальная память Отказ

Давайте сделаем небольшой объем, просто чтобы обновить свою память на некоторых основных концепциях, не стесняйтесь пропустить этот раздел, если это для вас старые новости.

Итак, как у вас могут иметь два процесса с той же адресами памяти, удерживающие разные значения?

Ваш процесс не взаимодействует непосредственно с вашим компьютером RAM, на самом деле, в памяти ОС резинки через механизм под названием виртуальная память. Это имеет много преимуществ, таких как:

  • Вы можете использовать больше памяти, чем доступной ОЗУ в вашей системе (с помощью диска)
  • Выделение и защита адреса памяти из других процессов
  • Непрерывное адресное пространство
  • Не нужно управлять общей памятью напрямую

Виртуальная память против физической памяти

На приведенной выше картинке вы можете увидеть два независимых процесса, которые имеют их изолированное пространство памяти. Каждый процесс имеет свое смежное адресное пространство и не нужно управлять там, где каждый Страница расположен.

Вы, вероятно, заметили, что некоторые страницы памяти находятся на диске. Это может произойти, если ваш процесс никогда не должен был получить доступ к этой странице, поскольку она началась (ОС будет загружать только страницы в оперативную память, когда их нуждается в процессе), или они были выселены из RAM, потому что это было необходимо для других процессов.

Когда процесс пытается получить доступ к странице, ОС будет подавать его непосредственно из оперативной памяти, если она уже загружена или принесет ее от диска, загрузите его в RAM, а затем обслуживать его к процессу, с тем, что единственное различие, являющееся задержкой.

Извините за объезд, теперь давайте вернемся к нашей главной теме!

После того, как вы видели (), вы в конечном итоге с двумя процессами, ребенком и родителем, которые разделяют большую часть своей памяти, пока один из них не должен писать на любую общую страницу общих памяти.

Этот подход называется копией на запись ( коровы ), и это позволяет избежать, чтобы ОС дублировала всю память процесса с самого начала, что экономит память и ускорение создания процесса.

Корова работает, отмечая эти страницы памяти как только для чтения и подсчета количества ссылок на страницу. Когда данные записываются на эти страницы, ядро перехватывает попытку записи и выделяет новую физическую страницу, инициализированную с помощью данных копирования в записи. Затем ядро обновляет таблицу страницы с новой (записью записи) страницы, уменьшает количество ссылок и выполняет запись.

Что это значит, что один простой способ избежать вздутий вашей памяти – убедиться, что вы загружаете все, что вы намерены делиться между процессами в память, прежде чем вилка ().

Если вы используете Gunicorn, чтобы обслуживать вашу API, это означает использование Предовольствие Параметр например. Не только вы можете избежать дублирования памяти, но он также не будет избежать дорогостоящих IPC Отказ

Загрузка Общих объектов только для чтения перед вилкой () отлично работает для «хорошо поведения» языков, поскольку эти страницы объекта никогда не будут скопированы, к сожалению, с Python, это не так.

Один из стратегий GC Python является Отсчет ссылки И Python отслеживает ссылки в каждом заголовке объекта. Что это значит на практике – это то, что каждый раз, когда вы читаете указанный объект, вы напишите ему.

Будь то используя Gunicorn с параметром предварительного загрузки или просто загрузка ваших данных, а затем высказывание с помощью многопроцессорного пакета, вы заметите, что, после количества времени, используя ваше использование памяти в возрасте до 1: 1 с количеством процессов. Это работа ГХ.

У меня есть хорошие и плохие новости … Ты не один В этом и будет немного больше проблем, но есть некоторые обходные пути к проблеме.

Давайте установим исходную линию и сначала запускаю некоторые тесты, а затем исследовать наши варианты. Чтобы запустить эти ориентиры, я создал маленький сервер колба с Gunicorn для вилки процесса на 3 рабочих. Вы можете проверить скрипт здесь Отказ

Использование памяти умножает с количеством работников

В вышеприведенном графике Gunicorn будет вилять перед запуском сервера, это означает, что каждый работник будет запустить этот скрипт:

self.big_data = [item _for_ item _in_ range(10000000)]

Как мы видим, использование памяти вырастает линейно с каждым работником.

Использование памяти не меняется с количеством работников

На вышеуказанном графике, поскольку я использую вариант предварительной нагрузки, Gunicorn загрузит все, прежде чем вытеснять. Мы можем видеть корову в действии здесь, так как использование памяти остается постоянной.

Память краски, как только рабочие петли через их копию общего списка

К сожалению, как мы видим здесь, как только каждый работник должен будет прочитать общие данные, GC попытается написать на эту страницу, чтобы сохранить ссылочный счетчик, провоцируя копию на запись. В конце концов, мы в конечном итоге с тем же использованием памяти, как если бы мы не использовали опцию Preload!

Хорошо, у нас есть базовый уровень, как мы можем улучшить?

Используя Джобликс

Очень маленькая разница в использовании памяти после доступа

Джобликс Это библиотека Python, которая в основном используется для сериализации данных и параллельной работы. Одна действительно хорошая вещь в этом заключается в том, что оно позволяет легко сохранить память, поскольку она не будет коровы при доступе к данным, загруженным этим пакетом.

_import_ joblib
# previously created with joblib.dump()
self.big_data = joblib.load('test.pkl') # big_data is a big list()

Используя numpy

Очень маленькая разница в использовании памяти после доступа

Если вы делаете науку Data, у меня действительно хорошие новости для вас! Вы получаете экономию памяти для «Free», просто используя Numpy Data Structures. И это включает в себя, если вы используете Pandas или другую библиотеку, если внутренняя структура данных – это Numpy Array.

Причина этого – это то, как они управляют памятью. Поскольку этот пакет в основном C с привязками Python, они имеют свободу (и ответственность) управлять всем без помех CPYthon. Они сделали умный выбор не сохраняют ссылочные значения на одних и тех же страницах тех больших структур данных сохраняются, избегая коровы при доступе к ними.

_import_ numpy _as_ np
self.big_data = np.array([[item, item] _for_ item _in_ range(10000000)])

Использование MMAP.

Ноль накладных расходов в использовании памяти

mmap это система Unix, соответствует POSIX, которая отображает файлы или устройства в память. Это позволяет вам взаимодействовать с огромными файлами, которые существуют на диске без необходимости загружать их в память в целом.

Еще одним большим преимуществом является то, что вы даже можете создать блок общей «неуправляемой» памяти без передачи файла -1 вместо пути файла, как это:

_import_ mmap
mmap.mmap(-1, length=....)

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

Как это все звучат? Есть что-то, что вы хотели бы, чтобы я расширился? Дайте мне знать ваши мысли в разделе комментариев ниже (и ударить хлопок, если это было полезно)!

Выводы

  • Python, как правило, копирует общие данные для каждого процесса при доступе к нему
  • ” Preload “- отличный способ сохранить память, если вам нужно поделиться большим структурой данных только для чтения в вашей API
  • Чтобы избежать коровы, когда вы читаете данные, вам нужно использовать Joblib, numpy, mmap, shared_memory или аналогичный
  • Содействие данным вместо общения данных между процессами может сэкономить вам много задержек

Оставайтесь настроиться на следующий пост. Следуйте, так что вы не пропустите его!

Оригинал: “https://dev.to/lsena/understanding-and-optimizing-python-multi-process-memory-management-4ech”