In diesem Beispielprogramm mache ich dasselbe (zumindest denke ich das) auf zwei verschiedene Arten. Ich führe dies auf meinem Linux-PC aus und überwache die Speichernutzung mit top. Bei der Verwendung von gfortran stelle ich fest, dass auf die erste Weise (zwischen "1" und "2") der verwendete Speicher 8,2 GB beträgt, während auf die zweite Weise (zwischen "2" und "3") die Speichernutzung 3,0 GB beträgt. Beim Intel-Compiler ist der Unterschied sogar noch größer: 10 GB gegenüber 3 GB. Dies scheint eine übermäßige Strafe für die Verwendung von Zeigern zu sein. Warum passiert das?
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
Hintergrund ist die lokale Netzverfeinerung. Ich habe die verknüpfte Liste ausgewählt, um Gesichter einfach hinzuzufügen und zu entfernen. Die Anzahl der Knoten ist standardmäßig 4, kann jedoch abhängig von den lokalen Verfeinerungen höher werden.
quelle
Antworten:
Eigentlich weiß ich nicht, wie Fortran-Compiler funktionieren, aber ich kann anhand der Sprachfunktionen raten.
Dynamische Arrays in fortran werden mit Metadaten geliefert, um mit Funktionen wie Form, Größe, Lbound, Ubound und Zugewiesen oder Zugeordnet (zuweisbar gegen Zeiger) zu arbeiten. Bei großen Arrays ist die Größe der Metadaten vernachlässigbar, bei kleinen Arrays kann sie sich jedoch wie in Ihrem Fall summieren. In Ihrem Fall haben die dynamischen Arrays der Größe 4 wahrscheinlich mehr Metadaten als echte Daten, was zu Ihrer Speicherauslastung führt.
Ich würde dringend gegen dynamische Speicher am unteren Rand Ihrer Strukturen empfehlen. Wenn Sie einen Code schreiben, der sich mit physischen Systemen in einer Reihe von Dimensionen befasst, können Sie ihn als Makro festlegen und neu kompilieren. Wenn Sie sich mit Diagrammen beschäftigen, können Sie statisch eine Obergrenze für die Anzahl der Kanten oder dergleichen zuweisen. Wenn Sie es mit einem System zu tun haben, das tatsächlich eine feinkörnige dynamische Speichersteuerung benötigt, ist es wahrscheinlich am besten, auf C zu wechseln.
quelle
n
Zeigern, die von der ersten Methode benötigt werden.Wie Maxhutch herausgestellt hat, liegt das Problem wahrscheinlich in der bloßen Anzahl separater Speicherzuordnungen. Zusätzlich zu den Metadaten gibt es wahrscheinlich alle zusätzlichen Daten und Ausrichtungen, die der Speichermanager benötigt, dh, es wird wahrscheinlich jede Zuordnung auf ein Vielfaches von 64 Bytes oder mehr aufgerundet.
Um zu vermeiden, dass jedem Knoten ein kleiner Block zugewiesen wird , können Sie versuchen, jedem Knoten einen Teil eines zuvor zugewiesenen Arrays zuzuweisen:
Mein Fortran ist ein bisschen rostig, aber das obige sollte funktionieren, wenn nicht im Prinzip.
Sie haben immer noch den Overhead von allem, was Ihr Fortran-Compiler für einen POINTER-Typ benötigt, aber Sie haben nicht den Overhead für den Speichermanager.
quelle
nodesType%nodes
ist ein Zeiger auf ein dynamisches Array.Oh. Das ist das gleiche Problem, unter dem ich gelitten habe. Diese Frage ist sehr alt, aber ich schlage einen etwas anderen Codestil vor. Mein Problem war zuweisbares Anweisungsarray im abgeleiteten Datentyp, wie folgt Code.
Bei einigen Tests habe ich bestätigt, dass ein Speicherverlust sehr groß ist, wenn ich die zuweisbare Anweisung oder die Zeigeranweisung im abgeleiteten Typ wie im folgenden Code für vier Fälle verwendet habe. In meinem Fall habe ich die Datei mit einer Größe von 520 MB rot. Die Speicherauslastung betrug jedoch 4 GB im Release-Modus auf Intel Fortran-kompatiblen Rechnern. Das ist 8 mal größer!
Ein Speicherverlust tritt nicht auf, wenn ich eine zuweisbare Anweisung oder eine Zeigeranweisung ohne abgeleiteten Typ verwende. Wenn ich meiner Meinung nach die Variable des zuweisbaren Typs oder des Zeigertyps als abgeleiteten Typ deklariere und die Variable des abgeleiteten Typs als nicht zuweisbare Variable im abgeleiteten Typ groß reserviere, tritt ein Speicherleck auf. Um dieses Problem zu lösen, habe ich meinen Code, der keinen abgeleiteten Typ enthält, wie folgt geändert.
oder wie wäre es mit diesem Stil?
NumNodes-Variable bedeutet die Anzahl der Knoten auf jeder Fläche und Node-Variable die Knotennummern, die mit NumNodes-Variablen übereinstimmen. Vielleicht ist in diesem Codestil kein Speicherverlust aufgetreten, denke ich.
quelle