Подтвердить что ты не робот

Параллельное приложение в python становится намного медленнее при использовании mpi, а не мультипроцессорного модуля

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

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

Моя тестовая платформа - это виртуальная машина (виртуальная машина), работающая под управлением Ubuntu 11.10 с Open MPI 1.4.3 на Core i7 (4/8 ядер), 8 ГБ оперативной памяти и SSD-накопитель.

То, что я нахожу, действительно удивительно, заключается в том, что я получаю приятное ускорение, однако, в зависимости от инструмента связи, после определенного порога процессов производительность ухудшается. Это можно проиллюстрировать на рисунках ниже.

y ось - время обработки
x ось - nr процессов
цвета - размер каждого человека (nr поплавков)

1) Использование модуля многопроцессорности - Pool.map enter image description here

2) Использование mpi - Scatter/Gather enter image description here

3) Обе фотографии друг на друга enter image description here

Сначала я думал, что он вызывает гиперпотоки, потому что для больших наборов данных он становится медленнее после достижения 4 процессов (4 физических ядра). Однако он также должен быть виден в многопроцессорном корпусе, и это не так. Еще одна догадка заключается в том, что методы коммуникации mpi гораздо менее эффективны, чем python, однако мне трудно поверить.

Есть ли у кого-нибудь объяснение этих результатов?

ДОБАВЛЕНО:

Я начинаю верить, что это ошибка Hyperthreading. Я тестировал свой код на машине с ядром i5 (2/4 ядра), а производительность хуже с 3 или более процессами. Единственное объяснение, которое приходит мне на ум, заключается в том, что i7, который я использую, не имеет достаточного количества ресурсов (кэш?) Для вычисления оценки одновременно с Hyperthreading и требует запланировать более 4 процессов для работы на 4 физических ядрах.

Однако интересно то, что, когда я использую mpi htop, показано полное использование всех 8 логических ядер, что должно предполагать, что приведенное выше утверждение неверно. С другой стороны, когда я использую Pool.Map, он не полностью использует все ядра. Он использует один или два до максимума, а остальные только частично, опять же не знаю, почему он ведет себя таким образом. Завтра я приложу скриншот, показывающий это поведение.

Я не делаю ничего необычного в коде, это очень просто (я не даю весь код не потому, что он секретный, а потому, что ему нужны дополнительные библиотеки, такие как DEAP. Если кто-то действительно интересуется проблема и готов к установке DEAP, я могу подготовить короткий пример). Код для MPI немного отличается, потому что он не может иметь дело с контейнером для населения (который наследуется из списка). Конечно, есть некоторые накладные расходы, но ничего серьезного. Помимо кода, который я покажу ниже, остальная часть этого же текста.

Pool.map:

def eval_population(func, pop):
    for ind in pop:
        ind.fitness.values = func(ind)

    return pop

# ...
self.pool = Pool(8)
# ...

for iter_ in xrange(nr_of_generations):
    # ...
    self.pool.map(evaluate, pop) # evaluate is really an eval_population alias with a certain function assigned to its first argument.
    # ...

MPI - Scatter/Gather

def divide_list(lst, n):
    return [lst[i::n] for i in xrange(n)]

def chain_list(lst):
    return list(chain.from_iterable(lst))

def evaluate_individuals_in_groups(func, rank, individuals):
    comm = MPI.COMM_WORLD
    size = MPI.COMM_WORLD.Get_size()

    packages = None
    if not rank:
        packages = divide_list(individuals, size)

    ind_for_eval = comm.scatter(packages)
    eval_population(func, ind_for_eval)

    pop_with_fit = comm.gather(ind_for_eval)

    if not rank:
        pop_with_fit = chain_list(pop_with_fit)
        for index, elem in enumerate(pop_with_fit):
            individuals[index] = elem

for iter_ in xrange(nr_of_generations):
        # ...
        evaluate_individuals_in_groups(self.func, self.rank, pop)
        # ...

ДОБАВЛЕНО 2: Как я уже упоминал ранее, я провел несколько тестов на своей машине i5 (2/4 ядра), и вот результат: enter image description here

Я также нашел машину с 2 хэонами (2x 6/12 ядер) и повторил эталон: enter image description here

Теперь у меня есть 3 примера такого же поведения. Когда я запускаю вычисления в большем количестве процессов, чем физические ядра, он начинает ухудшаться. Я считаю, потому что процессы на одном физическом ядре не могут выполняться одновременно из-за нехватки ресурсов.

4b9b3361

Ответ 1

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

mpi4py делает копию для каждого сообщения, поскольку она предназначена для использования распределенной памяти. Если ваш OpenMPI не настроен на использование sharedmemory для обмена внутри node, это сообщение будет отправлено через стек tcp ядра и обратно, чтобы доставить его другому процессу, который снова добавит некоторые служебные данные.

Если вы только собираетесь выполнять вычисления внутри одного и того же компьютера, здесь нет необходимости использовать mpi.

Некоторые из этих обсуждений обсуждаются в этом потоке.

Обновление Проект ipc-benchmark пытается понять, как различные типы связи выполняются в разных системах. (многоядерная, многопроцессорная, разделяемая память) И особенно, как это влияет на виртуализированные машины!

Я рекомендую запустить ipc-benchmark на виртуализированной машине и опубликовать результаты. Если они выглядят как этот, это может дать вам большую информацию о различии между tcp, сокетами и каналами.