В стандарте OpenMP указано, что omp_in_parallel()
возвращает true тогда и только тогда, когда окружающая область parallel
активна. Активный регион parallel
определяется как тот, который выполняется командой, состоящей из более чем одного потока. В вашем случае у вас есть неактивная параллельная область, так как есть только один поток. Таким образом, omp_in_parallel()
возвращает false
и выполняется throw
. Это ошибка, потому что стандарт OpenMP ограничивает исключения одним и тем же параллельным регионом и потоком:
throw
, выполняемый внутри области parallel
, должен вызывать возобновление выполнения в той же области parallel
, и тот же поток, который выдал исключение, должен его перехватить.
Это делает ваш код несовместимым, поскольку вы позволяете исключению проходить необработанным через параллельную область. Такая же ошибка возникает во время выполнения Intel OpenMP, поэтому это поведение не является специфичным для GCC.
На самом деле происходит то, что GCC преобразует регион OpenMP, заключая ваш код в блок try/catch
с фильтром для всех исключений:
#pragma omp parallel [child fn: main.omp_fn.0 (???)]
{
try
{
do_something ();
}
catch
{
<<<eh_filter (NULL)>>>
{
terminate ();
}
}
#pragma omp return
}
Именно этот всеобъемлющий фильтр исключений отвечает за сообщение о завершении.
Чтобы решить эту проблему, весь блок try/catch
должен находиться внутри параллельной области, а не наоборот:
# pragma omp parallel
{
try {
do_something();
} catch(int e) {
std::cerr<<"error: '"<<e<<"'\n"; // never gets here
}
}
(дополнительный блок был добавлен, чтобы сделать компилятор Intel C++ счастливым; он не является строго необходимым для GCC)
Это выводит error: '3'
, как и ожидалось.
Изменить: Самое смешное, что ваш обработчик исключений даже не попадает в окончательный двоичный файл. Даже при заданном уровне оптимизации GCC по умолчанию (то есть при компиляции с g++ -fopenmp -o prog prog.cc
) устранитель избыточности может определить, что ваш обработчик исключений никогда не будет достигнут из-за неявного внутреннего обработчика исключений, и поэтому ваш обработчик будет удален. Затем компилятор обнаруживает, что неявный обработчик завершения также является избыточным, так как уже есть обработчик исключения верхнего уровня для всего процесса, который делает то же самое (вызывает terminate()
) и таким образом удаляет даже неявный. Окончательный код такой скудный и подлый — вообще никаких обработчиков исключений:
;; Function int main() (main, funcdef_no=970, decl_uid=20816, cgraph_uid=212)
int main() ()
{
int e;
int D.20855;
struct basic_ostream & D.20854;
struct basic_ostream & D.20853;
void * D.20852;
register int * D.20819;
<bb 2>:
omp_set_num_threads (1);
__builtin_GOMP_parallel_start (main._omp_fn.0, 0B, 0);
main._omp_fn.0 (0B);
__builtin_GOMP_parallel_end ();
D.20855_1 = 0;
// <------ See, ma', no exception handling at all :)
<L0>:
return D.20855_1;
}
;; Function <built-in> (main._omp_fn.0, funcdef_no=976, decl_uid=20857, cgraph_uid=222)
<built-in> (void * .omp_data_i)
{
<bb 2>:
do_something ();
return;
// <------ See, ma', they've nuked the implicit termination handler
}
Можно полюбить вариант -fdump-tree-all
GCC.
Редактировать: Что касается вопроса о том, как исправить do_something()
- используйте omp_get_level()
вместо omp_in_parallel()
:
void do_something()
{
if(omp_get_level() == 0)
throw 3;
}
omp_get_level()
возвращает уровень вложенности parallel
областей, которые окружают вызов, независимо от того, активны они или нет.
person
Hristo Iliev
schedule
22.10.2012