MPI-SCATCI 2.0
An MPI version of SCATCI
Loading...
Searching...
No Matches
Timing_Module.f90
Go to the documentation of this file.
1! Copyright 2019
2!
3! For a comprehensive list of the developers that contributed to these codes
4! see the UK-AMOR website.
5!
6! This file is part of UKRmol-in (UKRmol+ suite).
7!
8! UKRmol-in is free software: you can redistribute it and/or modify
9! it under the terms of the GNU General Public License as published by
10! the Free Software Foundation, either version 3 of the License, or
11! (at your option) any later version.
12!
13! UKRmol-in is distributed in the hope that it will be useful,
14! but WITHOUT ANY WARRANTY; without even the implied warranty of
15! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16! GNU General Public License for more details.
17!
18! You should have received a copy of the GNU General Public License
19! along with UKRmol-in (in source/COPYING). Alternatively, you can also visit
20! <https://www.gnu.org/licenses/>.
21
22!> \brief Timing module
23!> \authors A Al-Refaie
24!> \date 2017
25!>
26!> \note 16/01/2019 - Jakub Benda: Unifom coding style and expanded documentation.
27!>
28module timing_module
29
30 use precisn, only: longint, wp
31 use const_gbl, only: stdout
32 use utility_module, only: get_real_time, string_hash
33
34 implicit none
35
36 public master_timer
37
38 private
39
40 integer, parameter :: timer_default_size = 1000
41 integer, parameter :: name_len = 40 ! Max length of timer name
42
43 type time_data
44 logical :: used ! Slot used?
45 logical :: active ! Currently active?
46 character(len=name_len) :: name ! Timer name
47 integer(longint) :: calls ! Number of times the timer was invoked
48
49 ! All times below are in seconds
50 real(wp) :: real_time ! Total real time on this timer
51 real(wp) :: real_kids ! Real time spent in nested timers
52 real(wp) :: real_start ! For active timers, time of activation
53 integer(longint) :: stack_p ! For active timers, position in the stack
54 end type time_data
55
56 type :: timer
57 integer :: process_id
58 type(time_data) :: timers(timer_default_size)
59 integer :: nested_timers(timer_default_size)
60 integer :: order(timer_default_size)
61 integer :: timer_count = 0 ! Number of defined timers
62 integer :: timer_active = 0 ! Number of currently active timers
63 real(wp) :: program_start
64 logical :: initialized = .false.
65 contains
66 procedure, public :: initialize
67 procedure, public :: start_timer
68 procedure, public :: stop_timer
69 procedure, public :: report_timers
70 procedure, private :: insert_time
71 !procedure, private :: sort_timers
72 end type timer
73
74 type(timer) :: master_timer
75
76contains
77
78 !> \brief Initialize timers
79 !> \authors A Al-Refaie
80 !> \date 2017
81 !>
82 subroutine initialize (this)
83 class(timer) :: this
84
85 this % timers(:) % active = .false.
86 this % timers(:) % used = .false.
87 this % program_start = get_real_time()
88
89 this % initialized = .true.
90
91 end subroutine initialize
92
93
94 !> \brief Start a new named timer
95 !> \authors A Al-Refaie
96 !> \date 2017
97 !>
98 subroutine start_timer(this, name)
99 class(timer) :: this
100 character(len=*), intent(in) :: name ! Timer name
101 integer :: timer_idx
102
103 timer_idx = this % insert_time(name)
104
105 if (this % timers(timer_idx) % active) then
106 write (stdout, "('TimerStart: timer ',a,' is already active')") trim(name)
107 stop 'TimerStart - nested timer'
108 end if
109
110 ! Push the new timer to the timer stack
111 this % timer_active = this % timer_active + 1
112 this % nested_timers(this % timer_active) = timer_idx
113
114 this % timers(timer_idx) % active = .true.
115 this % timers(timer_idx) % stack_p = this % timer_active
116 this % timers(timer_idx) % calls = this % timers(timer_idx) % calls + 1
117 this % timers(timer_idx) % real_start = get_real_time()
118 !this % timers(timer_idx) % cpu_start = get_cpu_time ()
119
120 end subroutine start_timer
121
122
123 !> \brief Stop a named timer
124 !> \authors A Al-Refaie
125 !> \date 2017
126 !>
127 subroutine stop_timer (this, name)
128 class(timer) :: this
129 character(len=*), intent(in) :: name ! Timer name
130 integer :: timer_idx
131 real(wp) :: real_time
132 type(time_data), pointer :: pt_timer
133
134 timer_idx = this % insert_time(name)
135 real_time = get_real_time() - this % timers(timer_idx) % real_start
136
137 this % timers(timer_idx) % real_time = this % timers(timer_idx) % real_time + real_time
138 this % timers(timer_idx) % active = .false.
139
140 this % timer_active = this % timer_active - 1
141
142 if (this % timer_active > 0) then
143 this % timers(this % nested_timers(this % timer_active)) % real_kids &
144 = this % timers(this % nested_timers(this % timer_active)) % real_kids + real_time
145 end if
146
147 end subroutine stop_timer
148
149
150 !> \brief Insert a new named timer
151 !> \authors A Al-Refaie
152 !> \date 2017
153 !>
154 integer function insert_time (this, name)
155 class(timer) :: this
156 character(len=*), intent(in) :: name ! Timer name
157
158 insert_time = string_hash(name, timer_default_size)
159
160 search: do
161 if (.not. this % timers(insert_time) % used) then
162 ! This is a new key, insert it
163 this % timer_count = this % timer_count + 1
164 if (this % timer_count >= timer_default_size / 5) then
165 write (stdout, "('Too many timers. Increase table_size in timer.f90 to at least ',i5)") this % timer_count * 5
166 stop 'timer%insert_item'
167 end if
168 this % order(this % timer_count) = insert_time
169 this % timers(insert_time) % used = .true.
170 this % timers(insert_time) % active = .false.
171 this % timers(insert_time) % name = name
172 this % timers(insert_time) % calls = 0
173 this % timers(insert_time) % real_time = 0
174 !this % timers(insert_time) % cpu_time = 0
175 this % timers(insert_time) % real_kids = 0
176 !this % timers(insert_time) % cpu_kids = 0
177 exit search
178 end if
179
180 if (this % timers(insert_time) % name == name) then
181 ! This is an existing key, simply return the location
182 exit search
183 end if
184
185 insert_time = 1 + modulo(insert_time - 2, timer_default_size)
186 end do search
187
188 end function insert_time
189
190
191 !> \brief Print a table of timers
192 !> \authors A Al-Refaie
193 !> \date 2017
194 !>
195 subroutine report_timers (this)
196 class(timer) :: this
197 real(wp) :: real_now, real_time, cpu_time, real_kids, cpu_kids, real_threshold
198 !real(wp) :: cpu_now, cpu_threshold
199 integer :: ord, pos, kid_pos, omitted
200
201 type(time_data), pointer :: t, k
202 character(len=1) :: active
203
204 real_now = get_real_time()
205 real_threshold = 0.01_wp * (real_now - this % program_start)
206
207 write (stdout, "(t2,' ',t38,' ',t45,'Total time (seconds)',t67,'Self time (seconds)')")
208 write (stdout, "(t2,'Timer',t38,'Calls',t45,'--------------------',t67,'-------------------')")
209 write (stdout, "(t2,'-----',t38,'-----',t50,'Real',t61,'CPU',t72,'Real',t83,'CPU')")
210 write (stdout, *)
211
212 omitted = 0
213
214 scan: do ord = 1, this % timer_count
215
216 pos = this % order(ord)
217 if (.not. this % timers(pos) % used) then
218 write (stdout, "('Timer ',i4,' in slot ',i5,' is defined but unused?!')") ord, pos
219 stop 'TimerReport - logic error'
220 end if
221
222 ! Calculate active-timer corrections
223 real_time = 0 ; real_kids = 0 ;
224 cpu_time = 0 ; cpu_kids = 0 ;
225 active = ' '
226 if (this % timers(pos) % active) then
227 real_time = real_now - this % timers(pos) % real_start
228 !cpu_time = cpu_now - t%cpu_start
229 if (this % timer_active /= this % timers(pos) % stack_p) then
230 ! If we are not at the top of the stack, adjust
231 ! cumulative children time.
232 kid_pos = this % nested_timers(this % timers(pos) % stack_p + 1)
233 real_kids = real_now - this % timers(kid_pos) % real_start
234 !cpu_kids = cpu_now - k%cpu_start
235 end if
236 active = '*'
237 end if
238
239 real_time = real_time + this % timers(pos) % real_time
240 !cpu_time = cpu_time + t%cpu_time
241 real_kids = real_kids + this % timers(pos) % real_kids
242 !cpu_kids = cpu_kids + t%cpu_kids
243
244 ! Output needed?
245 if (real_time < real_threshold) then
246 omitted = omitted + 1
247 cycle scan
248 end if
249
250 ! Output
251 write (stdout, "(t2,a30,t33,a1,t35,I8,t45,2(f9.1,1x,f9.1,3x))") &
252 this % timers(pos) % name, active, this % timers(pos) % calls, real_time, cpu_time, &
253 real_time - real_kids, cpu_time - cpu_kids
254
255 end do scan
256
257 if (omitted > 0) then
258 write (stdout, "(/' (',i3,' timers contributing less than 1% are not shown)')") omitted
259 end if
260
261 write (stdout, *)
262
263 end subroutine report_timers
264
265end module timing_module