memory management - statica - L'allocazione di memoria Fortran non dà un errore, ma il programma viene ucciso dal sistema operativo durante l'inizializzazione




stack e heap c# (2)

Commento esteso piuttosto che una risposta:

Nell'inizializzazione di Fortran ha un significato specifico; si riferisce all'impostazione del valore di una variabile alla dichiarazione. Così questo

real :: myvar = 0.0

è l'inizializzazione. Mentre questi

real :: myvar
....
myvar = 0.0

non sono. Ora, forse più rilevante per il problema che hai segnalato, questa affermazione

isensit%sa(:) = 0.0

assegna il valore 0.0 a ogni elemento della sezione dell'array isensit%sa(:) . Questo è molto (una volta che ci si abitua) diverso da ciò che penso si intendesse scrivere, che è:

isensit%sa = 0.0

Questa versione assegna il valore 0.0 a ogni elemento dell'array isensit%sa . Poiché una sezione di array, anche una che comprende ogni elemento dell'array, non è la matrice, i compilatori di Fortran possono allocare temporaneamente spazio per la sezione mentre elabora l'assegnazione. Questo probabilmente ha senso quando si pensa a una sezione di array più generale.

Non sono sicuro di capire perché pensi che lo spazio non venga assegnato quando viene eseguita l'istruzione allocate , ma ti suggerisco di risolvere il compito, poi ripensaci. E suppongo che l'allocazione temporanea dello spazio per la sezione dell'array, che occuperà tutto lo spazio occupato dall'array stesso, potrebbe dare una mancia al tuo programma oltre il limite e causare il comportamento da te segnalato.

Per inciso, potresti provare la dichiarazione

allocate(isensit%sa(isensit%nnz),source=0.0,stat=ierr)

che dovrebbe, se il compilatore è aggiornato, eseguire l'allocazione e impostare i valori nell'array in un'unica istruzione.

Oh, e un'osservazione completamente gratuita: preferisci use mpi (o use mpi_mod o qualsiasi altra installazione preferisca include mpif.h Questo include mpif.h (molti) errori che potrebbero derivare da chiamate non corrispondenti alle routine mpi con i loro requisiti. la routine significa che il compilatore può controllare la corrispondenza degli argomenti, l'inclusione di un file di intestazione no.

Dato l'esempio di lavoro minimo fornito di seguito, sai perché l'errore di allocazione della memoria non si verifica in fase di allocazione della memoria? Come ho controllato, quando uso valgrind per eseguire il codice, o aggiungo il parametro source = 0.0 all'istruzione di allocazione della memoria, allora ho, come previsto, l'errore di allocazione della memoria.

Aggiornamento: ho riprodotto il problema con un esempio di lavoro minimo:

 program memory_test

  implicit none

  double precision, dimension(:,:,:,:), allocatable :: sensitivity
  double precision, allocatable :: sa(:)
  double precision, allocatable :: sa2(:)

  integer :: ierr,nnz
  integer :: nx,ny,nz,ndata

  nx = 50
  ny = 50
  nz = 100
  ndata = 1600

  allocate(sensitivity(nx,ny,nz,ndata),stat=ierr)

  sensitivity = 1.0

  nnz = 100000000

  !allocate(sa(nnz),source=dble(0.0),stat=ierr)
  allocate(sa(nnz),stat=ierr)
  if(ierr /= 0) print*, 'Memory error!'

  !allocate(sa2(nnz),source=dble(0.0),stat=ierr)
  allocate(sa2(nnz),stat=ierr)
  if(ierr /= 0) print*, 'Memory error!'

  print*, 'Start initialization'

  sa = 0.0
  sa2 = 0.0

  print*, 'End initialization'

end program memory_test

Quando lo eseguo non ho alcun messaggio 'Errore memoria!' stampato, ma con il messaggio "Avvia l'inizializzazione", quindi il programma viene ucciso dal sistema operativo. Se utilizzo l'allocazione di memoria con il parametro 'source' (come commentato nel codice) solo allora ho il messaggio 'Memory error!'.

Per le statistiche della memoria, il comando 'gratuito' mi dà questo risultato:

             total       used       free     shared    buffers     cached
Mem:       8169952    3630284    4539668      46240       1684     124888
-/+ buffers/cache:    3503712    4666240
Swap:            0          0          0

Stai vedendo il comportamento della strategia di allocazione della memoria che usa Linux. Quando si assegna memoria ma non ci si è scritti, essa è contenuta unicamente nella memoria virtuale (si noti che questo potrebbe anche essere influenzato dalla particolare libreria di runtime Fortran, ma non sono sicuro). Questa memoria esiste nello spazio degli indirizzi virtuali del tuo processo, ma non è supportata da alcuna effettiva pagina di memoria fisica. Solo quando scrivi nella memoria le pagine fisiche verranno allocate e solo quanto basta per soddisfare la scrittura.

Considera il seguente programma:

program test
   implicit none
   real,allocatable :: array(:) 

   allocate(array(1000000000)) !4 gb array

   print *,'Check memory - 4 GB allocated'
   read *

   array(1:1000000) = 1.0

   print *,'Check memory - 4 MB assigned'
   read *

   array(1000000:100000000) = 2.0

   print *,'Check memory - 400 MB assigned'
   read *

   array = 5.0

   print *,'Check memory - 4 GB assigned'
   read *

end program

Questo programma alloca 4 GB di memoria, quindi scrive su una sezione di array da 4 MB, una sezione di array da 396 MB (scritture totali = 400 MB) e infine scrive sull'array completo (scritture totali = 4 GB). Il programma si interrompe tra ogni scrittura in modo da poter dare un'occhiata all'utilizzo della memoria.

Dopo l'allocazione, prima della prima scrittura:

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                           
29192 casey     20   0 3921188   1176   1052 S   0.0  0.0   0:00.00 fortranalloc

Tutta la memoria è virtuale (VIRT), solo un piccolo bit è supportato dalla memoria fisica (RES).

Dopo i 4 MB scrivi:

29192 casey     20   0 3921188   5992   1984 S   0.0  0.0   0:00.00 fortranalloc

dopo i 396 MB scrivi:

29192 casey     20   0 3921188 392752   1984 S   0.0  1.6   0:00.18 fortranalloc

e dopo i 4 GB scrivi:

29192 casey     20   0 3921188 3.727g   1984 S  56.6 15.8   0:01.88 fortranalloc 

Notare che dopo ogni scrittura la memoria residente aumenta per soddisfare la scrittura. Questo mostra che l'allocazione fisica effettiva della memoria si verifica solo in scrittura, non solo su allocazione, quindi il normale allocate() non ha modo di rilevare l'errore. Quando si aggiunge il parametro source da allocate si verifica una scrittura e questo provoca l'allocazione fisica completa della memoria e, se ciò non riesce, l'errore può essere rilevato.

Quello che probabilmente vedrete è il Linux OOM Killer che viene invocato quando la memoria è esaurita. Quando ciò si verifica, OOM Killer utilizzerà un algoritmo per determinare cosa uccidere per liberare memoria e il comportamento del codice lo rende un candidato molto probabile da uccidere. Quando la tua scrittura causa l'allocazione fisica che può essere soddisfatta, il tuo processo viene ucciso dal kernel. Lo vedi su scrittura (causata da assegnazione) ma non allocazione a causa del comportamento sopra descritto.





fortran