Производительность выделяемых массивов - PullRequest
3 голосов
/ 09 сентября 2011

Существует mpi-версия программы, которая использует блоки COMMON для хранения массивов, которые повсеместно используются в коде. К сожалению, нет способа объявить массивы в ОБЩЕМ блоке, размер которого будет известен только во время выполнения. Итак, в качестве обходного пути я решил переместить эти массивы в модули, которые принимают ALLOCATABLE массивы внутри. То есть все массивы в ОБЩИХ блоках исчезли, вместо этого использовался ALLOCATE. Так что это было единственное, что я изменил в своей программе. К сожалению, производительность программы была ужасной (по сравнению с реализацией ОБЩИХ блоков). Что касается mpi-настроек, то на каждом вычислительном узле есть один mpi-процесс, и каждый mpi-процесс имеет один поток. Я нашел похожий вопрос, заданный здесь, но не думаю (не понимаю :)), как его можно применить к моему случаю (где каждый процесс имеет одну нить). Я ценю любую помощь.

Вот простой пример, который иллюстрирует то, о чем я говорил (ниже псевдокод):

"ИСТОЧНИК ФАЙЛ":

SUBROUTINE ZEROSET()
   INCLUDE 'FILE_1.INC'
   INCLUDE 'FILE_2.INC'
   INCLUDE 'FILE_3.INC'
   ....
   INCLUDE 'FILE_N.INC'

   ARRAY_1 = 0.0
   ARRAY_2 = 0.0
   ARRAY_3 = 0.0
   ARRAY_4 = 0.0
   ...
   ARRAY_N = 0.0
END SUBROUTINE

Как вы можете видеть, ZEROSET () не имеет параллельных или MPI-компонентов. FILE_1.INC, FILE_2, ..., FILE_N.INC - это файлы, в которых ARRAY_1, ARRAY_2 ... ARRAY_N определены в блоках COMMON. Нечто подобное

REAL ARRAY_1
COMMON /ARRAY_1/ ARRAY_1(NX, NY, NZ)

Где NX, NY, NZ - это четко определенные параметры, описанные с помощью директивы PARAMETER. Когда я использую модули, я просто уничтожил все ОБЩИЕ блоки, поэтому FILE_I.INC выглядит как

REAL, ALLOCATABLE:: ARRAY_I(:,:,:)

А затем просто изменил приведенный выше оператор "INCLUDE 'FILE_I.INC" на "USE FILE_I". На самом деле, когда выполняется параллельная программа, одному конкретному процессу не требуется целый домен (NX, NY, NZ), поэтому я вычисляю параметры и затем выделяю ARRAY_I (только ОДИН РАЗ!).

Подпрограмма ZEROSET () выполняется 0,18 секунды с блоками COMMON и 0,36 с модулями (когда размеры массива вычисляются во время выполнения). Итак, производительность ухудшалась в два раза.

Я надеюсь, что теперь все ясно. Я очень ценю вашу помощь.

Ответы [ 3 ]

3 голосов
/ 09 сентября 2011

Использование выделяемых массивов в модулях часто может снизить производительность, поскольку компилятор не имеет представления о размерах во время компиляции. Вы получите намного лучшую производительность со многими компиляторами с этим кодом:

   subroutine X
   use Y  ! Has allocatable array A(N,N) in it
   call Z(A,N)
   end subroutine

   subroutine Z(A,N)
   Integer N
   real A(N,N)
   do stuff here
   end

Тогда этот код:

   subroutine X
   use Y  ! Has allocatable array A(N,N) in it
   do stuff here
   end subroutine

Компилятор будет знать, что массив равен NxN, а циклы do превышают N, и сможет воспользоваться этим фактом (большинство кодов работают таким образом на массивах). Кроме того, после любых вызовов подпрограммы в «do stuff here» компилятор должен будет предположить, что массив «A» мог изменить размеры или переместить места в памяти и перепроверить. Это убивает оптимизацию.

Это должно вернуть вам большую часть вашего выступления.

Общие блоки также находятся в определенном месте в памяти, что также позволяет оптимизировать.

0 голосов
/ 10 сентября 2011

На самом деле, я полагаю, ваша проблема здесь, в сочетании со стеком и кучей памяти, действительно основана на оптимизации компилятора.В зависимости от того, какой компилятор вы используете, он может выполнять более эффективную блокировку памяти, а для фиксированного фрагмента памяти даже не требуется проверять его размер и расположение в подпрограмме.Таким образом, в массивах фиксированного размера не будет почти никаких накладных расходов.Эта процедура вызывается очень часто, или почему вы заботитесь об этих 0,18 с?Если это действительно актуально, лучшим вариантом было бы вообще избавиться от настройки 0 и вместо этого, например, отделить первый цикл итерации и использовать его для инициализации, таким образом, вам не нужно вводить дополнительные обращения к памяти, простодля инициализации с 0. Однако это будет дублировать некоторый код ...

0 голосов
/ 09 сентября 2011

Я мог бы подумать только о следующих причинах, когда речь идет о производительности на Fortran с использованием массивов:

  1. массивы в стеке и куча VS, но я сомневаюсь, что это может оказать огромное влияние на производительность.
  2. передача массивов в подпрограмму, потому что лучший способ сделать это зависит от массива, см. Эту страницу на с использованием массивов
...