Python geneti c оптимизация многопроцессорной обработки с глобальной постоянной переменной, как ускорить? - PullRequest
4 голосов
/ 26 февраля 2020

Я пишу универсальный c алгоритм оптимизации на основе пакета deap в python 2.7 (цель - в ближайшее время перейти на python 3). Поскольку это довольно сложный процесс, некоторые части оптимизации обрабатываются с помощью многопроцессорного пакета. Вот краткое описание моей программы:

  1. Конфигурации считываются и сохраняются в config объекте
  2. Некоторые дополнительные предварительные вычисления выполняются и сохраняются также в config объект
  3. Оптимизация начинается (популяция инициализируется случайным образом и происходят мутации, для нахождения лучшего решения применяется кроссовер), а некоторые ее части (функция оценки) выполняются в многопроцессорной среде
  4. Результаты сохранены

Для функции оценки нам нужен доступ к некоторым частям объекта config (который после фазы 2 остается постоянным). Поэтому мы делаем его доступным для разных ядер, используя глобальную (постоянную) переменную:

from deap import base
import multiprocessing

toolbox = base.Toolbox()

def evaluate(ind):
    # compute evaluation using config object
    return(obj1,obj2)

toolbox.register('evaluate',evaluate)

def init_pool_global_vars(self, _config):
    global config
    config = _config

...
# setting up multiprocessing
pool = multiprocessing.Pool(processes=72, initializer=self.init_pool_global_vars,
                                        initargs=[config])
toolbox.register('map', pool.map_async)
...
while tic < max_time:
    # creating new individuals
    # computing in optimisation the objective function on the different individuals
    jobs = toolbox.map(toolbox.evaluate, ind)
    fits = jobs.get()
    # keeping best individuals

Мы в основном делаем разные итерации (большие для l oop), пока не будет достигнуто максимальное время. Я заметил, что если я сделаю объект конфигурации больше (то есть добавлю к нему большие атрибуты, например, большой массив numpy), даже если код все тот же, он будет работать намного медленнее (меньше итераций за тот же промежуток времени). Поэтому я подумал, что создам указанный объект c config_multiprocessing, который содержит только атрибуты, необходимые в многопроцессорной части, и передам его как глобальную переменную, но когда я запускаю его на 3 ядрах, он медленнее, чем с большим config объект и на 72 ядрах, это немного быстрее, но не намного.

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

Запуск в образе Linux docker на виртуальной машине linux в облаке.

1 Ответ

0 голосов
/ 16 марта 2020

Пакет joblib предназначен для обработки случаев, когда у вас есть большие массивы numpy для распределения среди работников с общей памятью . Это особенно полезно, если вы рассматриваете данные в общей памяти как «только для чтения», как то, что вы описываете в своем сценарии. Вы также можете создать доступную для записи общую память , как описано в документации .

Ваш код может выглядеть примерно так:


import os

import numpy as np
from joblib import Parallel, delayed
from joblib import dump, load

folder = './joblib_memmap'
try:
    os.mkdir(folder)
except FileExistsError:
    pass

def evaluate(ind, data):
    # compute evaluation using shared memory data
    return(obj1, obj2)

# just used to initialize memory mapped data
def init_memmap_data(original_data):
    data_filename_memmap = os.path.join(folder, 'data_memmap')
    dump(original_data, data_filename_memmap)
    shared_data = load(data_filename_memmap, mmap_mode='r')
    return shared_data

...
# however you set up indices needs to be changed here
indexes = range(10)  

# however you load your numpy data needs to be done here
shared_data = init_memmap_data(numpy_array_to_share)  

# change n_jobs as appropriate
results = Parallel(n_jobs=2)(delayed(evaluate)(ind, shared_data) for ind in indexes)  

# get index of the maximum as the "best" individual
best_fit_individual = indexes[results.argmax()]

Кроме того, joblib поддерживает threading backend , который может быть быстрее, чем процесс на основе. Это будет легко проверить с помощью JobLib.

...