W tym przykładowym programie robię to samo (przynajmniej tak mi się wydaje) na dwa różne sposoby. Korzystam z tego na komputerze z systemem Linux i monitoruję użycie pamięci za pomocą top. Korzystając z gfortran, stwierdzam, że w pierwszy sposób (między „1” a „2”) używana pamięć wynosi 8,2 GB, podczas gdy w drugim sposobie (między „2” i „3”) zużycie pamięci wynosi 3,0 GB. W przypadku kompilatora Intel różnica jest jeszcze większa: 10 GB w porównaniu do 3 GB. Wydaje się to nadmierną karą za używanie wskaźników. Dlaczego to się dzieje?
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
Tłem jest lokalne udoskonalenie sieci. Wybrałem połączoną listę, aby łatwo dodawać i usuwać twarze. Domyślnie liczba węzłów wynosi 4, ale może wzrosnąć w zależności od lokalnych ulepszeń.
performance
fortran
Chris
źródło
źródło
Odpowiedzi:
Właściwie nie wiem, jak działają kompilatory fortran, ale w oparciu o funkcje językowe mogę zgadywać.
Dynamiczne tablice w Fortranie są dostarczane z meta-danymi do pracy z funkcjami wewnętrznymi, takimi jak kształt, rozmiar, łączenie, ubound oraz przydzielane lub powiązane (przydzielane vs wskaźniki). W przypadku dużych tablic rozmiar metadanych jest znikomy, ale w przypadku małych tablic, tak jak w twoim przypadku, może się sumować. W twoim przypadku tablice dynamiczne rozmiaru 4 prawdopodobnie zawierają więcej metadanych niż dane rzeczywiste, co prowadzi do powstania balonu użycia pamięci.
Zdecydowanie odradzam pamięć dynamiczną na dole twoich struktur. Jeśli piszesz kod dotyczący systemów fizycznych w pewnej liczbie wymiarów, możesz ustawić go jako makro i ponownie skompilować. Jeśli masz do czynienia z wykresami, możesz statycznie przypisać górną granicę liczby krawędzi lub polubień. Jeśli masz do czynienia z systemem, który faktycznie potrzebuje drobnoziarnistej dynamicznej kontroli pamięci, prawdopodobnie najlepiej jest przejść do C.
źródło
n
wskaźników wymaganych przez pierwszą metodę.Jak wskazał maxhutch , problemem jest prawdopodobnie sama liczba oddzielnych przydziałów pamięci. Oprócz metadanych prawdopodobnie są to jednak wszelkie dodatkowe dane i wyrównanie, których potrzebuje menedżer pamięci, tzn. Prawdopodobnie zaokrągla każdą alokację do pewnej wielokrotności 64 bajtów lub więcej.
Aby uniknąć przydzielania małej porcji dla każdego węzła, możesz spróbować przydzielić każdemu węzłowi część wstępnie przydzielonej tablicy:
Mój Fortran jest trochę zardzewiały, ale powyższe powinno działać, jeśli nie w zasadzie.
Nadal będziesz mieć koszty ogólne tego, co kompilator Fortran uważa za potrzebne do przechowywania typu POINTER, ale nie będziesz mieć kosztów ogólnych związanych z menedżerem pamięci.
źródło
nodesType%nodes
jest wskaźnikiem do tablicy dynamicznej.O. To ten sam problem, który cierpiałem. To pytanie jest bardzo stare, ale sugeruję nieco inny styl kodu. Moim problemem była tablica instrukcji do przydzielenia w pochodnym typie danych, jak kod podążający.
Na podstawie niektórych testów potwierdziłem, że jeśli użyję instrukcji alokowalnej lub instrukcji wskaźnikowej w typie pochodnym jako kodu podążającego o czterech przypadkach, wyciek pamięci stanie się bardzo duży. W moim przypadku czerwony plik o rozmiarze 520 MB. Ale użycie pamięci wyniosło 4 GB w trybie zwolnienia na kompilatorze Intel Fortran. To 8 razy większy!
Wyciek pamięci nie występuje, gdy używam instrukcji alokowalnej lub wskaźnika bez typu pochodnego. Moim zdaniem, jeśli zadeklaruję zmienną typu możliwego do przydzielenia lub zmienną typu wskaźnikowego w typie pochodnym, a dużą zmienną typu pochodnego zmienną typu niemożliwą do przydzielenia w typie pochodnym, nastąpi wyciek pamięci. Aby rozwiązać ten problem, zmieniłem kod, który nie zawiera typu pochodnego jako kodu podążającego.
a może ten styl?
Zmienna NumNodes oznacza liczbę węzłów na każdej powierzchni, a zmienna Węzeł to numery węzłów pasujące do zmiennej NumNodes. Może w tym stylu kodu nie występuje wyciek pamięci.
źródło