общая память, MPI и системы очередей - PullRequest
20 голосов
/ 26 декабря 2009

Мое приложение Unix / Windows C ++ уже распараллелено с использованием MPI: задание разбивается на N cpus, и каждый фрагмент выполняется параллельно, довольно эффективно, очень хорошее масштабирование скорости, задание выполняется правильно.

Но некоторые данные повторяются в каждом процессе, и по техническим причинам эти данные не могут быть легко разделены по MPI (...). Например:

  • 5 ГБ статических данных, то же самое, загруженное для каждого процесса
  • 4 ГБ данных, которые могут быть распределены в MPI, чем больше процессоров используется, тем меньше это ОЗУ на процессор.

При работе с 4 процессорами это означало бы загрузку ОЗУ не менее 20 ГБ, большая часть памяти была потрачена впустую, это ужасно.

Я думаю, что при использовании разделяемой памяти для снижения общей нагрузки «статический» чанк будет загружаться только один раз на компьютер.

Итак, главный вопрос:

  • Есть ли какой-нибудь стандартный способ MPI для совместного использования памяти на узле? Какой-то легкодоступный + бесплатная библиотека?

    • Если нет, я бы использовал boost.interprocess и использовал вызовы MPI для распределения идентификаторов локальной общей памяти.
    • Общая память будет считываться "локальным мастером" на каждом узле и доступна только для чтения. Нет необходимости в любом виде семафора / синхронизации, потому что он не изменится.
  • Есть ли какие-либо проблемы с производительностью или особые проблемы, которые следует опасаться?

    • (не будет никаких «строк» ​​или слишком странных структур данных, все может быть сведено к массивам и указателям на структуры)
  • Задание будет выполнено в системе очередей PBS (или SGE), в случае нечистого завершения процесса, интересно, будут ли они очищать разделяемую память для конкретного узла.

Ответы [ 8 ]

8 голосов
/ 06 января 2010

Одним из все более распространенных подходов в высокопроизводительных вычислениях (HPC) являются гибридные программы MPI / OpenMP. То есть у вас есть N процессов MPI, и каждый процесс MPI имеет M потоков. Этот подход хорошо отображает кластеры, состоящие из многопроцессорных узлов с общей памятью.

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

В зависимости от реализации MPI, вы можете или не можете выполнять вызовы MPI из всех потоков. Это определяется аргументами required и provided функции MPI_Init_Thread (), которую вы должны вызывать вместо MPI_Init (). Возможные значения

{ MPI_THREAD_SINGLE}
    Only one thread will execute. 
{ MPI_THREAD_FUNNELED}
    The process may be multi-threaded, but only the main thread will make MPI calls (all MPI calls are ``funneled'' to the main thread). 
{ MPI_THREAD_SERIALIZED}
    The process may be multi-threaded, and multiple threads may make MPI calls, but only one at a time: MPI calls are not made concurrently from two distinct threads (all MPI calls are ``serialized''). 
{ MPI_THREAD_MULTIPLE}
    Multiple threads may call MPI, with no restrictions. 

По моему опыту, современные реализации MPI, такие как Open MPI, поддерживают наиболее гибкий MPI_THREAD_MULTIPLE. Если вы используете старые библиотеки MPI или какую-то специализированную архитектуру, вам может быть хуже.

Конечно, вам не нужно выполнять многопоточность с OpenMP, это просто самый популярный вариант в HPC. Вы можете использовать, например, библиотека потоков Boost, библиотека Intel TBB или прямые потоки или потоки Windows в этом отношении.

7 голосов
/ 27 декабря 2009

Я не работал с MPI, но если это похоже на другие библиотеки IPC, которые я видел, которые скрывают, находятся ли другие потоки / процессы / что-либо на одном и том же или разных компьютерах, то он не сможет гарантировать совместную память , Да, он может обрабатывать разделяемую память между двумя узлами на одной машине, если эта машина сама предоставила разделяемую память. Но попытка разделить память между узлами на разных машинах в лучшем случае была бы очень сложной из-за сложных проблем, связанных с согласованностью. Я ожидаю, что это просто не будет реализовано.

С практической точки зрения, если вам нужно разделить память между узлами, лучше всего делать это вне MPI. я не думаю, что вам нужно использовать разделяемую память в стиле boost.interprocess, поскольку вы не описываете ситуацию, когда разные узлы вносят детальные изменения в разделяемую память; он доступен только для чтения или разбит на разделы.

Ответы Джона и Деуса охватывают, как отобразить в файле, что, безусловно, вы хотите сделать для статических данных 5 Гб (гига бит ?). Данные для каждого процессора звучат как одно и то же, и вам просто нужно отправить сообщение каждому узлу, сообщающее, какую часть файла он должен получить. ОС должна позаботиться о сопоставлении виртуальной памяти с физической памятью с файлами.

Что касается очистки ... Я бы предположил, что она не выполняет никакой очистки общей памяти, но mmap ed-файлы должны быть очищены, так как файлы закрываются (что должно освободить их отображения памяти), когда очищается процесс вверх. Я понятия не имею, что предостережения CreateFileMapping и т. Д. Имеют

Фактическая «разделяемая память» (т. Е. boost.interprocess) не очищается, когда процесс умирает. Если возможно, я бы порекомендовал попробовать убить процесс и посмотреть, что осталось позади.

2 голосов
/ 29 декабря 2009

С MPI-2 у вас есть RMA (удаленный доступ к памяти) через такие функции, как MPI_Put и MPI_Get. Использование этих функций, если ваша установка MPI их поддерживает, безусловно, поможет вам снизить общее потребление памяти вашей программой. В коде сложность добавляется, но это удовольствие от параллельного программирования. Опять же, он удерживает вас в области MPI.

0 голосов
/ 26 марта 2018

MPI-3 предлагает окна совместной памяти (см., Например, MPI_Win_allocate_shared()), что позволяет использовать общую память на узле без каких-либо дополнительных зависимостей.

0 голосов
/ 06 января 2010

Я столкнулся с этой проблемой, когда использовал MPI несколько лет назад.

Я не уверен, что SGE понимает файлы, отображенные в памяти. Если вы распространяете против кластера Беовульф, я подозреваю, что у вас возникнут проблемы с согласованностью. Не могли бы вы немного рассказать о вашей многопроцессорной архитектуре?

Мой черновой подход заключается в том, чтобы настроить архитектуру, в которой каждая часть данных принадлежит определенному ЦП. Было бы два потока: один поток, являющийся двусторонней связью MPI, и один поток для вычисления результата. Обратите внимание, что MPI и потоки не всегда хорошо играют вместе.

0 голосов
/ 28 декабря 2009

У меня было несколько проектов с MPI в SHUT.

Как я знаю, существует много способов распространения проблемы с использованием MPI, возможно, вы можете найти другое решение, которое не требует общей памяти, мой проект решал уравнение 7 000 000 и переменную 7 000 000

если вы сможете объяснить свою проблему, я постараюсь вам помочь

0 голосов
/ 27 декабря 2009

Если вы храните ваши статические данные в файле, вы можете использовать mmap в unix для получения произвольного доступа к данным. Данные будут разбиты на страницы по мере необходимости доступа к определенному фрагменту данных. Все, что вам нужно сделать, это наложить любые двоичные структуры на данные файла. Это Unix-эквивалент CreateFileMapping и MapViewOfFile, упомянутых выше.

Кстати, glibc использует mmap при вызове malloc для запроса более чем одной страницы данных.

0 голосов
/ 27 декабря 2009

Я не знаю много о Unix, и я не знаю, что такое MPI. Но в Windows вы описываете точное соответствие для объекта сопоставления файлов.

Если эти данные встроены в ваш .EXE или .DLL, который он загружает, то они будут автоматически распределены между всеми процессами. Разрушение вашего процесса, даже в результате сбоя, не приведет к утечке или невыпущенной блокировке ваших данных. однако 9Gb .dll звучит немного странно. Так что это, вероятно, не работает для вас.

Однако вы можете поместить свои данные в файл, затем CreateFileMapping и MapViewOfFile в нем. Отображение может быть доступно только для чтения, и вы можете отобразить весь или часть файла в память. Все процессы будут совместно использовать страницы, которые отображаются в один и тот же базовый объект CreateFileMapping. Хорошей практикой является закрытие unmap-представлений и закрытие дескрипторов, но если вы этого не сделаете, ОС сделает это за вас после завершения.

Обратите внимание, что если вы не используете x64, вы не сможете отобразить файл 5 ГБ в одно представление (или даже файл 2 ГБ, 1 ГБ может работать). Но, учитывая, что вы говорите о том, что это уже работает, я предполагаю, что вы уже используете только x64.

...