Por que falha de segmentação está acontecendo neste código openmp?

programa principal:

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 

módulo:

 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 

compilado usando:

 ifort -openmp main.f90 my_module.f90 

Dá a falha de segmentação quando a execução. Se remover um dos comandos de impressão no programa principal, ele funcionará bem. Além disso, se você remover a function omp e compilar sem a opção -openmp, ela também funcionará bem.

A causa mais provável para esse comportamento é que seu limite de tamanho de pilha é muito pequeno (por qualquer motivo). Como e_in é privado para cada thread OpenMP, uma cópia por thread é alocada na pilha de threads (mesmo se você tiver especificado -heap-arrays !). 202000 elementos de REAL(KIND=8) levam 1616 kB (ou 1579 KiB).

O limite de tamanho da pilha pode ser controlado por vários mecanismos:

  • Em shells padrão do sistema Unix, a quantidade de tamanho de pilha é controlada por ulimit -s . Esse também é o limite de tamanho de pilha para o thread principal do OpenMP. O valor desse limite também é usado pela biblioteca de encadeamentos POSIX ( pthreads ) como o tamanho da pilha de encadeamentos padrão ao criar novos encadeamentos.

  • O OpenMP suporta controle sobre o limite de tamanho de pilha de todos os encadeamentos adicionais através da variável de ambiente OMP_STACKSIZE . Seu valor é um número com um sufixo opcional k / K para KiB, m / M f para MiB ou g / G para GiB. Este valor não afeta o tamanho da pilha do thread principal.

  • O tempo de execução do GNU OpenMP ( libgomp ) reconhece a variável de ambiente não padrão GOMP_STACKSIZE . Se configurado, substitui o valor de OMP_STACKSIZE .

  • O tempo de execução do Intel OpenMP reconhece a variável de ambiente não padrão KMP_STACKSIZE . Se configurado, substitui o valor de OMP_STACKSIZE e também substitui o valor de GOMP_STACKSIZE se a compatibilidade OpenMP run-time for usada (que é o padrão como atualmente a única biblioteca de tempo de execução Intel OpenMP disponível é a compat ).

  • Se nenhuma das variables *_STACKSIZE estiver definida, o padrão para o tempo de execução do Intel OpenMP é de 2m em arquiteturas de 32 bits e 4m em arquiteturas de 64 bits.

  • No Windows, o tamanho da pilha do encadeamento principal faz parte do header PE e é incorporado ao vinculador. Se estiver usando o LINK da Microsoft para fazer a vinculação, o tamanho é especificado usando o /STACK:reserve[,commit] . O argumento de reserve especifica o tamanho máximo de pilha em bytes, enquanto o argumento de commit opcional especifica o tamanho de confirmação inicial. Ambos podem ser especificados como valores hexadecimais usando o prefixo 0x . Se a vinculação do executável não for uma opção, o tamanho da pilha poderá ser modificado editando o header PE com EDITBIN . Leva o mesmo argumento relacionado à pilha que o vinculador. Programas compilados com toda a otimização de programas do MSVC ativada ( /GL ) não podem ser editados.

  • O vinculador GNU para destinos Win32 suporta a configuração do tamanho da pilha através do argumento --stack . Para passar a opção diretamente do GCC, o -Wl,--stack, pode ser usado.

Observe que as pilhas de encadeamento são, na verdade, alocadas com o tamanho definido por *_STACKSIZE (ou com o valor padrão), diferente da pilha do encadeamento principal, que começa pequeno e cresce sob demanda até o limite definido. Portanto, não defina *_STACKSIZE para um valor arbitrário maior, caso contrário, você poderá atingir o limite de tamanho da memory virtual do processo.

aqui estão alguns exemplos:

 $ ifort -openmp my_module.f90 main.f90 

Defina o limite do tamanho da pilha principal como 1 MiB (o segmento OpenMP adicional teria 4 MiB como padrão):

 $ ulimit -s 1024 $ ./a.out zsh: segmentation fault (core dumped) ./a.out 

Defina o limite do tamanho da pilha principal para 1700 KiB:

 $ ulimit -s 1700 $ ./a.out 0.000000000000000E+000 (0.000000000000000E+000,0.000000000000000E+000) 0.000000000000000E+000 (0.000000000000000E+000,0.000000000000000E+000) 

Defina o limite do tamanho da pilha principal como 2 MiB e o tamanho da pilha da linha adicional como 1 MiB:

 $ ulimit -s 2048 $ KMP_STACKSIZE=1m ./a.out zsh: segmentation fault (core dumped) KMP_STACKSIZE=1m ./a.out 

Na maioria dos sistemas Unix, o limite de tamanho de pilha do thread principal é definido pelo PAM ou outro mecanismo de login (veja /etc/security/limits.conf ). O padrão no Scientific Linux 6.3 é de 10 MiB.

Outro cenário possível que pode levar a um erro é se o limite do espaço de endereço virtual estiver definido como muito baixo. Por exemplo, se o limite do espaço de endereço virtual for 1 GiB e o limite de tamanho da pilha de encadeamentos estiver definido como 512 MiB, o tempo de execução do OpenMP tentará alocar 512 MiB para cada encadeamento adicional. Com dois encadeamentos, um teria 1 GiB apenas para as pilhas e, quando o espaço para código, bibliotecas compartilhadas, heap, etc. fosse incluído, o tamanho da memory virtual aumentaria para mais de 1 GiB e ocorreria um erro:

Configure o limite do espaço de endereço virtual como 1 GiB e execute com dois encadeamentos adicionais com 512 MiB pilhas (comentei a chamada para 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 

Nesse caso, a biblioteca de tempo de execução do OpenMP falharia ao criar um novo thread e o notificaria antes de interromper a finalização do programa.

A falha de segmentação é devido ao limite de memory da pilha ao usar o OpenMP. Usando as soluções da resposta anterior não resolveu o problema para mim no meu sistema operacional Windows. Usar a alocação de memory no heap, em vez de na memory da pilha, parece funcionar:

 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) 

Além disso, isso não envolveria a alteração de nenhum parâmetro de ambiente padrão.

Confirmação e referência à solução de ohm314 aqui: matriz grande usando alocação de memory de heap