1

Я прочитал об этом сообщении, Python multiprocessing: sharing a large read-only object between processes?, но все еще не уверен, как действовать дальше.Как разделить очень большой словарь среди процессов в Python

Вот моя проблема:

я анализирую массив миллионов строк с использованием multiprocessing, и должна быть проверена на большой Словарь, который состоит из примерно 2 миллиона долларов (может быть, выше) ключей каждой строки. Его значения - это объекты настраиваемого класса Python под названием Bloomfilter (поэтому они не просто простые int или float, либо массивы), а их размеры варьируются от нескольких байтов до 1,5 Гб. Анализ для каждой строки в основном проверяет, находится ли строка в определенном количестве цветных фильтров в словаре. Это зависит от самой строки, чтобы решить, какие цветущие фильтры имеют значение. Словарь представляет собой преобразование 30G sqlite3 db. Мотивация состоит в том, чтобы загрузить весь объем sqlite3 db в память, чтобы ускорить обработку, но я не нашел способ эффективно делиться dict. У меня около 100G памяти в моей системе.

Вот что я попытался:

Анализа для каждой строки является CPU переплета, поэтому я выбрал многопроцессорный над многопоточностью. Ключ заключается в том, как разделить большой dict среди процессов без копирования. multiprocess.Value и multiprocessing.Array не могут обрабатывать сложные объекты, такие как dict. Я пробовал multiprocessing.Manager(), но так как диктофон настолько велик, что я получаю ошибку IOError: bad message length. Я также попытался использовать базу данных в памяти, такую ​​как Redis на localhost, но битаррей, который используется для создания Bloomfilter после извлечения, слишком велик, чтобы вписаться, и это заставляет меня думать, что передача больших сообщений среди процессов просто слишком дорого (это?)

Мой вопрос:

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

+1

Вы используете Unix? Вы могли бы просто использовать fork() столько раз, сколько необходимо, что дало бы вам копию структуры данных в каждом процессе. Если вы используете окна, это, вероятно, более сложно, поскольку я не думаю, что fork реализован совершенно так. – Max

+0

Да, я на Linux. Не могли бы вы немного подробнее рассказать о '' fork() '' или указать мне ссылку? – zyxue

+0

Хорошо, имейте в виду, что fork - инструмент очень низкого уровня, но он находится в модуле os: https://docs.python.org/2/library/os.html#os.fork (та же идея в python 3) , Он буквально клонирует процесс, поэтому они имеют все те же данные; если после этого вы не выполните никаких операций с записью структуры данных, процессы должны совместно использовать соответствующие страницы памяти. Затем вам может понадобиться написать свои собственные методы для подсчета вычисленных данных в конце. – Max

ответ

1

Оказывается, что и @Max, и @Dunes являются правильными, но я не нужно либо os.fork() напрямую, либо глобальная переменная. Некоторый псевдокод показан ниже, если big_dict не изменен в worker, в памяти имеется только одна копия. Тем не менее, я не уверен, что эта функция копирования на запись является универсальной в мире ОС Unix. ОС, в которой я запускаю мой код, - CentOS release 5.10 (окончательный).

from multiprocessing import Process, Lock 

def worker(pid, big_dict, lock): 
    # big_dict MUST NOT be modified in the worker because of copy-on-write 
    pass 
    # do some heavy work 

def main(): 
    big_dict = init_a_very_big_dict() 

    NUM_CPUS = 24 
    lock = Lock() 
    procs = [] 
    for pid in range(NUM_CPUS): 
     proc = Process(target=worker, args=(pid, big_dict, lock)) 
     proc.daemon = True 
     procs.append(proc) 
     proc.start() 

    for proc in procs: 
     proc.join() 
+0

В примечании: это, вероятно, не работает на окнах, у которых нет процесса forking. – Max

+0

@Max У меня точно такая же ситуация, и он использовался для работы на нашем сервере debian, но теперь он больше не работает ... Даже используя Менеджер (который не дает ошибки в моем случае), память растет и растет ... Похоже, что только часть данных копируется, так как размер растет медленно, но неуклонно, пока процессоры работают на 100%. Может быть, какой-то буфер + отсутствует сбор мусора? –