2015-09-21 2 views
1

Я написал научный код, и, как обычно, это сводится к вычислению коэффициентов в алгебраическом уравнении на собственные значения: вычисление этих коэффициентов требует интегрирования по многомерным массивам, что резко увеличивает интенсивность использования памяти. После вычисления матричных коэффициентов исходные, предварительно интегрированные многомерные массивы могут быть освобождены, а интеллектуальные решатели возьмут верх, поэтому использование памяти перестает быть большой проблемой. Как вы можете видеть, есть узкое место, а на моем 64-битном, 4-ядерном, 8 потоках, 8 ГБ оперативной памяти, программа вылетает из-за недостаточной памяти.Почему Fortran выделяет такие большие массивы?

Поэтому я внедряю систему, которая контролирует использование памяти, ограничивая размер задач, которые процессы MPI могут принимать при вычислении некоторых элементов матрицы собственных значений. По завершении они будут искать оставшиеся задания, которые будут выполняться, чтобы матрица все еще заполнялась, но была более последовательной и менее параллельной.

Поэтому я проверял, сколько памяти я могу выделить, и вот где начинается путаница: я выделяю парные разряды размером 8 байт (проверяется с помощью sizeof(1)) и смотрю на статус распределения.

Хотя у меня есть 8 ГБ оперативной памяти, работающей в тесте только с одним процессом, я могу выделить массив размером до (40000,40000), что соответствует примерно 13 ГБ памяти! Мой первый вопрос заключается в следующем: как это возможно? Существует ли столько виртуальной памяти?

Во-вторых, я понял, что я тоже могу сделать то же самое для нескольких процессов: до процессов может одновременно выделить эти массивные массивы!

Возможно, это не так?

Кто-нибудь понимает, почему это происходит? И я делаю что-то неправильно?

Edit:

Вот код, который производит вышеупомянутое чудо, по крайней мере, на моей машине. Однако, когда я устанавливаю элементы массивов на какое-то значение, он действительно ведет себя так, как должен, и сбой - или, по крайней мере, начинает вести себя очень медленно, что, я думаю, связано с тем, что используется медленная виртуальная память?

program test_miracle 
    use ISO_FORTRAN_ENV 
    use MPI 

    implicit none 

    ! global variables 
    integer, parameter :: dp = REAL64           ! double precision 
    integer, parameter :: max_str_ln = 120          ! maximum length of filenames 
    integer :: ierr                ! error variable 
    integer :: n_procs               ! MPI nr. of procs 

    ! start MPI 
    call MPI_init(ierr)               ! initialize MPI 
    call MPI_Comm_size(MPI_Comm_world,n_procs,ierr)        ! nr. MPI processes 
    write(*,*) 'RUNNING MPI WITH', n_procs, 'processes' 

    ! call asking for 6 GB 
    call test_max_memory(6000._dp) 
    call MPI_Barrier(MPI_Comm_world,ierr) 

    ! call asking for 13 GB 
    call test_max_memory(13000._dp) 
    call MPI_Barrier(MPI_Comm_world,ierr) 

    ! call asking for 14 GB 
    call test_max_memory(14000._dp) 
    call MPI_Barrier(MPI_Comm_world,ierr) 

    ! stop MPI 
    call MPI_finalize(ierr) 

contains 
    ! test whether maximum memory feasible 
    subroutine test_max_memory(max_mem_per_proc) 
     ! input/output 
     real(dp), intent(in) :: max_mem_per_proc        ! maximum memory per process 

     ! local variables 
     character(len=max_str_ln) :: err_msg         ! error message 
     integer :: n_max              ! maximum size of array 
     real(dp), allocatable :: max_mem_arr(:,:)        ! array with maximum size 
     integer :: ierr               ! error variable 

     write(*,*) ' > Testing whether maximum memory per process of ',& 
      &max_mem_per_proc/1000, 'GB is possible' 

     n_max = ceiling(sqrt(max_mem_per_proc/(sizeof(1._dp)*1.E-6))) 

     write(*,*) ' * Allocating doubles array of size', n_max 

     allocate(max_mem_arr(n_max,n_max),STAT=ierr) 
     err_msg = ' * cannot allocate this much memory. Try setting & 
      &"max_mem_per_proc" lower' 
     if (ierr.ne.0) then 
      write(*,*) err_msg 
      stop 
     end if 

     !max_mem_arr = 0._dp              ! UNCOMMENT TO MAKE MIRACLE DISSAPEAR 


     deallocate(max_mem_arr) 

     write(*,*) ' * Maximum memory allocatable' 
    end subroutine test_max_memory 
end program test_miracle 

Чтобы быть сохранены в test.f90, а затем компиляции и запуска с

mpif90 test.f90 -o test && mpirun -np 2 ./test 
+2

Не могли бы вы опубликовать минимальный рабочий код, который воспроизводит это чудо? – francis

+9

Попробуйте установить всю память в ноль - то есть массив = 0.0. Что происходит тогда? Я подозреваю, что это проявление http://stackoverflow.com/questions/864416/are-some-allocators-lazy, которое для HPC в основном является болью в пресловутой ... –

+1

@IanBush Я очень уверен, что это точно проблема. Фактически, я столкнулся с этим вопросом сегодня. OP, если, как только вы выделите память, вы попытаетесь установить ее на 0.0, она, несомненно, сбой. – NoseKnowsAll

ответ

4

Когда вы делаете allocate заявление, вы бронируете домен в виртуальном пространстве памяти. Виртуальное пространство представляет собой сумму физической памяти + своп +, возможно, какое-то дополнительное возможное пространство из-за некоторого overcommit возможность, которая предположит, что вы не будете использовать все оговорки.

Но память еще не сохранена до тех пор, пока вы ничего не напишете в ней. Когда вы что-то записываете в память, система будет физически распределять соответствующую страницу для вас. Если вы не инициализируете свой массив, и если ваш массив очень разрежен, возможно, что существует много страниц, которые никогда не записываются, поэтому память никогда физически не используется полностью.

Когда вы видите, что система замедляется, может случиться так, что система переводит страницы на диск, потому что физическая память заполнена.Если у вас есть 8 ГБ оперативной памяти и 8 ГБ на диске, ваши вычисления могут выполняться (очень медленно thow ...)

Этот механизм довольно хорош в средах NUMA, так как эта политика «первого касания» будет выделять память, близкую к CPU, который сначала записывает в него. Таким образом, вы можете инициализировать массив в цикле OpenMP, чтобы физически разместить память рядом с процессорами, которые будут ее использовать.

+0

Спасибо. Это прекрасно объясняет мой опыт. – Toon

Смежные вопросы