Почему в этом коде 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 здесь: большой массив с использованием выделения динамической памяти

Другие вопросы по тегам