(git:71c3ab0)
Loading...
Searching...
No Matches
timings_report.F
Go to the documentation of this file.
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! **************************************************************************************************
21 USE cp_files, ONLY: close_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,&
30 list_pop,&
31 list_push,&
35 USE routine_map, ONLY: routine_map_get,&
37 USE timings, ONLY: get_timer_env
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
51
52CONTAINS
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 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 CALL list_init(reports)
78 CALL collect_reports_from_ranks(reports, cost_type, para_env)
79
80 IF (list_size(reports) > 0 .AND. iw > 0) THEN
81 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 DO WHILE (list_size(reports) > 0)
86 r_report => list_pop(reports)
87 DEALLOCATE (r_report)
88 END DO
89 CALL list_destroy(reports)
90
91 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 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 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 NULLIFY (r_stat, r_report, timer_env)
115 IF (.NOT. list_isready(reports)) THEN
116 cpabort("BUG")
117 END IF
118
119 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 routinen = ""
125 CALL para_env%bcast(routinen, 0)
126 sending_rank = 0
127 CALL para_env%max(sending_rank)
128 CALL para_env%sum(sending_rank)
129 foobar = 0.0_dp
130 CALL para_env%max(foobar)
131 dbuf = 0.0_dp
132 CALL para_env%maxloc(dbuf)
133 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 ALLOCATE (collected(list_size(timer_env%routine_stats)), source=0)
139
140 DO
141 ! does any rank have uncollected stats?
142 sending_rank = -1
143 IF (.NOT. all(collected == 1)) sending_rank = para_env%mepos
144 CALL para_env%max(sending_rank)
145 IF (sending_rank < 0) EXIT ! every rank got all routines collected
146 IF (sending_rank == para_env%mepos) THEN
147 local_routine_id = minloc(collected, dim=1)
148 r_stat => list_get(timer_env%routine_stats, local_routine_id)
149 routinen = r_stat%routineN
150 END IF
151 CALL para_env%bcast(routinen, sending_rank)
152
153 ! Create new report for routineN
154 ALLOCATE (r_report)
155 CALL list_push(reports, r_report)
156 r_report%routineN = routinen
157
158 ! If routineN was called on local node, add local stats
159 IF (routine_map_haskey(timer_env%routine_names, routinen)) THEN
160 local_routine_id = routine_map_get(timer_env%routine_names, routinen)
161 collected(local_routine_id) = 1
162 r_stat => list_get(timer_env%routine_stats, local_routine_id)
163 r_report%max_total_calls = r_stat%total_calls
164 r_report%sum_total_calls = r_stat%total_calls
165 r_report%sum_stackdepth = r_stat%stackdepth_accu
166 SELECT CASE (cost_type)
167 CASE (cost_type_energy)
168 r_report%max_icost = r_stat%incl_energy_accu
169 r_report%sum_icost = r_stat%incl_energy_accu
170 r_report%max_ecost = r_stat%excl_energy_accu
171 r_report%sum_ecost = r_stat%excl_energy_accu
172 CASE (cost_type_time)
173 r_report%max_icost = r_stat%incl_walltime_accu
174 r_report%sum_icost = r_stat%incl_walltime_accu
175 r_report%max_ecost = r_stat%excl_walltime_accu
176 r_report%sum_ecost = r_stat%excl_walltime_accu
177 CASE DEFAULT
178 cpabort("BUG")
179 END SELECT
180 END IF
181
182 ! collect stats of routineN via MPI
183 CALL para_env%max(r_report%max_total_calls)
184 CALL para_env%sum(r_report%sum_total_calls)
185 CALL para_env%sum(r_report%sum_stackdepth)
186
187 ! get value and rank of the maximum inclusive cost
188 dbuf = [r_report%max_icost, real(para_env%mepos, kind=dp)]
189 CALL para_env%maxloc(dbuf)
190 r_report%max_icost = dbuf(1)
191 r_report%max_irank = int(dbuf(2))
192
193 CALL para_env%sum(r_report%sum_icost)
194
195 ! get value and rank of the maximum exclusive cost
196 dbuf = [r_report%max_ecost, real(para_env%mepos, kind=dp)]
197 CALL para_env%maxloc(dbuf)
198 r_report%max_ecost = dbuf(1)
199 r_report%max_erank = int(dbuf(2))
200
201 CALL para_env%sum(r_report%sum_ecost)
202 END DO
203
204 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 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 INTEGER, ALLOCATABLE, DIMENSION(:) :: indices
232 REAL(kind=dp) :: asd, maxcost, mincost
233 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: max_costs
234 TYPE(routine_report_type), POINTER :: r_report_i, r_report_j
235
236 NULLIFY (r_report_i, r_report_j)
237 IF (.NOT. list_isready(reports)) THEN
238 cpabort("BUG")
239 END IF
240
241 ! are we printing timing or energy ?
242 SELECT CASE (cost_type)
243 CASE (cost_type_energy)
244 title = "E N E R G Y"
245 label = "ENER"
246 CASE (cost_type_time)
247 title = "T I M I N G"
248 label = "TIME"
249 CASE DEFAULT
250 cpabort("BUG")
251 END SELECT
252
253 ! write banner
254 WRITE (unit=iw, fmt="(/,T2,A)") repeat("-", 79)
255 WRITE (unit=iw, fmt="(T2,A,T80,A)") "-", "-"
256 WRITE (unit=iw, fmt="(T2,A,T35,A,T80,A)") "-", trim(title), "-"
257 WRITE (unit=iw, fmt="(T2,A,T80,A)") "-", "-"
258 WRITE (unit=iw, fmt="(T2,A)") repeat("-", 79)
259 IF (report_maxloc) THEN
260 WRITE (unit=iw, fmt="(T2,A,T35,A,T41,A,T45,2A18,A8)") &
261 "SUBROUTINE", "CALLS", " ASD", "SELF "//label, "TOTAL "//label, "MAXRANK"
262 ELSE
263 WRITE (unit=iw, fmt="(T2,A,T35,A,T41,A,T45,2A18)") &
264 "SUBROUTINE", "CALLS", " ASD", "SELF "//label, "TOTAL "//label
265 END IF
266
267 WRITE (unit=iw, fmt="(T33,A)") &
268 "MAXIMUM AVERAGE MAXIMUM AVERAGE MAXIMUM"
269
270 ! sort statistics
271 num_routines = list_size(reports)
272 ALLOCATE (max_costs(num_routines))
273 DO i = 1, num_routines
274 r_report_i => list_get(reports, i)
275 IF (sort_by_exclusiv_cost) THEN
276 max_costs(i) = r_report_i%max_ecost
277 ELSE
278 max_costs(i) = r_report_i%max_icost
279 END IF
280 END DO
281 ALLOCATE (indices(num_routines))
282 CALL sort(max_costs, num_routines, indices)
283
284 maxcost = maxval(max_costs)
285 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 decimals = 3
290 IF (maxcost >= 10000) decimals = 2
291 IF (maxcost >= 100000) decimals = 1
292 IF (maxcost >= 1000000) decimals = 0
293 IF (report_maxloc) THEN
294 WRITE (unit=fmt, fmt="(A,I0,A)") &
295 "(T2,A30,1X,I7,1X,F4.1,4(1X,F8.", decimals, "),I8)"
296 ELSE
297 WRITE (unit=fmt, fmt="(A,I0,A)") &
298 "(T2,A30,1X,I7,1X,F4.1,4(1X,F8.", decimals, "))"
299 END IF
300
301 !write output
302 DO i = num_routines, 1, -1
303 IF (max_costs(i) >= mincost) THEN
304 j = indices(i)
305 r_report_j => list_get(reports, j)
306 ! average stack depth
307 asd = real(r_report_j%sum_stackdepth, kind=dp)/ &
308 REAL(max(1_int_8, r_report_j%sum_total_calls), kind=dp)
309 IF (report_maxloc) THEN
310 WRITE (unit=iw, fmt=fmt) &
311 adjustl(r_report_j%routineN(1:31)), &
312 r_report_j%max_total_calls, &
313 asd, &
314 r_report_j%sum_ecost/para_env%num_pe, &
315 r_report_j%max_ecost, &
316 r_report_j%sum_icost/para_env%num_pe, &
317 r_report_j%max_icost, &
318 r_report_j%max_erank
319 ELSE
320 WRITE (unit=iw, fmt=fmt) &
321 adjustl(r_report_j%routineN(1:31)), &
322 r_report_j%max_total_calls, &
323 asd, &
324 r_report_j%sum_ecost/para_env%num_pe, &
325 r_report_j%max_ecost, &
326 r_report_j%sum_icost/para_env%num_pe, &
327 r_report_j%max_icost
328 END IF
329 END IF
330 END DO
331 WRITE (unit=iw, fmt="(T2,A,/)") repeat("-", 79)
332
333 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 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 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 file_form="FORMATTED", unit_number=unit)
356 timer_env => get_timer_env()
357
358 ! use outermost routine as total runtime
359 r_stat => list_get(timer_env%routine_stats, 1)
360 WRITE (unit=unit, fmt="(A)") "events: Walltime Energy"
361 WRITE (unit=unit, fmt="(A,I0,1X,I0)") "summary: ", &
362 int(t*r_stat%incl_walltime_accu, kind=int_8), &
363 int(e*r_stat%incl_energy_accu, kind=int_8)
364
365 DO i = 1, list_size(timer_env%routine_stats)
366 r_stat => list_get(timer_env%routine_stats, i)
367 WRITE (unit=unit, fmt="(A,I0,A,A)") "fn=(", r_stat%routine_id, ") ", r_stat%routineN
368 WRITE (unit=unit, fmt="(A,I0,1X,I0)") "1 ", &
369 int(t*r_stat%excl_walltime_accu, kind=int_8), &
370 int(e*r_stat%excl_energy_accu, kind=int_8)
371 END DO
372
373 ct_items => callgraph_items(timer_env%callgraph)
374 DO i = 1, SIZE(ct_items)
375 c_stat => ct_items(i)%value
376 WRITE (unit=unit, fmt="(A,I0,A)") "fn=(", ct_items(i)%key(1), ")"
377 WRITE (unit=unit, fmt="(A,I0,A)") "cfn=(", ct_items(i)%key(2), ")"
378 WRITE (unit=unit, fmt="(A,I0,A)") "calls=", c_stat%total_calls, " 1"
379 WRITE (unit=unit, fmt="(A,I0,1X,I0)") "1 ", &
380 int(t*c_stat%incl_walltime_accu, kind=int_8), &
381 int(e*c_stat%incl_energy_accu, kind=int_8)
382 END DO
383 DEALLOCATE (ct_items)
384
385 CALL close_file(unit_number=unit, file_status="KEEP")
386
387 END SUBROUTINE timings_report_callgraph
388END MODULE timings_report
389
type(callgraph_item_type) function, dimension(:), pointer, public callgraph_items(hash_map)
Returns a pointer to an array of all key/value-items stored in the hash map. Caution: The caller is r...
Definition callgraph.F:385
Utility routines to open and close files. Tracking of preconnections.
Definition cp_files.F:16
subroutine, public open_file(file_name, file_status, file_form, file_action, file_position, file_pad, unit_number, debug, skip_get_unit_number, file_access)
Opens the requested file using a free unit number.
Definition cp_files.F:311
subroutine, public close_file(unit_number, file_status, keep_preconnection)
Close an open file given by its logical unit number. Optionally, keep the file and unit preconnected.
Definition cp_files.F:122
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public int_8
Definition kinds.F:54
integer, parameter, public dp
Definition kinds.F:34
integer, parameter, public default_string_length
Definition kinds.F:57
An array-based list which grows on demand. When the internal array is full, a new array of twice the ...
An array-based list which grows on demand. When the internal array is full, a new array of twice the ...
Definition list.F:24
Interface to the message passing library MPI.
integer(kind=int_4) function, public routine_map_get(hash_map, key, default_value)
Gets a value for a given key from the hash map. If the key is not found the default_value will be ret...
logical function, public routine_map_haskey(hash_map, key)
Checks whether a given key is currently stored in the hash_map.
Types used by timings.F and timings_report.F The types in this module are used within dict or list,...
Timing routines for accounting.
integer, parameter, public cost_type_energy
subroutine, public timings_report_callgraph(filename)
Write accumulated callgraph information as cachegrind-file. http://kcachegrind.sourceforge....
integer, parameter, public cost_type_time
subroutine, public timings_report_print(iw, r_timings, sort_by_self_time, cost_type, report_maxloc, para_env)
Print accumulated information on timers.
Types used by timings.F and timings_report.F Due to the fortran restriction on cicular module-depende...
Timing routines for accounting.
Definition timings.F:17
type(timer_env_type) function, pointer, public get_timer_env()
returns the current timer env from the stack
Definition timings.F:148
All kind of helpful little routines.
Definition util.F:14
stores all the informations relevant to an mpi environment