Общая память в многопроцессорной обработке

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

l1=[bitarray 1, bitarray 2, ... ,bitarray n] l2=[array 1, array 2, ... , array n] l3=[array 1, array 2, ... , array n] 

Эти структуры данных занимают довольно много ОЗУ (~ 16 ГБ всего).

Если я запустил 12 подпроцессов, используя:

 multiprocessing.Process(target=someFunction, args=(l1,l2,l3)) 

Означает ли это, что l1, l2 и l3 будут скопированы для каждого подпроцесса или будут ли подпроцессы разделять эти списки? Или, чтобы быть более прямым, я буду использовать 16 ГБ или 192 ГБ ОЗУ?

someFunction будет считывать некоторые значения из этих списков и затем выполняет некоторые вычисления на основе прочитанных значений. Результаты будут возвращены родительскому процессу. Списки l1, l2 и l3 не будут изменены некоторой функцией.

Поэтому я бы предположил, что подпроцессы не нужны и не будут копировать эти огромные списки, но вместо этого просто передадут их родителям. Это означает, что в программе потребуется 16 ГБ оперативной памяти (независимо от того, сколько подпроцессов я запускаю) из-за подхода copy-on-write под linux? Правильно ли я, или мне не хватает чего-то, что приведет к копированию списков?

EDIT : Я все еще смущен, прочитав немного больше по этому вопросу. С одной стороны, Linux использует copy-on-write, что должно означать, что данные не копируются. С другой стороны, доступ к объекту изменит значение ref-count (я все еще не уверен, почему и что это значит). Тем не менее, будет ли скопирован весь объект?

Например, если я определяю someFunction следующим образом:

 def someFunction(list1, list2, list3): i=random.randint(0,99999) print list1[i], list2[i], list3[i] 

Будет ли использование этой функции означать, что l1, l2 и l3 будут полностью скопированы для каждого подпроцесса?

Есть ли способ проверить это?

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

Счетчик ссылок для l1, l2 и l3 фактически не используется в моей программе. Это связано с тем, что l1, l2 и l3 будут сохраняться в памяти (без изменений), пока не завершится родительский процесс. Нет необходимости освобождать память, используемую этими списками, до тех пор. На самом деле я точно знаю, что счетчик ссылок останется выше 0 (для этих списков и каждого объекта в этих списках) до выхода программы.

Итак, теперь возникает вопрос, как я могу убедиться, что объекты не будут скопированы в каждый подпроцесс? Могу ли я отключить подсчет ссылок для этих списков и каждого объекта в этих списках?

EDIT3 Еще одно примечание. Подпроцессам не нужно изменять l1 , l2 и l3 или любые объекты в этих списках. Подпроцессы должны только иметь возможность ссылаться на некоторые из этих объектов, не вызывая копирование памяти для каждого подпроцесса.

  • Атрибуты функции Python - использование и злоупотребления
  • Как печатать на stderr в Python?
  • Разбор YAML, возврат с номером строки
  • Как построить два столбца кадра данных pandas с помощью точек?
  • Учитывая список словарей, как я могу устранить дубликаты одного ключа и сортировать по другому
  • Получение последнего элемента списка в Python
  • Python Key Press и Key Release Listener
  • После установки Anaconda команда conda завершается с "ImportError: no module named conda.cli"
  • 2 Solutions collect form web for “Общая память в многопроцессорной обработке”

    Вообще говоря, существует два способа совместного использования одних и тех же данных:

    • Многопоточность
    • Общая память

    Многопоточность Python не подходит для задач, связанных с CPU (из-за GIL), поэтому обычным решением в этом случае является multiprocessing . Тем не менее, с помощью этого решения вам необходимо явно передавать данные, используя multiprocessing.Value и multiprocessing.Array .

    Обратите внимание, что обычно совместное использование данных между процессами может быть не лучшим выбором из-за всех проблем синхронизации; подход, в котором участвуют участники обмена сообщениями, обычно рассматривается как лучший выбор. См. Также документацию Python :

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

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

    В вашем случае вам нужно обернуть l1 , l2 и l3 в некотором смысле понятным путем multiprocessing (например, с использованием multiprocessing.Array ), а затем передать их как параметры.
    Обратите также внимание на то, что, как вы сказали, вам не нужен доступ на запись, вам следует передать lock=False при создании объектов, или весь доступ будет по-прежнему сериализован.

    Если вы хотите использовать функцию копирования на запись, а ваши данные являются статическими (без изменений в дочерних процессах), вы должны сделать python не путать с блоками памяти, где ваши данные лежат. Вы можете легко сделать это, используя структуры C или C ++ (например, stl) в качестве контейнеров и предоставить свои собственные оболочки python, которые будут использовать указатели на память данных (или, возможно, копировать данные), когда объект уровня на основе python будет создан, если вообще , Все это можно сделать очень просто с простотой и синтаксисом python с помощью cython .

     # pseudo cython
     cdef class FooContainer:
        cdef char * данные
        def __cinit __ (self, char * foo_value):
            self.data = malloc (1024, sizeof (char))
            memcpy (self.data, foo_value, min (1024, len (foo_value)))
    
        def get (self):
            return self.data
    
    
     # часть python
     от foo import FooContainer
    
     f = FooContainer («привет мир»)
     pid = fork ()
     если не pid:
        f.get () # этот вызов будет читать одну и ту же страницу памяти, где
                # родительский процесс написал 1024 символа self.data
                # и cython автоматически создадут новую строку python
                # объект из него и вернуться к вызывающему
    

    Вышеупомянутый псевдокод плохо написан. Не используйте его. Вместо self.data должен быть контейнер C или C ++ в вашем случае.

    Python - лучший язык программирования в мире.