2013-06-11 5 views
20

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

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

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

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

ось у - время обработки
оси х - NR процессов
цветов - размер каждого индивидуума (№ поплавков)

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

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

3) Обе картины на верхней части друг друга enter image description here

Сначала я подумал, что это вина HyperThreading, потому что для наборов данных большого становится медленнее после достижения 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 Xeons (2x 6/12 ядер) и повторил тест: enter image description here

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

+1

Реализации MPI обычно имеют множество различных алгоритмов для коллективных операций, таких как разброс и сбор. Большинство библиотек имеют собственную эвристику, чтобы выбрать лучший алгоритм, но иногда он терпит неудачу. Некоторые библиотеки позволяют принудительно выполнять эти алгоритмы, например. в Open MPI это может быть достигнуто путем передачи аргументов MCA в 'mpiexec'. Это поможет, если вы сообщите нам, какую реализацию MPI вы используете. –

+0

Я использую Open MPI, добавленный в вопрос. – Michal

+0

Было бы здорово, если бы вы могли предоставить больше контекста, например, показать части кода, в котором данные распределяются (обе реализации). Обычно разбросаны и собираются в Open MPI-шкале очень хорошо с количеством процессов, когда все они работают на одном узле. –

ответ

5

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

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

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

Некоторые это обсуждается in this thread.

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

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

+0

Я работаю над приложением для тестирования эволюционных алгоритмов в разных вычислительных средах: ПК, кластеры, сетки и т. Д. Я не спорю, правильно ли здесь использовать MPI. Мне просто любопытно, откуда возникают накладные расходы. И, кстати, пул python pool.map использует очередь для связи, которая реализуется либо с помощью труб, либо с сокетами. Я настроил его на использование сокетов, поэтому метод связи одинаков. – Michal

+0

@Michal это не потому, что вы используете сокеты, которые используете tcp. Вот несколько интересных слайдов на разных скоростях, которые вы можете получить с помощью труб, сокетов или shmem, + инструмента сравнения, который расскажет вам, что может быть самым быстрым в вашей системе (в зависимости от того, как ваши ядра могут взаимодействовать, как ваши сотовые сокеты могут общаться, и их домен numa.) http://anil.recoil.org/talks/fosdem-io-2012.pdf –

+0

спасибо, что указали это. Я проверю, что вы написали, и веб-сайт, который вы дали, когда у меня есть время. – Michal

Смежные вопросы