разделяемая память, MPI и системы массового обслуживания

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

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

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

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

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

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

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

    • If not, I would use boost.interprocess and use MPI calls to distribute local shared memory identifiers.
    • Общая память будет считываться «локальным мастером» на каждом узле и доступна только для чтения. Нет необходимости в каком-либо семафоре/синхронизации, потому что он не изменится.
  • Есть ли какие-либо проблемы с производительностью или особые проблемы, на которые следует обратить внимание?

    • (There wont be any "strings" or overly weird data structures, everything can be brought down to arrays and structure pointers)
  • Задание будет выполняться в системе очередей PBS (или SGE), в случае нечистого выхода процесса, интересно, очистят ли они разделяемую память, специфичную для узла.


person Blklight    schedule 26.12.2009    source источник
comment
После ответов до сих пор, тестов и дополнительных чтений, файлы с отображением памяти могут быть самым простым вариантом: - Только главный процесс MPI должен будет подготовить файл памяти, который будет отображаться всеми процессами. - Поскольку файл будет доступен только для чтения, не нужно беспокоиться о согласованности содержимого. - Насчет производительности понятия не имею... может только эксперимент покажет.   -  person Blklight    schedule 27.12.2009
comment
Производительность полностью зависит от вашей платформы. Ваши данные скудны, но, учитывая ваши доступные процессоры и оперативную память, у вас не должно возникнуть большой проблемы. Единственное место, где файлы mmapped терпят неудачу, - это если вам нужно изменить общую память (ваши распределенные данные), вам не нужно, чтобы содержимое общей памяти было постоянным, и вам нужна только общая ОЗУ. В этом случае ваша система будет тратить много времени на запись всех изменений памяти на диск.   -  person Mike DeSimone    schedule 10.01.2010
comment
Был в отъезде и не мог выбрать окончательный ответ, тот, кто набрал наибольшее количество голосов, получил его :) Но в любом случае, вокруг много хороших ответов, но ничего точно не отвечающего тому, что я искал, так что я думаю, что нет широко стандартного способ сделать это!   -  person Blklight    schedule 11.01.2010


Ответы (8)


Одним из все более распространенных подходов к высокопроизводительным вычислениям (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 или прямые потоки pthreads или Windows, если уж на то пошло.

person janneb    schedule 06.01.2010
comment
Если вы изменяете свой код, чтобы он был многопоточным на каждом многопроцессорном узле с общей памятью, убедитесь, что вы тщательно написали свое планирование потоков, чтобы принять во внимание локальность кэша и другую архитектуру памяти. - person stephan; 07.01.2010
comment
Я не уверен, что гибридный подход становится все более распространенным. Вот один из примеров того, что этот подход может не стоить того, чтобы его использовать: гибрид" rel="nofollow noreferrer">pdc.kth.se/education/historical/2008/PRACE-P2S2/coursework/ Да, это хорошая концепция, но на практике сомнительная ценность по сравнению с усилиями, необходимыми для изменить свое приложение. - person High Performance Mark; 07.01.2010
comment
этот ответ не касается ни одной из проблем в вопросе - person lurscher; 18.01.2012

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

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

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

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

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

person Mike D.    schedule 27.12.2009

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

person High Performance Mark    schedule 29.12.2009
comment
Разве это не сильно увеличит задержку доступа к разделяемой памяти? Или MPI_Get просто псевдоним для прямой выборки по шине памяти? - person Crashworks; 07.01.2010
comment
@Crashworks Да, MPI-2 RMA на самом деле не быстрее, чем традиционный метод отправки/получения. Во многих случаях медленнее, из-за необходимости прописывать окна памяти. В принципе, в будущем при поддержке специального сетевого оборудования это может стать быстрее, но сегодня нет особых причин для его использования. - person janneb; 07.01.2010
comment
Да, в самом деле. Но, возможно, причина использования MPI2 RMA заключается в том, чтобы выполнять программирование с общей памятью в рамках парадигмы MPI, не прибегая к функциям более низкого уровня, таким как отображаемые в память файлы или библиотеки IPC. Стоимость незначительно более высокой производительности выполнения вполне может быть намного ниже производительности разработки. Интересно, что ОП делает из всего этого. - person High Performance Mark; 08.01.2010

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

person Patrick Sanan    schedule 26.03.2018
comment
Интересно прочитать другой ответ, все датируемый 2009 годом, и посмотреть, через какие обручи пришлось пройти людям до MPI 3 в 2012 году. - person Victor Eijkhout; 11.07.2021

Я мало разбираюсь в unix и не знаю, что такое MPI. Но в Windows то, что вы описываете, является точным соответствием объекту сопоставления файлов.

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

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

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

person John Knoeller    schedule 26.12.2009
comment
Из документации я делаю вывод, что boost.interprocess позволяет сделать это кросс-платформенным способом (нет необходимости в #ifdef) и с чистым кодом. И есть опция, специфичная для Windows, позволяющая именно то, что вы описываете. Но суть проблемы здесь не в технической реализации системы с разделяемой памятью, а в том, как это сделать чисто, когда у вас есть 128 экземпляров ваших приложений, распределенных по 8-ядерным машинам :-) - person Blklight; 27.12.2009
comment
Я не уверен, почему это может быть проблемой. Вы говорите, что хотите поделиться на нескольких компьютерах? Я почти уверен, что каждая машина будет видеть только свою собственную оперативную память, и что все ядра на машине имеют общее представление об оперативной памяти этой машины. - person John Knoeller; 27.12.2009

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

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

person doron    schedule 26.12.2009
comment
Порог glibc malloc mmap по умолчанию составляет 128 КБ, что не соответствует размеру страницы. - person janneb; 07.01.2010

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

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

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

person amir beygi    schedule 28.12.2009
comment
Конечно, статическую часть задачи можно было бы распараллелить лучше, но время разработки было бы огромным. Большую часть памяти полной задачи можно загрузить один раз на каждом вычислительном узле. Итак, я стремлюсь к общей памяти и стремлюсь к лучшей технике для этого! - person Blklight; 29.12.2009
comment
Что я хотел бы знать, так это то, какую проблему вы решали с 7 * 10 ^ 6 переменными. - person ; 05.01.2010

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

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

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

person Paul Nathan    schedule 06.01.2010
comment
Да, данные принадлежат только одному процессору и доступны только для чтения. Здесь нет проблем с согласованностью. Таким образом, файл с отображением памяти может быть простым вариантом. - person Blklight; 08.01.2010
comment
Согласованный. Но это будет зависеть от вашей архитектуры. memmapped-файлы лучше всего подходят для архитектуры с общей памятью. Я не уверен, как это сделать с кластером Беовульф. - person Paul Nathan; 08.01.2010