Разделяемая память, MPI и системы очередей
Мое приложение unix/windows С++ уже распараллеливается с использованием MPI: задание разбивается на N cpus, и каждый кусок выполняется параллельно, довольно эффективный, очень хороший масштабирование скорости, работа выполняется правильно.
Но некоторые данные повторяются в каждом процессе, и по техническим причинам эти данные не могут быть легко разделены на MPI (...).
Например:
- 5 Gb статических данных, то же самое загружено для каждого процесса
- 4 Гб данных, которые могут быть распределены в MPI, чем больше процессоров используется, тем меньше это ОЗУ на процессор.
В четырехзадачном задании ЦП это означало бы, по крайней мере, нагрузку на 20 ГБ, большая часть памяти "впустую", это ужасно.
Я собираюсь использовать общую память для уменьшения общей нагрузки, "статический" кусок будет загружен только один раз на компьютер.
Итак, главный вопрос:
-
Есть ли какой-либо стандартный способ MPI для обмена памятью на node? Какой-нибудь доступный + бесплатный библиотека?
- Если нет, я бы использовал
boost.interprocess
и использовал вызовы MPI для распространения локальных идентификаторов разделяемой памяти.
- Общая память будет считываться "локальным мастером" для каждого node и доступна только для чтения. Нет необходимости в каких-либо семафорах/синхронизации, потому что это не изменится.
-
Любые проблемы с производительностью или особые проблемы, о которых нужно опасаться?
- (Там не будет никаких "строк" или слишком странных структур данных, все может быть сведено к массивам и указателям структуры).
-
Задача будет выполнена в системе очередности PBS (или SGE), в случае нечистого выхода процесса, я задаюсь вопросом, очистят ли они эту специальную разделяемую память node.
Ответы
Ответ 1
Один из наиболее распространенных подходов в высокопроизводительных вычислениях (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.
Ответ 2
Я не работал с MPI, но если он похож на другие библиотеки IPC, я видел, что скрывают, являются ли другие потоки/процессы/все на одном и том же или разных машинах, тогда он не сможет гарантировать общую память, Да, он может обрабатывать разделяемую память между двумя узлами на одной машине, если на этой машине была предоставлена общая память. Но попытка обмена памятью между узлами на разных машинах в лучшем случае была бы очень сложной, из-за возникающих сложных проблем согласованности. Я ожидаю, что это просто не реализовано.
При всей практичности, если вам нужно обмениваться памятью между узлами, лучше всего сделать это за пределами MPI. я не думаю, что вам нужно использовать разделяемую память boost.interprocess
-style, так как вы не описываете ситуацию, когда разные узлы делают мелкозернистые изменения в общей памяти; он либо доступен только для чтения, либо разбит на разделы.
John и deus отвечают на вопрос, как отображать в файле, что, безусловно, вы хотите сделать для статических данных 5 Gb (gigabit?). Данные для каждого процессора похожи на одно и то же, и вам просто нужно отправить сообщение каждому node, чтобы сообщить, какую часть файла он должен захватить. ОС должна заботиться о сопоставлении виртуальной памяти с физической памятью с файлами.
Что касается очистки... Я бы предположил, что он не выполняет очистку разделяемой памяти, но mmap
ed файлы должны быть очищены, так как файлы закрыты (что должно отпускать их отображения памяти), когда процесс очищается вверх. Я понятия не имею, какие оговорки CreateFileMapping
и т.д. Имеют.
Фактическая "общая память" (т.е. boost.interprocess
) не очищается, когда процесс умирает. Если возможно, я рекомендую попробовать убить процесс и посмотреть, что осталось.
Ответ 3
С MPI-2 у вас есть RMA (доступ к удаленной памяти) через такие функции, как MPI_Put и MPI_Get. Используя эти функции, если ваша поддержка MPI их поддерживает, это наверняка поможет вам сократить общее потребление памяти вашей программой. В стоимость добавлена сложность кодирования, но эта часть удовольствия от параллельного программирования. Опять же, он удерживает вас в области MPI.
Ответ 4
Я не знаю много о unix, и я не знаю, что такое MPI. Но в Windows то, что вы описываете, является точным соответствием для объекта сопоставления файлов.
Если эти данные вложены в ваш .EXE или .DLL, который он загружает, тогда он будет автоматически разделяться между всеми процессами. Срыв вашего процесса, даже в результате сбоя, не приведет к утечкам или невыпущенным блокировкам ваших данных. однако 9Gb.dll звучит немного некорректно. Так что это, вероятно, не сработает для вас.
Однако вы можете поместить свои данные в файл, а затем CreateFileMapping
и MapViewOfFile
. Отображение может быть только для чтения, и вы можете отобразить все или часть файла в памяти. Все процессы будут обмениваться страницами, которые сопоставлены с тем же базовым объектом CreateFileMapping. это хорошая практика, чтобы закрыть unmap-представления и закрыть дескрипторы, но если вы не будете делать это для вас в режиме разговора.
Обратите внимание, что если вы не используете x64, вы не сможете сопоставить 5Gb файл в одном представлении (или даже файл 2Gb, 1Gb может работать). Но, учитывая, что вы говорите о том, что это уже работает, я предполагаю, что вы уже только x64.
Ответ 5
Если вы сохраняете свои статические данные в файле, вы можете использовать mmap для unix для получения произвольного доступа к данным. Данные будут обрабатываться по мере необходимости, когда вам нужен доступ к определенному биту данных. Все, что вам нужно сделать, это наложить любые двоичные структуры на данные файла. Это эквивалент Unix для CreateFileMapping и MapViewOfFile, упомянутых выше.
Кстати, glibc использует mmap, когда вы вызываете malloc для запроса более чем страницы данных.
Ответ 6
У меня были некоторые проекты с MPI в SHUT.
Как я знаю, существует множество способов распространения проблемы с использованием MPI, возможно, вы можете найти другое решение, для которого не требуется общая память,
мой проект решал уравнение 7 000 000 и 7 000 000 переменных
если вы можете объяснить свою проблему, я постараюсь вам помочь.
Ответ 7
Я столкнулся с этой проблемой в маленьком, когда я использовал MPI несколько лет назад.
Я не уверен, что SGE понимает файлы с отображением памяти. Если вы распространяете данные против кластера Beowulf, я подозреваю, что у вас будут проблемы согласованности. Не могли бы вы немного рассказать о своей многопроцессорной архитектуре?
Мой проект проекта заключался бы в создании архитектуры, в которой каждая часть данных принадлежит определенному процессору. Было бы два потока: один поток был двунаправленным MPI MPI и одним потоком для вычисления результата. Обратите внимание, что MPI и потоки не всегда хорошо сочетаются.