Fortran: динамические массивы против автоматического массива. Избегание выделения памяти

Профилируя один из наших кодов фортрана, есть две подпрограммы, которые занимают большую часть вычислительного времени (22,1% и 17,2%). В каждой подпрограмме ~5% времени тратится на выделение и освобождение памяти. Эти процедуры выглядят как

MODULE foo

CONTAINS

SUBROUTINE bar( ... )
...
IMPLICIT NONE
...
REAL, ALLOCATABLE, DIMENSION(:,:) :: work
...
ALLOCATE (work(size1,size2))
...
DEALLOCATE (work)
END SUBROUTINE bar
...
END MODULE foo

Эти подпрограммы вызываются порядка 4000-5000 раз в моем тесте, поэтому я хотел бы избавиться от ALLOCATE и DEALLOCATE. Изменение этих параметров на автоматические массивы приводит к изменению вывода профилировщика на.

MODULE foo

CONTAINS

SUBROUTINE bar( ... )
...
IMPLICIT NONE
...
REAL, DIMENSION(size1,size2) :: work
...
END SUBROUTINE bar
...
END MODULE foo

Изменяет результирующий профиль на

Running Time        Symbol Name
20955.0ms  17.0%    __totzsp_mod_MOD_totzsps
    7.0ms   0.0%        malloc
    5.0ms   0.0%        free
    2.0ms   0.0%        user_trap

16192.0ms  13.2%    __tomnsp_mod_MOD_tomnsps
   20.0ms   0.0%        free
    3.0ms   0.0%        malloc
    1.0ms   0.0%        szone_size_try_large

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

Второй подход, который я использую, состоит в том, чтобы выделить и освободить эти массивы один раз.

work_array.f

MODULE work_array
IMPLICIT NONE

REAL(rprec), ALLOCATABLE, DIMENSION(:,:) :: work

END MODULE work_array

Я выделяю их один раз в другой части кода. Теперь моя подпрограмма выглядит так

MODULE foo

CONTAINS

SUBROUTINE bar( ... )
...
USE work_array
IMPLICIT NONE
...
END SUBROUTINE bar
...
END MODULE foo

Однако, когда я запускаю код, профиль ухудшается.

Running Time        Symbol Name
30584.0ms  21.6%    __totzsp_mod_MOD_totzsps
 3494.0ms   2.4%        free
 3143.0ms   2.2%        malloc
   27.0ms   0.0%        DYLD-STUB$$malloc_zone_malloc
   19.0ms   0.0%        szone_free_definite_size
    6.0ms   0.0%        malloc_zone_malloc

24325.0ms  17.1%    __tomnsp_mod_MOD_tomnsps
 2937.0ms   2.0%        free
 2456.0ms   1.7%        malloc
   23.0ms   0.0%        DYLD-STUB$$malloc_zone_malloc
    3.0ms   0.0%        szone_free_definite_size

Откуда эти лишние мальлоки и освобождения? Как я могу настроить это так, чтобы я выделил эти массивы один раз?

2 ответа

Так как work массив используется только внутри bar подпрограмму, вы можете добавить save приписать его и выделить его, когда подпрограмма вызывается в первый раз. Если work1 или же work2 отличается от предыдущих вызовов, в этом случае вы можете просто перераспределить массив.

Это оставляет проблему освобождения, когда подпрограмма больше не нужна. Если вам нужно вызывать его в течение всего времени жизни программы, это не проблема, так как ОС должна освободить память при выходе из программы. С другой стороны, если вам это нужно только во время инициализации, память останется выделенной, даже если она не нужна. Может быть, вы можете добавить в подпрограмму аргумент, который говорит ей освободить work массив, если использование памяти является проблемой.

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

Если вам нужен только фиксированный размер, но вы не знаете этот размер до времени выполнения, вам нужно будет использовать окончательный вариант - одно выделение при инициализации. Однако не имеет смысла, что это увеличило удар производительности распределения. Мне нужно увидеть определение и код распределения, чтобы сказать больше.

Поскольку выделяется виртуальная память, "использование памяти" на самом деле не является проблемой, если только массив не настолько велик, что влияет на доступное адресное пространство.

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