LCOV - code coverage report
Current view: top level - src/common - timings_report.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 88.1 % 160 141
Test Date: 2026-07-25 06:35:44 Functions: 100.0 % 4 4

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2026 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : !> \brief Timing routines for accounting
      10              : !> \par History
      11              : !>      02.2004 made a stacked version (of stacks...) [Joost VandeVondele]
      12              : !>      11.2004 storable timer_envs (for f77 interface) [fawzi]
      13              : !>      10.2005 binary search to speed up lookup in timeset [fawzi]
      14              : !>      12.2012 Complete rewrite based on dictionaries. [ole]
      15              : !>      01.2014 Collect statistics from all MPI ranks. [ole]
      16              : !> \author JGH
      17              : ! **************************************************************************************************
      18              : MODULE timings_report
      19              :    USE callgraph,                       ONLY: callgraph_item_type,&
      20              :                                               callgraph_items
      21              :    USE cp_files,                        ONLY: close_file,&
      22              :                                               open_file
      23              :    USE kinds,                           ONLY: default_string_length,&
      24              :                                               dp,&
      25              :                                               int_8
      26              :    USE list,                            ONLY: list_destroy,&
      27              :                                               list_get,&
      28              :                                               list_init,&
      29              :                                               list_isready,&
      30              :                                               list_pop,&
      31              :                                               list_push,&
      32              :                                               list_size
      33              :    USE list_routinereport,              ONLY: list_routinereport_type
      34              :    USE message_passing,                 ONLY: mp_para_env_type
      35              :    USE routine_map,                     ONLY: routine_map_get,&
      36              :                                               routine_map_haskey
      37              :    USE timings,                         ONLY: get_timer_env
      38              :    USE timings_base_type,               ONLY: call_stat_type,&
      39              :                                               routine_report_type,&
      40              :                                               routine_stat_type
      41              :    USE timings_types,                   ONLY: timer_env_type
      42              :    USE util,                            ONLY: sort
      43              : #include "../base/base_uses.f90"
      44              : 
      45              :    IMPLICIT NONE
      46              :    PRIVATE
      47              : 
      48              :    INTEGER, PUBLIC, PARAMETER :: cost_type_time = 17, cost_type_energy = 18
      49              : 
      50              :    PUBLIC :: timings_report_print, timings_report_callgraph
      51              : 
      52              : CONTAINS
      53              : 
      54              : ! **************************************************************************************************
      55              : !> \brief Print accumulated information on timers
      56              : !> \param iw ...
      57              : !> \param r_timings ...
      58              : !> \param sort_by_self_time ...
      59              : !> \param cost_type ...
      60              : !> \param report_maxloc ...
      61              : !> \param para_env is needed to collect statistics from other nodes.
      62              : !> \par History
      63              : !>      none
      64              : !> \author JGH
      65              : ! **************************************************************************************************
      66        11087 :    SUBROUTINE timings_report_print(iw, r_timings, sort_by_self_time, cost_type, report_maxloc, para_env)
      67              :       INTEGER, INTENT(IN)                                :: iw
      68              :       REAL(KIND=dp), INTENT(IN)                          :: r_timings
      69              :       LOGICAL, INTENT(IN)                                :: sort_by_self_time
      70              :       INTEGER, INTENT(IN)                                :: cost_type
      71              :       LOGICAL, INTENT(IN)                                :: report_maxloc
      72              :       TYPE(mp_para_env_type), INTENT(IN)                 :: para_env
      73              : 
      74              :       TYPE(list_routinereport_type)                      :: reports
      75              :       TYPE(routine_report_type), POINTER                 :: r_report
      76              : 
      77        11087 :       CALL list_init(reports)
      78        11087 :       CALL collect_reports_from_ranks(reports, cost_type, para_env)
      79              : 
      80        11087 :       IF (list_size(reports) > 0 .AND. iw > 0) THEN
      81         5649 :          CALL print_reports(reports, iw, r_timings, sort_by_self_time, cost_type, report_maxloc, para_env)
      82              :       END IF
      83              : 
      84              :       ! deallocate reports
      85      4577487 :       DO WHILE (list_size(reports) > 0)
      86      4566400 :          r_report => list_pop(reports)
      87      4566400 :          DEALLOCATE (r_report)
      88              :       END DO
      89        11087 :       CALL list_destroy(reports)
      90              : 
      91        11087 :    END SUBROUTINE timings_report_print
      92              : 
      93              : ! **************************************************************************************************
      94              : !> \brief Collects the timing or energy reports from all MPI ranks.
      95              : !> \param reports ...
      96              : !> \param cost_type ...
      97              : !> \param para_env ...
      98              : !> \author Ole Schuett
      99              : ! **************************************************************************************************
     100        11087 :    SUBROUTINE collect_reports_from_ranks(reports, cost_type, para_env)
     101              :       TYPE(list_routinereport_type), INTENT(INOUT)       :: reports
     102              :       INTEGER, INTENT(IN)                                :: cost_type
     103              :       TYPE(mp_para_env_type), INTENT(IN)                 :: para_env
     104              : 
     105              :       CHARACTER(LEN=default_string_length)               :: routineN
     106              :       INTEGER                                            :: local_routine_id, sending_rank
     107        11087 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: collected
     108              :       REAL(KIND=dp)                                      :: foobar
     109              :       REAL(KIND=dp), DIMENSION(2)                        :: dbuf
     110              :       TYPE(routine_report_type), POINTER                 :: r_report
     111              :       TYPE(routine_stat_type), POINTER                   :: r_stat
     112              :       TYPE(timer_env_type), POINTER                      :: timer_env
     113              : 
     114        11087 :       NULLIFY (r_stat, r_report, timer_env)
     115        11087 :       IF (.NOT. list_isready(reports)) THEN
     116            0 :          CPABORT("BUG")
     117              :       END IF
     118              : 
     119        11087 :       timer_env => get_timer_env()
     120              : 
     121              :       ! make sure all functions have been called so that list_size(timer_env%routine_stats)
     122              :       ! and the actual dictionary are consistent in the loop below, preventing out of bounds.
     123              :       ! this hack makes sure they are called before
     124        11087 :       routineN = ""
     125        11087 :       CALL para_env%bcast(routineN, 0)
     126        11087 :       sending_rank = 0
     127        11087 :       CALL para_env%max(sending_rank)
     128        11087 :       CALL para_env%sum(sending_rank)
     129        11087 :       foobar = 0.0_dp
     130        11087 :       CALL para_env%max(foobar)
     131        11087 :       dbuf = 0.0_dp
     132        11087 :       CALL para_env%maxloc(dbuf)
     133        11087 :       CALL para_env%sum(foobar)
     134              :       ! end hack
     135              : 
     136              :       ! Array collected is used as a bit field.
     137              :       ! It's of type integer in order to use the convenient MINLOC routine.
     138        33261 :       ALLOCATE (collected(list_size(timer_env%routine_stats)), SOURCE=0)
     139              : 
     140      4566400 :       DO
     141              :          ! does any rank have uncollected stats?
     142      4577487 :          sending_rank = -1
     143    949376392 :          IF (.NOT. ALL(collected == 1)) sending_rank = para_env%mepos
     144      4577487 :          CALL para_env%max(sending_rank)
     145      4577487 :          IF (sending_rank < 0) EXIT ! every rank got all routines collected
     146      4566400 :          IF (sending_rank == para_env%mepos) THEN
     147   1123046665 :             local_routine_id = MINLOC(collected, dim=1)
     148      2306600 :             r_stat => list_get(timer_env%routine_stats, local_routine_id)
     149      2306600 :             routineN = r_stat%routineN
     150              :          END IF
     151      4566400 :          CALL para_env%bcast(routineN, sending_rank)
     152              : 
     153              :          ! Create new report for routineN
     154      4566400 :          ALLOCATE (r_report)
     155      4566400 :          CALL list_push(reports, r_report)
     156      4566400 :          r_report%routineN = routineN
     157              : 
     158              :          ! If routineN was called on local node, add local stats
     159      4566400 :          IF (routine_map_haskey(timer_env%routine_names, routineN)) THEN
     160      4549595 :             local_routine_id = routine_map_get(timer_env%routine_names, routineN)
     161      4549595 :             collected(local_routine_id) = 1
     162      4549595 :             r_stat => list_get(timer_env%routine_stats, local_routine_id)
     163      4549595 :             r_report%max_total_calls = r_stat%total_calls
     164      4549595 :             r_report%sum_total_calls = r_stat%total_calls
     165      4549595 :             r_report%sum_stackdepth = r_stat%stackdepth_accu
     166      4549595 :             SELECT CASE (cost_type)
     167              :             CASE (cost_type_energy)
     168            0 :                r_report%max_icost = r_stat%incl_energy_accu
     169            0 :                r_report%sum_icost = r_stat%incl_energy_accu
     170            0 :                r_report%max_ecost = r_stat%excl_energy_accu
     171            0 :                r_report%sum_ecost = r_stat%excl_energy_accu
     172              :             CASE (cost_type_time)
     173      4549595 :                r_report%max_icost = r_stat%incl_walltime_accu
     174      4549595 :                r_report%sum_icost = r_stat%incl_walltime_accu
     175      4549595 :                r_report%max_ecost = r_stat%excl_walltime_accu
     176      4549595 :                r_report%sum_ecost = r_stat%excl_walltime_accu
     177              :             CASE DEFAULT
     178      4549595 :                CPABORT("BUG")
     179              :             END SELECT
     180              :          END IF
     181              : 
     182              :          ! collect stats of routineN via MPI
     183      4566400 :          CALL para_env%max(r_report%max_total_calls)
     184      4566400 :          CALL para_env%sum(r_report%sum_total_calls)
     185      4566400 :          CALL para_env%sum(r_report%sum_stackdepth)
     186              : 
     187              :          ! get value and rank of the maximum inclusive cost
     188     13699200 :          dbuf = [r_report%max_icost, REAL(para_env%mepos, KIND=dp)]
     189      4566400 :          CALL para_env%maxloc(dbuf)
     190      4566400 :          r_report%max_icost = dbuf(1)
     191      4566400 :          r_report%max_irank = INT(dbuf(2))
     192              : 
     193      4566400 :          CALL para_env%sum(r_report%sum_icost)
     194              : 
     195              :          ! get value and rank of the maximum exclusive cost
     196     13699200 :          dbuf = [r_report%max_ecost, REAL(para_env%mepos, KIND=dp)]
     197      4566400 :          CALL para_env%maxloc(dbuf)
     198      4566400 :          r_report%max_ecost = dbuf(1)
     199      4566400 :          r_report%max_erank = INT(dbuf(2))
     200              : 
     201      4577487 :          CALL para_env%sum(r_report%sum_ecost)
     202              :       END DO
     203              : 
     204        11087 :    END SUBROUTINE collect_reports_from_ranks
     205              : 
     206              : ! **************************************************************************************************
     207              : !> \brief Print the collected reports
     208              : !> \param reports ...
     209              : !> \param iw ...
     210              : !> \param threshold ...
     211              : !> \param sort_by_exclusiv_cost ...
     212              : !> \param cost_type ...
     213              : !> \param report_maxloc ...
     214              : !> \param para_env ...
     215              : !> \par History
     216              : !>      01.2014 Refactored (Ole Schuett)
     217              : !> \author JGH
     218              : ! **************************************************************************************************
     219         5649 :    SUBROUTINE print_reports(reports, iw, threshold, sort_by_exclusiv_cost, cost_type, report_maxloc, para_env)
     220              :       TYPE(list_routinereport_type), INTENT(IN)          :: reports
     221              :       INTEGER, INTENT(IN)                                :: iw
     222              :       REAL(KIND=dp), INTENT(IN)                          :: threshold
     223              :       LOGICAL, INTENT(IN)                                :: sort_by_exclusiv_cost
     224              :       INTEGER, INTENT(IN)                                :: cost_type
     225              :       LOGICAL, INTENT(IN)                                :: report_maxloc
     226              :       TYPE(mp_para_env_type), INTENT(IN)                 :: para_env
     227              : 
     228              :       CHARACTER(LEN=4)                                   :: label
     229              :       CHARACTER(LEN=default_string_length)               :: fmt, title
     230              :       INTEGER                                            :: decimals, i, j, num_routines
     231         5649 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: indices
     232              :       REAL(KIND=dp)                                      :: asd, maxcost, mincost
     233         5649 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: max_costs
     234              :       TYPE(routine_report_type), POINTER                 :: r_report_i, r_report_j
     235              : 
     236         5649 :       NULLIFY (r_report_i, r_report_j)
     237         5649 :       IF (.NOT. list_isready(reports)) THEN
     238            0 :          CPABORT("BUG")
     239              :       END IF
     240              : 
     241              :       ! are we printing timing or energy ?
     242         5649 :       SELECT CASE (cost_type)
     243              :       CASE (cost_type_energy)
     244            0 :          title = "E N E R G Y"
     245            0 :          label = "ENER"
     246              :       CASE (cost_type_time)
     247         5649 :          title = "T I M I N G"
     248         5649 :          label = "TIME"
     249              :       CASE DEFAULT
     250         5649 :          CPABORT("BUG")
     251              :       END SELECT
     252              : 
     253              :       ! write banner
     254         5649 :       WRITE (UNIT=iw, FMT="(/,T2,A)") REPEAT("-", 79)
     255         5649 :       WRITE (UNIT=iw, FMT="(T2,A,T80,A)") "-", "-"
     256         5649 :       WRITE (UNIT=iw, FMT="(T2,A,T35,A,T80,A)") "-", TRIM(title), "-"
     257         5649 :       WRITE (UNIT=iw, FMT="(T2,A,T80,A)") "-", "-"
     258         5649 :       WRITE (UNIT=iw, FMT="(T2,A)") REPEAT("-", 79)
     259         5649 :       IF (report_maxloc) THEN
     260              :          WRITE (UNIT=iw, FMT="(T2,A,T35,A,T41,A,T45,2A18,A8)") &
     261            0 :             "SUBROUTINE", "CALLS", " ASD", "SELF "//label, "TOTAL "//label, "MAXRANK"
     262              :       ELSE
     263              :          WRITE (UNIT=iw, FMT="(T2,A,T35,A,T41,A,T45,2A18)") &
     264         5649 :             "SUBROUTINE", "CALLS", " ASD", "SELF "//label, "TOTAL "//label
     265              :       END IF
     266              : 
     267              :       WRITE (UNIT=iw, FMT="(T33,A)") &
     268         5649 :          "MAXIMUM       AVERAGE  MAXIMUM  AVERAGE  MAXIMUM"
     269              : 
     270              :       ! sort statistics
     271         5649 :       num_routines = list_size(reports)
     272        16947 :       ALLOCATE (max_costs(num_routines))
     273      2312249 :       DO i = 1, num_routines
     274      2306600 :          r_report_i => list_get(reports, i)
     275      2312249 :          IF (sort_by_exclusiv_cost) THEN
     276         1440 :             max_costs(i) = r_report_i%max_ecost
     277              :          ELSE
     278      2305160 :             max_costs(i) = r_report_i%max_icost
     279              :          END IF
     280              :       END DO
     281        16947 :       ALLOCATE (indices(num_routines))
     282         5649 :       CALL sort(max_costs, num_routines, indices)
     283              : 
     284      2312249 :       maxcost = MAXVAL(max_costs)
     285         5649 :       mincost = maxcost*threshold
     286              : 
     287              :       ! adjust fmt dynamically based on the max walltime.
     288              :       ! few clocks have more than 3 digits resolution, so stop there
     289         5649 :       decimals = 3
     290         5649 :       IF (maxcost >= 10000) decimals = 2
     291            0 :       IF (maxcost >= 100000) decimals = 1
     292         5649 :       IF (maxcost >= 1000000) decimals = 0
     293         5649 :       IF (report_maxloc) THEN
     294              :          WRITE (UNIT=fmt, FMT="(A,I0,A)") &
     295            0 :             "(T2,A30,1X,I7,1X,F4.1,4(1X,F8.", decimals, "),I8)"
     296              :       ELSE
     297              :          WRITE (UNIT=fmt, FMT="(A,I0,A)") &
     298         5649 :             "(T2,A30,1X,I7,1X,F4.1,4(1X,F8.", decimals, "))"
     299              :       END IF
     300              : 
     301              :       !write output
     302      2312249 :       DO i = num_routines, 1, -1
     303      2312249 :          IF (max_costs(i) >= mincost) THEN
     304       294408 :             j = indices(i)
     305       294408 :             r_report_j => list_get(reports, j)
     306              :             ! average stack depth
     307              :             asd = REAL(r_report_j%sum_stackdepth, KIND=dp)/ &
     308       294408 :                   REAL(MAX(1_int_8, r_report_j%sum_total_calls), KIND=dp)
     309       294408 :             IF (report_maxloc) THEN
     310              :                WRITE (UNIT=iw, FMT=fmt) &
     311            0 :                   ADJUSTL(r_report_j%routineN(1:31)), &
     312            0 :                   r_report_j%max_total_calls, &
     313            0 :                   asd, &
     314            0 :                   r_report_j%sum_ecost/para_env%num_pe, &
     315            0 :                   r_report_j%max_ecost, &
     316            0 :                   r_report_j%sum_icost/para_env%num_pe, &
     317            0 :                   r_report_j%max_icost, &
     318            0 :                   r_report_j%max_erank
     319              :             ELSE
     320              :                WRITE (UNIT=iw, FMT=fmt) &
     321       294408 :                   ADJUSTL(r_report_j%routineN(1:31)), &
     322       294408 :                   r_report_j%max_total_calls, &
     323       294408 :                   asd, &
     324       294408 :                   r_report_j%sum_ecost/para_env%num_pe, &
     325       294408 :                   r_report_j%max_ecost, &
     326       294408 :                   r_report_j%sum_icost/para_env%num_pe, &
     327       588816 :                   r_report_j%max_icost
     328              :             END IF
     329              :          END IF
     330              :       END DO
     331         5649 :       WRITE (UNIT=iw, FMT="(T2,A,/)") REPEAT("-", 79)
     332              : 
     333         5649 :    END SUBROUTINE print_reports
     334              : 
     335              : ! **************************************************************************************************
     336              : !> \brief Write accumulated callgraph information as cachegrind-file.
     337              : !> http://kcachegrind.sourceforge.net/cgi-bin/show.cgi/KcacheGrindCalltreeFormat
     338              : !> \param filename ...
     339              : !> \par History
     340              : !>     12.2012  initial version[ole]
     341              : !> \author Ole Schuett
     342              : ! **************************************************************************************************
     343            1 :    SUBROUTINE timings_report_callgraph(filename)
     344              :       CHARACTER(len=*), INTENT(in)                       :: filename
     345              : 
     346              :       INTEGER, PARAMETER                                 :: E = 1000, T = 100000
     347              : 
     348              :       INTEGER                                            :: i, unit
     349              :       TYPE(call_stat_type), POINTER                      :: c_stat
     350            1 :       TYPE(callgraph_item_type), DIMENSION(:), POINTER   :: ct_items
     351              :       TYPE(routine_stat_type), POINTER                   :: r_stat
     352              :       TYPE(timer_env_type), POINTER                      :: timer_env
     353              : 
     354              :       CALL open_file(file_name=filename, file_status="REPLACE", file_action="WRITE", &
     355            1 :                      file_form="FORMATTED", unit_number=unit)
     356            1 :       timer_env => get_timer_env()
     357              : 
     358              :       ! use outermost routine as total runtime
     359            1 :       r_stat => list_get(timer_env%routine_stats, 1)
     360            1 :       WRITE (UNIT=unit, FMT="(A)") "events: Walltime Energy"
     361            1 :       WRITE (UNIT=unit, FMT="(A,I0,1X,I0)") "summary: ", &
     362            1 :          INT(T*r_stat%incl_walltime_accu, KIND=int_8), &
     363            2 :          INT(E*r_stat%incl_energy_accu, KIND=int_8)
     364              : 
     365          455 :       DO i = 1, list_size(timer_env%routine_stats)
     366          454 :          r_stat => list_get(timer_env%routine_stats, i)
     367          454 :          WRITE (UNIT=unit, FMT="(A,I0,A,A)") "fn=(", r_stat%routine_id, ") ", r_stat%routineN
     368          454 :          WRITE (UNIT=unit, FMT="(A,I0,1X,I0)") "1 ", &
     369          454 :             INT(T*r_stat%excl_walltime_accu, KIND=int_8), &
     370          909 :             INT(E*r_stat%excl_energy_accu, KIND=int_8)
     371              :       END DO
     372              : 
     373            1 :       ct_items => callgraph_items(timer_env%callgraph)
     374          793 :       DO i = 1, SIZE(ct_items)
     375          792 :          c_stat => ct_items(i)%value
     376          792 :          WRITE (UNIT=unit, FMT="(A,I0,A)") "fn=(", ct_items(i)%key(1), ")"
     377          792 :          WRITE (UNIT=unit, FMT="(A,I0,A)") "cfn=(", ct_items(i)%key(2), ")"
     378          792 :          WRITE (UNIT=unit, FMT="(A,I0,A)") "calls=", c_stat%total_calls, " 1"
     379          792 :          WRITE (UNIT=unit, FMT="(A,I0,1X,I0)") "1 ", &
     380          792 :             INT(T*c_stat%incl_walltime_accu, KIND=int_8), &
     381         1585 :             INT(E*c_stat%incl_energy_accu, KIND=int_8)
     382              :       END DO
     383            1 :       DEALLOCATE (ct_items)
     384              : 
     385            1 :       CALL close_file(unit_number=unit, file_status="KEEP")
     386              : 
     387            1 :    END SUBROUTINE timings_report_callgraph
     388              : END MODULE timings_report
     389              : 
        

Generated by: LCOV version 2.0-1