Могу ли я разрешить исключение внутри MPI-распараллеленного кода?

Вот некоторые общие вопросы, с которыми я столкнулся при разработке обработки ошибок для алгоритма, который должен выполняться параллельно с использованием MPI (в С++):

  • Выполняются ли исключения внутри кода, который выполняется параллельно? Определено ли поведение?
  • Как они работают? Разве это отличается для разных реализаций?
  • Это хорошая практика - или я должен использовать коды возврата?

Ответ 1

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

MPI_Barrier(comm);            /* Or any synchronous call */
if (!rank) throw Exception("early exit on rank=0");
MPI_Barrier(comm);            /* rank>0 deadlocks here because rank=0 exited early */

У всех методов обработки ошибок есть эта проблема, трудно восстановить ошибки, которые не происходят последовательно на коммуникаторе. В приведенном выше примере вы можете выполнить MPI_Allreduce, чтобы все ранги выбирали одну ветвь.

Мое предпочтение заключается в том, чтобы вызывать обработчики ошибок и распространять их по стеку, поскольку это имеет тенденцию давать мне наиболее полезное/подробное сообщение об ошибке, и его легко поймать с точкой останова (или обработчик ошибок может присоединить отладчик к себе и отправить это на вашу рабочую станцию ​​в xterm).

Ответ 2

В идеальном мире вы можете использовать их, чтобы делать то, что вы просите. Под "идеальным миром" я подразумеваю, что у вас есть выбор реализации MPI и вы можете управлять им самостоятельно (вместо того, чтобы убедить владельца кластера переконфигурировать его для вас). Минимальная конфигурация для исключений будет включать в себя флаг: --with-exceptions и, возможно, еще несколько.

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

Они работают в том же духе, что и 'vanilla' исключения С++. И они работают внутри параллельного исполняемого кода.

В какой-то момент вашего кода запуска вы хотите включить их:

MPI::COMM_WORLD.Set_errhandler ( MPI::ERRORS_THROW_EXCEPTIONS );

(если ваша библиотека не настроена на разрешение исключений, это, вероятно, плохая идея - поведение "undefined" в соответствии с LAM)

И затем:

try { /* something that can fail */ } 
catch ( MPI::Exception e ) {

    cout << "Oops: " << e.Get_error_string() << e.Get_error_code();
    MPI::COMM_WORLD.Abort (-1) ;
}

Что касается хорошей или плохой практики, я не могу сказать. Я не видел их широкого использования в коде, написанном закаленными хакерами MPI, но это может быть из-за того, что в моем случае код обычно больше C, чем С++.

Среднее расстояние между кодами ошибок и исключениями может быть обработчиком ошибок, в двух словах вы можете назначать функции, которые будут вызываться, когда возникает конкретная ошибка (обозначенная кодом). Это может быть вариант, если вы не можете заставить администратора на борту включить исключения.

Ответ 3

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

Если вы хотите получить более подробную информацию об ошибках, чем просто числовой код возврата, вы можете, конечно, вернуться и/или передать строки ошибок или другие объекты (в рамках одного и того же процесса или через MPI, конечно).