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

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

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

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

на работе 4 CPU это означало бы, по крайней мере, нагрузку на 20GB RAM, большую часть памяти "впустую", это ужасно.

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

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

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

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

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

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 threads, библиотеку Intel TBB или прямые потоки pthreads или Windows.


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

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

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

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

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


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


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

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

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

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


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

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


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

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

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


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

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

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


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