MPI-SCATCI  2.0
An MPI version of SCATCI
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 
29 
30  use precisn, only: longint, wp
31  use const_gbl, only: stdout
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 
75 
76 contains
77 
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 
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 
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 
154  integer function insert_time (this, name)
155  class(timer) :: this
156  character(len=*), intent(in) :: name ! Timer name
157 
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 
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 
265 end module timing_module
timing_module::timer_default_size
integer, parameter timer_default_size
Definition: Timing_Module.f90:40
timing_module::timer
Definition: Timing_Module.f90:56
timing_module
Timing module.
Definition: Timing_Module.f90:28
timing_module::stop_timer
subroutine stop_timer(this, name)
Stop a named timer.
Definition: Timing_Module.f90:128
timing_module::report_timers
subroutine report_timers(this)
Print a table of timers.
Definition: Timing_Module.f90:196
utility_module
Utility module.
Definition: Utility_module.f90:28
utility_module::get_real_time
real(wp) function, public get_real_time()
Get current (real) time.
Definition: Utility_module.f90:133
utility_module::string_hash
integer function, public string_hash(str, table_size)
Calculate a string hash.
Definition: Utility_module.f90:110
timing_module::name_len
integer, parameter name_len
Definition: Timing_Module.f90:41
timing_module::initialize
subroutine initialize(this)
Initialize timers.
Definition: Timing_Module.f90:83
timing_module::time_data
Definition: Timing_Module.f90:43
timing_module::insert_time
integer function insert_time(this, name)
Insert a new named timer.
Definition: Timing_Module.f90:155
timing_module::start_timer
subroutine start_timer(this, name)
Start a new named timer.
Definition: Timing_Module.f90:99
timing_module::master_timer
type(timer), public master_timer
Definition: Timing_Module.f90:74