Fortran: Bester Weg, Teile Ihres Codes zeitlich zu bestimmen?

15

Manchmal, während ich den Code optimiere, ist es erforderlich, bestimmte Teile des Codes zeitlich zu bestimmen. Ich verwende das Folgende seit Jahren, habe mich aber gefragt, ob es einen einfacheren / besseren Weg gibt, dies zu tun?

call system_clock(count_rate=clock_rate) !Find the time rate
call system_clock(count=clock_start)     !Start Timer

call do_something_subroutine             !This is what gets timed

call system_clock(count=clock_stop)      ! Stop Timer

e_time = real(clock_stop-clock_start)/real(clock_rate)
Isopyznale Schwingung
quelle

Antworten:

11

Hierfür gibt es einige andere Möglichkeiten mit Vor- und Nachteilen:

  • MPI_WTIME : Dies ist eine hochauflösende Wanduhr. Es ist wahrscheinlich die "vertrauenswürdigste" Option. es funktioniert einfach. Der Nachteil ist, dass Sie, wenn Ihr Programm MPI nicht bereits verwendet, MPI darum wickeln müssen (was nicht schwer ist).
  • Verwenden Sie eine fortran-Eigenschaft (wie Sie es bereits getan haben): Dies ist wahrscheinlich die einfachste und im Allgemeinen ausreichend, funktioniert jedoch möglicherweise nicht so gut auf einer fremden Architektur oder für parallele Jobs. Es gibt eine kleine Diskussion über diesen Stapelüberlauf
  • Einen C-Aufruf umbrechen: Fortran und C sind objektkompatibel, daher ist es einfach genug, einen Wrapper um C-Aufrufe zu schreiben. Ein Code, mit dem ich arbeite, verwendet getrusage, was eine seltsame Wahl sein könnte. Es gibt viele Diskussionen darüber auf Stapelüberlauf.

Meine persönliche Empfehlung wäre MPI_WTIME, da Sie wissen, dass es überall dort gut funktioniert, wo es MPI gibt. Hier ist ein Beispiel aus einer Schnellsuche:

  include 'mpif.h'
  DOUBLE PRECISION :: start, end
  start = MPI_Wtime()

  ! code to be timed

  end   = MPI_Wtime()
  write(*,*) 'That took ',end-start,' seconds'
Max Hutchinson
quelle
4

Wenn Sie den GNU-Compiler verwenden, schauen Sie sich gprof an .

Kurz gesagt, fügen Sie Ihrem Compiler das Flag -g wie folgt hinzu:

g77 -g -pg -0 myprogram myprogram.F

Führen Sie dann die Ausgabe aus, und eine Datei mit dem Namen gmon.out wird in Ihrem Verzeichnis angezeigt. Dann ruf an

gprof --line myprogram gmon.out

Dies ergibt ein zeilenweises CPU-Zeitprofil.

icurays1
quelle
Danke für die Antwort, ich muss nur klarstellen, dass ich nach einer programmatischen Lösung gefragt habe. Ein Profiler ist großartig, aber er ist mehr als das, wonach ich gefragt habe.
Isopycnal Oscillation
3
Die Flagge ist -pg, -gist für Debug-Symbole (auch interessant, aber nicht erforderlich)
RSFalcon7
Ich habe an mehreren Stellen gehört, dass die von gprof angegebenen Timings nicht unbedingt genau sind, wie z. B. yosefk.com/blog/… , stackoverflow.com/questions/1777556/alternatives-to-gprof/… (und verschiedene andere Antworten von Mike Dunlavey) auf Stapelüberlauf). Tools wie gprof und kcachegrind sind immer noch nützlich, da die Anzahl der Funktionsaufrufe immer noch korrekt ist und Sie einige Timing-Daten erhalten, aber ich würde sie nicht als Evangelium behandeln. Das DOE hat einige Werkzeuge dafür, aber ich weiß nicht, ob sie besser sind als das Einfügen von Timern.
Geoff Oxberry
1
Im Ernst, @IsopycnalOscillation versuchen, den Profiler zu verwenden. Es ist etwas Neues zu lernen, aber es wird Ihnen auf lange Sicht enorm helfen (und Ihren Code bereinigen!).
tmarthal
danke @tmarthal Ich habe zuvor schon Profiler verwendet und werde definitiv einen für mein nächstes Projekt verwenden - ich stimme voll und ganz mit dem überein, was Sie gesagt haben.
Isopycnal Oscillation
2

Wie von icurays1 erwähnt, ist die Profilerstellung am besten. Sie können das oben genannte auch etwas vereinfachen ...

use utils
...
call tic()
   ! Section to be timed
call toc()
...
call tic()
   ! Section to be timed
call toc()
...

wo das utils Modul enthält ...

real(8) :: t1,t2
...
subroutine tic()
  implicit none
  call cpu_time(t1)
end subroutine tic

subroutine toc()
  implicit none
  call cpu_time(t2)
  ! if (rank==0) print*,"Time Taken -->", real(t2-t1)
  print*,"Time Taken -->", real(t2-t1)
end subroutine toc

Wenn Sie viele solcher Abschnitte haben, übergeben Sie eine Zeichenfolge, z. B. "section_id" in toc, damit die ID / der Name zusammen mit dem Timing gedruckt wird.

stali
quelle
Ich würde vorschlagen, nicht zu machen t1und t2global, sondern vorbeit1 als Parameter an beide Funktionen zu übergeben, um mehrere Timer zu ermöglichen. Sie können auch einfach die Uhrzeit zurückgeben und nichts drucken.
Pedro