Почему в этом коде openmp происходит ошибка сегментации?
Основная программа:
program main
use omp_lib
use my_module
implicit none
integer, parameter :: nmax = 202000
real(8) :: e_in(nmax) = 0.D0
integer i
call omp_set_num_threads(2)
!$omp parallel default(firstprivate)
!$omp do
do i=1,2
print *, e_in(i)
print *, eTDSE(i)
end do
!$omp end do
!$omp end parallel
end program main
модуль:
module my_module
implicit none
integer, parameter, private :: ntmax = 202000
double complex :: eTDSE(ntmax) = (0.D0,0.D0)
!$omp threadprivate(eTDSE)
end module my_module
составлено с использованием:
ifort -openmp main.f90 my_module.f90
Это дает ошибку сегментации при выполнении. Если удалить одну из команд печати в основной программе, она работает нормально. Также, если удалить функцию omp и скомпилировать без опции -openmp, она тоже работает нормально.
2 ответа
Наиболее вероятной причиной такого поведения является то, что ограничение размера стека слишком мало (по любой причине). поскольку e_in
является приватным для каждого потока OpenMP, одна копия на поток выделяется в стеке потока (даже если вы указали -heap-arrays
!). 202000
элементы REAL(KIND=8)
возьмите 1616 кБ (или 1579 кБ).
Предел размера стека может контролироваться несколькими механизмами:
На стандартных оболочках системы Unix размер стека контролируется
ulimit -s <stacksize in KiB>
, Это также ограничение размера стека для основного потока OpenMP. Значение этого предела также используется потоками POSIX (pthreads
) библиотека в качестве размера стека потоков по умолчанию при создании новых потоков.OpenMP поддерживает контроль над пределом размера стека всех дополнительных потоков через переменную окружения
OMP_STACKSIZE
, Его значение представляет собой число с необязательным суффиксом.k
/K
для КиБ,m
/M
для MiB, илиg
/G
для ГиБ. Это значение не влияет на размер стека основного потока.GNU OpenMP во время выполнения (
libgomp
) распознает нестандартную переменную средыGOMP_STACKSIZE
, Если установлено, оно переопределяет значениеOMP_STACKSIZE
,Среда выполнения Intel OpenMP распознает нестандартную переменную среды
KMP_STACKSIZE
, Если установлено, оно переопределяет значениеOMP_STACKSIZE
а также переопределяет значениеGOMP_STACKSIZE
если используется совместимость OpenMP во время выполнения (которая используется по умолчанию, так как в настоящее время единственная доступная библиотека времени выполнения Intel OpenMP - этоcompat
один).Если ни один из
*_STACKSIZE
переменные установлены, по умолчанию для Intel OpenMP время выполнения2m
на 32-битных архитектурах и4m
на 64-битных.В Windows размер стека основного потока является частью PE-заголовка и встроен там компоновщиком. Если использовать Microsoft
LINK
чтобы сделать ссылку, размер указывается с помощью/STACK:reserve[,commit]
,reserve
Аргумент указывает максимальный размер стека в байтах, а необязательныйcommit
Аргумент указывает начальный размер коммита. Оба могут быть указаны как шестнадцатеричные значения, используя0x
префикс. Если перекомпоновка исполняемого файла не является опцией, размер стека можно изменить, отредактировав заголовок PE с помощьюEDITBIN
, Он принимает тот же аргумент, связанный со стеком, что и компоновщик. Программы, скомпилированные с полной оптимизацией программы MSVC (/GL
) не может быть отредактировано.Линкер GNU для целей Win32 поддерживает установку размера стека с помощью
--stack
аргумент. Чтобы передать опцию непосредственно из GCC,-Wl,--stack,<size in bytes>
может быть использован.
Обратите внимание, что стеки потоков фактически выделяются с размером, установленным *_STACKSIZE
(или к значению по умолчанию), в отличие от стека основного потока, который начинается с малого, а затем увеличивается по требованию до установленного предела. Так что не устанавливайте *_STACKSIZE
в произвольно большое значение, в противном случае вы можете достичь предела размера виртуальной памяти процесса.
Вот некоторые примеры:
$ ifort -openmp my_module.f90 main.f90
Установите ограничение размера основного стека равным 1 МБ (дополнительный поток OpenMP получит 4 МБ по умолчанию):
$ ulimit -s 1024
$ ./a.out
zsh: segmentation fault (core dumped) ./a.out
Установите ограничение размера основного стека на 1700 КиБ:
$ ulimit -s 1700
$ ./a.out
0.000000000000000E+000
(0.000000000000000E+000,0.000000000000000E+000)
0.000000000000000E+000
(0.000000000000000E+000,0.000000000000000E+000)
Установите ограничение размера основного стека равным 2 МБ, а размер стека дополнительного потока - 1 МБ:
$ ulimit -s 2048
$ KMP_STACKSIZE=1m ./a.out
zsh: segmentation fault (core dumped) KMP_STACKSIZE=1m ./a.out
В большинстве систем Unix ограничение размера стека основного потока устанавливается PAM или другим механизмом входа в систему (см. /etc/security/limits.conf
). По умолчанию в Scientific Linux 6.3 установлено значение 10 МБ.
Другой возможный сценарий, который может привести к ошибке, - это ограничение слишком малого виртуального адресного пространства. Например, если ограничение виртуального адресного пространства составляет 1 ГиБ, а ограничение размера стека потока установлено на 512 МБ, то во время выполнения OpenMP будет пытаться выделить 512 МБ для каждого дополнительного потока. С двумя потоками один будет иметь 1 ГБ только для стеков, а при добавлении пространства для кода, общих библиотек, кучи и т. Д. Размер виртуальной памяти превысит 1 ГБ, и произойдет ошибка:
Установите ограничение виртуального адресного пространства равным 1 ГБ и запустите с двумя дополнительными потоками со стеками 512 МБ (я закомментировал вызов omp_set_num_threads()
):
$ ulimit -v 1048576
$ KMP_STACKSIZE=512m OMP_NUM_THREADS=3 ./a.out
OMP: Error #34: System unable to allocate necessary resources for OMP thread:
OMP: System error #11: Resource temporarily unavailable
OMP: Hint: Try decreasing the value of OMP_NUM_THREADS.
forrtl: error (76): Abort trap signal
... trace omitted ...
zsh: abort (core dumped) OMP_NUM_THREADS=3 KMP_STACKSIZE=512m ./a.out
В этом случае библиотека времени выполнения OpenMP не сможет создать новый поток и уведомит вас перед тем, как прервать завершение программы.
Ошибка сегментации связана с ограничением памяти стека при использовании OpenMP. Использование решений из предыдущего ответа не решило проблему для меня в моей ОС Windows. Использование распределения памяти в куче, а не в стеке, похоже, работает:
integer, parameter :: nmax = 202000
real(dp), dimension(:), allocatable :: e_in
integer i
allocate(e_in(nmax))
e_in = 0
! rest of code
deallocate(e_in)
Кроме того, это не повлечет за собой изменение параметров среды по умолчанию.
Подтверждение и ссылка на решение ohm314 здесь: большой массив с использованием выделения динамической памяти