В этом примере программы я делаю одно и то же (по крайней мере, мне так кажется) двумя разными способами. Я запускаю это на своем компьютере с Linux и наблюдаю за использованием памяти с помощью top. Используя gfortran, я обнаружил, что в первом случае (между «1» и «2») используемая память составляет 8,2 ГБ, а во втором (между «2» и «3») использование памяти составляет 3,0 ГБ. С компилятором Intel разница еще больше: 10 ГБ против 3 ГБ. Это кажется чрезмерным штрафом за использование указателей. Почему это происходит?
program test
implicit none
type nodesType
integer:: nnodes
integer,dimension(:),pointer:: nodes
end type nodesType
type nodesType2
integer:: nnodes
integer,dimension(4):: nodes
end type nodesType2
type(nodesType),dimension(:),allocatable:: FaceList
type(nodesType2),dimension(:),allocatable:: FaceList2
integer:: n,i
n = 100000000
print *, '1'
read(*,*)
allocate(FaceList(n))
do i=1,n
FaceList(i)%nnodes = 4
allocate(FaceList(i)%nodes(4))
FaceList(i)%nodes(1:4) = (/1,2,3,4/)
end do
print *, '2'
read(*,*)
do i=1,n
deallocate(FaceList(i)%nodes)
end do
deallocate(FaceList)
allocate(FaceList2(n))
do i=1,n
FaceList2(i)%nnodes = 4
FaceList2(i)%nodes(1:4) = (/1,2,3,4/)
end do
print *, '3'
read(*,*)
end program test
Фоном является локальное уточнение сетки. Я выбрал связанный список, чтобы легко добавлять и удалять лица. Количество узлов по умолчанию равно 4, но может увеличиться в зависимости от локальных уточнений.
performance
fortran
Крис
источник
источник
Ответы:
На самом деле я не знаю, как работают компиляторы фортрана, но, основываясь на особенностях языка, я могу предположить.
Динамические массивы в Фортране поставляются с метаданными для работы со встроенными функциями, такими как форма, размер, привязка, выделение и выделение или связывание (распределение по указателям). Для больших массивов размер метаданных незначителен, но для крошечных массивов, как в вашем случае, он может сложиться. В вашем случае динамические массивы размера 4, скорее всего, содержат больше метаданных, чем реальных данных, что приводит к увеличению объема используемой памяти.
Я настоятельно рекомендую против динамической памяти в нижней части ваших структур. Если вы пишете код, который имеет дело с физическими системами в некотором количестве измерений, вы можете установить его как макрос и перекомпилировать. Если вы имеете дело с графиками, вы можете статически выделить верхнюю границу для количества ребер или лайков. Если вы имеете дело с системой, которая действительно нуждается в детальном динамическом управлении памятью, то, вероятно, лучше всего перейти на C.
источник
n
указателей, необходимых для первого метода.Как указал maxhutch , проблема, вероятно, заключается в большом количестве отдельных выделений памяти. Однако, помимо метаданных, вероятно, есть какие-то дополнительные данные и выравнивание, которые необходимы менеджеру памяти, то есть он, вероятно, округляет каждое выделение до некоторого кратного 64 байтам или более.
Чтобы избежать выделения небольшого фрагмента для каждого узла, вы можете попытаться выделить каждому узлу часть предварительно выделенного массива:
Мой Фортран немного ржавый, но вышеперечисленное должно работать, если не в принципе.
Вы по-прежнему будете иметь накладные расходы на то, что ваш компилятор Фортрана считает необходимым хранить для типа POINTER, но у вас не будет накладных расходов менеджера памяти.
источник
nodesType%nodes
является указателем на динамический массив.Ой. Это та же проблема, что и у меня. Этот вопрос очень старый, но я предлагаю немного другой стиль кода. Моя проблема заключалась в размещаемом массиве операторов в производном типе данных, как следует из кода.
Из какого-то теста я подтвердил, что, если я использовал размещаемый оператор или оператор указателя в производном типе, как следует из кода, описывающего четыре случая, утечка памяти происходит очень сильно. В моем случае я красный файл размером 520 МБ. Но использование памяти в режиме релиза на Intel Fortran Complier составляло 4 ГБ. Это в 8 раз больше!
Утечка памяти не происходит, когда я использую выделение или указатель без производного типа. По моему мнению, если я объявляю переменную распределенного типа или указателя типа в производном типе, а большое выделяет переменную производного типа, а не переменную распределенного типа в производном типе, происходит утечка памяти. Чтобы решить эту проблему, я изменил свой код, который не включает производный тип, как следующий код.
или как насчет этого стиля?
Переменная NumNodes означает количество узлов на каждой грани, а переменная Node - это номера узлов, соответствующие переменной NumNodes. Возможно утечка памяти не произошла в этом стиле кода, я думаю.
источник