Fortran: La meilleure façon de chronométrer des sections de votre code?

15

Parfois, tout en optimisant le code, il est nécessaire de chronométrer certaines parties du code, j'utilise les éléments suivants depuis des années, mais je me demandais s'il y avait une manière plus simple / meilleure de le faire?

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)
Oscillation isopycnale
la source

Réponses:

11

Il existe plusieurs autres façons de procéder, avec des avantages et des inconvénients:

  • MPI_WTIME : Il s'agit d'une horloge murale haute résolution. C'est probablement l'option la plus «fiable»; ça marche juste. L'inconvénient est que si votre programme n'utilise pas déjà MPI, vous devrez en entourer MPI (ce qui n'est pas difficile).
  • Utilisez un fortran intrinsèque (comme vous l'avez): c'est probablement le plus simple et généralement suffisant, mais peut ne pas fonctionner aussi bien sur une architecture étrange ou pour des travaux parallèles. Il y a un peu de discussion sur ce débordement de pile
  • Envelopper un appel C: Fortran et C sont compatibles avec les objets, il est donc assez facile d'écrire un wrapper autour des appels C. Un code avec lequel je travaille utilise getrusage, ce qui pourrait être un choix étrange. Il y a de nombreuses discussions à ce sujet sur Stack Overflow.

Ma recommandation personnelle serait MPI_WTIME, comme vous le savez, cela fonctionnera bien partout où il y a MPI. Voici un exemple d'une recherche rapide:

  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
la source
4

Si vous utilisez le compilateur GNU, consultez gprof .

En bref, vous ajouterez l'indicateur -g à votre compilateur, comme ceci:

g77 -g -pg -0 myprogram myprogram.F

Ensuite, exécutez la sortie et un fichier appelé gmon.out apparaîtra dans votre répertoire. Ensuite, appelez

gprof --line myprogram gmon.out

Cela donnera un profil de temps CPU ligne par ligne.

icurays1
la source
Merci pour la réponse, je dois juste préciser que je demandais une solution programmatique. Un profileur est génial mais c'est plus que ce que je demandais.
Oscillation isopycnale le
3
le drapeau est -pg, -gest pour les symboles de débogage (également intéressant, mais pas obligatoire)
RSFalcon7
J'ai entendu à plusieurs endroits que les timings donnés par gprof ne sont pas nécessairement précis, tels que yosefk.com/blog/… , stackoverflow.com/questions/1777556/alternatives-to-gprof/… (et diverses autres réponses de Mike Dunlavey sur Stack Overflow). Des outils comme gprof et kcachegrind sont toujours utiles, dans la mesure où le nombre d'appels de fonction est toujours correct, et ils vous donnent des données de synchronisation, mais je ne les traiterais pas comme du gospel. Le DOE a quelques outils pour cela, mais je ne sais pas s'ils sont meilleurs que l'insertion de minuteries.
Geoff Oxberry
1
Sérieusement, @IsopycnalOscillation essaie d'utiliser le profileur. C'est quelque chose de nouveau à apprendre, mais cela vous aidera énormément (et nettoiera votre code!) À long terme.
tmarthal
merci @tmarthal J'ai déjà utilisé des profileurs et j'en utiliserai certainement un pour mon prochain projet - je suis totalement d'accord avec ce que vous avez dit.
Oscillation isopycnale du
2

Comme mentionné par icurays1, le profilage est le meilleur. Vous pouvez également simplifier légèrement ce qui précède ...

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

où le module utils contient ...

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

Si vous avez plusieurs de ces sections, passez une chaîne, par exemple, "section_id" dans toc afin qu'il imprime l'id / nom avec le timing.

stali
la source
Je suggérerais de ne pas faire t1et t2global, mais plutôt de passer t1en paramètre aux deux fonctions, pour permettre plusieurs minuteries. Vous pouvez également simplement renvoyer l'heure, ne rien imprimer.
Pedro