MODFLOW 6  version 6.7.0.dev3
USGS Modular Hydrologic Model
profilermodule Module Reference

Data Types

type  measuredsectiontype
 
type  profilertype
 A public type for profiling performance in the application. The ProfilerType is used to measure and record the performance of various parts of the application. It provides mechanisms to start, stop, and. More...
 

Functions/Subroutines

subroutine pre_init (this)
 
subroutine initialize (this)
 
integer(i4b) function add_section (this, title, parent_id)
 Add a new timed section to the tree, passing the parent id will add it as a child. More...
 
subroutine start (this, title, section_id)
 Start section timing, add when not exist yet (i.e. when id < 1) More...
 
subroutine stop (this, section_id)
 
subroutine print (this, output_unit)
 
recursive subroutine print_section (this, section_id, level)
 
subroutine print_total (this, subtitle)
 
real(dp) function aggregate_walltime (this, title)
 Aggregate walltime over sections with a certain title. More...
 
integer(i4b) function aggregate_counts (this, title)
 Aggregate counts over sections with a certain title. More...
 
subroutine set_print_option (this, profile_option)
 Set the profile option from the user input. More...
 
subroutine destroy (this)
 Clean up the CPU timer object. More...
 
integer(i4b) function largest_title_length (this)
 Calculate the largest title length. More...
 
subroutine sort_by_walltime (this, idxs)
 Sort section indexes based on walltime. More...
 

Variables

integer(i4b), parameter max_sections_per_sln = 40
 
integer(i4b), parameter, public len_section_title = 128
 
type(profilertype), public g_prof
 the global timer object (to reduce trivial lines of code) More...
 

Function/Subroutine Documentation

◆ add_section()

integer(i4b) function profilermodule::add_section ( class(profilertype this,
character(len=*)  title,
integer(i4b)  parent_id 
)
private

Definition at line 140 of file Profiler.f90.

141  use simmodule, only: ustop
142  class(ProfilerType) :: this
143  character(len=*) :: title
144  integer(I4B) :: parent_id
145  integer(I4B) :: section_id
146 
147  ! increment to new section id
148  this%nr_sections = this%nr_sections + 1
149  section_id = this%nr_sections
150  if (section_id > size(this%all_sections)) then
151  write (*, *) "Internal error: Too many profiled sections, "&
152  &"disable profiling to circumvent."
153  call ustop()
154  end if
155 
156  ! initialize new section
157  this%all_sections(section_id)%title = title
158  this%all_sections(section_id)%walltime = dzero
159  this%all_sections(section_id)%status = 0
160 
161  ! if parent, otherwise root section
162  if (parent_id > 0) then
163  ! add child to parent
164  this%all_sections(section_id)%parent_id = parent_id
165  call this%all_sections(parent_id)%children%push_back(section_id)
166  else
167  ! this is the root, assume there's only one!
168  this%all_sections(section_id)%parent_id = 0
169  this%root_id = section_id
170  end if
171 
This module contains simulation methods.
Definition: Sim.f90:10
subroutine, public ustop(stopmess, ioutlocal)
Stop the simulation.
Definition: Sim.f90:312
Here is the call graph for this function:

◆ aggregate_counts()

integer(i4b) function profilermodule::aggregate_counts ( class(profilertype this,
character(len=*)  title 
)
private

Definition at line 366 of file Profiler.f90.

367  class(ProfilerType) :: this
368  character(len=*) :: title
369  integer(I4B) :: counts
370  ! local
371  integer(I4B) :: i
372 
373  counts = 0
374  do i = 1, this%nr_sections
375  if (index(this%all_sections(i)%title, trim(title)) > 0) then
376  counts = counts + this%all_sections(i)%count
377  end if
378  end do
379 

◆ aggregate_walltime()

real(dp) function profilermodule::aggregate_walltime ( class(profilertype this,
character(len=*)  title 
)
private

Definition at line 348 of file Profiler.f90.

349  class(ProfilerType) :: this
350  character(len=*) :: title
351  real(DP) :: walltime
352  ! local
353  integer(I4B) :: i
354 
355  walltime = dzero
356  do i = 1, this%nr_sections
357  if (index(this%all_sections(i)%title, trim(title)) > 0) then
358  walltime = walltime + this%all_sections(i)%walltime
359  end if
360  end do
361 
Here is the caller graph for this function:

◆ destroy()

subroutine profilermodule::destroy ( class(profilertype this)
private

Definition at line 403 of file Profiler.f90.

404  class(ProfilerType) :: this
405  ! local
406  integer(I4B) :: i
407 
408  call this%callstack%destroy()
409 
410  do i = 1, size(this%all_sections)
411  call this%all_sections(i)%children%destroy()
412  end do
413  deallocate (this%all_sections)
414  nullify (this%all_sections)
415 

◆ initialize()

subroutine profilermodule::initialize ( class(profilertype this)
private

Definition at line 84 of file Profiler.f90.

85  class(ProfilerType) :: this
86  ! local
87  character(len=LENMEMPATH) :: input_mempath
88  type(CharacterStringType), dimension(:), contiguous, &
89  pointer :: slntype
90  character(len=:), pointer :: prprof
91  integer(I4B) :: i
92  integer(I4B) :: max_sections
93  integer(I4B) :: nr_solutions
94 
95  this%tmr_run = -1
96  this%tmr_init = -1
97  this%tmr_update = -1
98  this%tmr_finalize = -1
99  this%tmr_prep_tstp = -1
100  this%tmr_do_tstp = -1
101  this%tmr_final_tstp = -1
102  this%tmr_output = -1
103  this%tmr_nc_export = -1
104 
105  call this%callstack%init()
106 
107  ! get nr of solutions from input context
108  input_mempath = create_mem_path('SIM', 'NAM', idm_context)
109  call mem_setptr(slntype, 'SLNTYPE', input_mempath)
110  nr_solutions = size(slntype)
111  call mem_setptr(prprof, 'PRPROF', input_mempath)
112  call this%set_print_option(prprof)
113 
114  max_sections = max_sections_per_sln * nr_solutions
115  allocate (this%all_sections(max_sections))
116  do i = 1, max_sections
117  this%all_sections(i)%title = "undefined"
118  this%all_sections(i)%status = 0
119  this%all_sections(i)%walltime = dzero
120  this%all_sections(i)%count = 0
121  this%all_sections(i)%parent_id = 0
122  call this%all_sections(i)%children%init()
123  end do
124 
125  this%nr_sections = 0
126  this%root_id = 0
127  this%top_three = [0, 0, 0]
128 
129  ! start root section here with previously recorded start time
130  if (this%pr_option > 0) then
131  call this%start("Run", this%tmr_run)
132  this%all_sections(this%tmr_run)%walltime = -this%sim_start_time
133  end if
134 
Here is the call graph for this function:

◆ largest_title_length()

integer(i4b) function profilermodule::largest_title_length ( class(profilertype this)
private

Definition at line 420 of file Profiler.f90.

421  class(ProfilerType) :: this
422  integer(I4B) :: max_length
423  integer(I4B) :: i
424 
425  max_length = 0
426  do i = 1, this%nr_sections
427  max_length = max(max_length, len_trim(this%all_sections(i)%title))
428  end do
429 

◆ pre_init()

subroutine profilermodule::pre_init ( class(profilertype this)
private

Definition at line 75 of file Profiler.f90.

76  class(ProfilerType) :: this
77 
78  call cpu_time(this%sim_start_time)
79 

◆ print()

subroutine profilermodule::print ( class(profilertype this,
integer(i4b), intent(in)  output_unit 
)
private

Definition at line 227 of file Profiler.f90.

228  class(ProfilerType) :: this
229  integer(I4B), intent(in) :: output_unit
230  ! local
231  integer(I4B) :: level, i, top_idx
232  integer(I4B), dimension(:), allocatable :: sorted_idxs
233 
234  if (this%pr_option == 0) return
235 
236  this%iout = output_unit
237 
238  ! get top three leaf sections based on walltime
239  top_idx = 1
240  sorted_idxs = (/(i, i=1, this%nr_sections)/)
241  call this%sort_by_walltime(sorted_idxs)
242  do i = 1, this%nr_sections
243  if (this%all_sections(sorted_idxs(i))%children%size == 0) then ! leaf node
244  if (top_idx > 3) exit
245  this%top_three(top_idx) = sorted_idxs(i)
246  top_idx = top_idx + 1
247  end if
248  end do
249 
250  this%max_title_len = this%largest_title_length()
251 
252  if (this%pr_option > 1) then
253  ! print timing call stack
254  level = 0
255  write (this%iout, '(/1x,a/)') &
256  repeat('-', 18)//" Profiler: Call Stack "//repeat('-', 18)
257  call this%print_section(this%root_id, level)
258  end if
259 
260  ! print walltime per category from substring (if exist)
261  ! note: the sections containing the substring should not be nested,
262  ! otherwise the walltime will be counted multiple times
263  write (this%iout, '(1x,a/)') &
264  repeat('-', 20)//" Profiler: Totals "//repeat('-', 20)
265  call this%print_total("Formulate")
266  call this%print_total("Linear solve")
267  call this%print_total("Calculate flows")
268  call this%print_total("Calculate budgets")
269  call this%print_total("Write output")
270  call this%print_total("Parallel Solution")
271  call this%print_total("MPI_WaitAll")
272  write (this%iout, '(/1x,a/)') &
273  repeat('-', 22)//" End Profiler "//repeat('-', 22)
274 
Here is the caller graph for this function:

◆ print_section()

recursive subroutine profilermodule::print_section ( class(profilertype this,
integer(i4b)  section_id,
integer(i4b)  level 
)
private

Definition at line 277 of file Profiler.f90.

278  use arrayhandlersmodule, only: ifind
279  class(ProfilerType) :: this
280  integer(I4B) :: section_id
281  integer(I4B) :: level
282  ! local
283  integer(I4B) :: i, new_level, nr_padding, idx_top
284  real(DP) :: percent
285  type(MeasuredSectionType), pointer :: section
286  character(len=:), allocatable :: title_padded
287  character(len=LINELENGTH) :: top_marker
288 
289  section => this%all_sections(section_id)
290 
291  ! calculate percentage
292  percent = 1.0_dp
293  if (section%parent_id /= 0) then
294  percent = section%walltime / this%all_sections(this%root_id)%walltime
295  end if
296  percent = percent * 100.0_dp
297 
298  ! determine if section should be marked as top three
299  top_marker = ""
300  idx_top = ifind(this%top_three, section_id)
301  if (idx_top > 0) then
302  nr_padding = max(0, 32 - level * 4)
303  write (top_marker, '(a,i0)') repeat(" ", nr_padding)//"<== #", idx_top
304  end if
305 
306  ! print section timing
307  nr_padding = this%max_title_len - len_trim(section%title) + 2
308  title_padded = trim(section%title)//":"//repeat(' ', nr_padding)
309  write (this%iout, '(3a,f6.2,2a,f14.6,2a,i0,a,a)') " ", &
310  repeat('....', level), "[", percent, "%] ", title_padded, &
311  section%walltime, "s", " (", section%count, "x)", trim(top_marker)
312 
313  ! print children
314  new_level = level + 1
315  do i = 1, section%children%size
316  call this%print_section(section%children%at(i), new_level)
317  end do
318 
319  if (level == 0) write (this%iout, *)
320 

◆ print_total()

subroutine profilermodule::print_total ( class(profilertype this,
character(len=*)  subtitle 
)

Definition at line 323 of file Profiler.f90.

324  class(ProfilerType) :: this
325  character(len=*) :: subtitle
326  ! local
327  integer(I4B) :: count
328  real(DP) :: walltime, percent
329  integer(I4B) :: nr_padding
330  character(len=:), allocatable :: title_padded
331 
332  ! get maximum length of title
333  nr_padding = this%max_title_len - len_trim(subtitle)
334  title_padded = trim(subtitle)//repeat(' ', nr_padding)
335 
336  count = this%aggregate_counts(subtitle)
337  if (count > 0) then
338  walltime = aggregate_walltime(this, subtitle)
339  percent = (walltime / this%all_sections(this%root_id)%walltime) * 100.0_dp
340  write (this%iout, '(2a,f6.2,3a,f14.6,2a,i0,a)') " ", "[", percent, &
341  "%] ", title_padded, ": ", walltime, "s", " (", count, "x)"
342  end if
343 
Here is the call graph for this function:

◆ set_print_option()

subroutine profilermodule::set_print_option ( class(profilertype this,
character(len=*), intent(in)  profile_option 
)
private

Definition at line 384 of file Profiler.f90.

385  class(ProfilerType) :: this
386  character(len=*), intent(in) :: profile_option
387 
388  select case (trim(profile_option))
389  case ("NONE")
390  this%pr_option = 0
391  case ("SUMMARY")
392  this%pr_option = 1
393  case ("DETAIL")
394  this%pr_option = 2
395  case default
396  this%pr_option = 0
397  end select
398 

◆ sort_by_walltime()

subroutine profilermodule::sort_by_walltime ( class(profilertype this,
integer(i4b), dimension(:), allocatable  idxs 
)
private
Parameters
idxsarray with unsorted section idxs

Definition at line 434 of file Profiler.f90.

435  class(ProfilerType) :: this
436  integer(I4B), dimension(:), allocatable :: idxs !< array with unsorted section idxs
437  integer(I4B) :: i, j, temp
438 
439  ! Simple bubble sort for demonstration purposes
440  do i = 1, size(idxs) - 1
441  do j = 1, size(idxs) - i
442  if (this%all_sections(idxs(j))%walltime < &
443  this%all_sections(idxs(j + 1))%walltime) then
444  temp = idxs(j)
445  idxs(j) = idxs(j + 1)
446  idxs(j + 1) = temp
447  end if
448  end do
449  end do
450 

◆ start()

subroutine profilermodule::start ( class(profilertype this,
character(len=*)  title,
integer(i4b)  section_id 
)

Definition at line 176 of file Profiler.f90.

177  class(ProfilerType) :: this
178  character(len=*) :: title
179  integer(I4B) :: section_id
180  ! local
181  integer(I4B) :: parent_id
182  real(DP) :: start_time
183  type(MeasuredSectionType), pointer :: section
184 
185  if (this%pr_option == 0) return
186 
187  call cpu_time(start_time)
188 
189  if (section_id == -1) then
190  ! add section if not exist
191  parent_id = 0 ! root
192  if (this%callstack%size() > 0) then
193  parent_id = this%callstack%top()
194  end if
195  section_id = this%add_section(title, parent_id)
196  end if
197  call this%callstack%push(section_id)
198 
199  section => this%all_sections(section_id)
200  section%count = section%count + 1
201  section%status = 1
202  section%walltime = section%walltime - start_time
203 

◆ stop()

subroutine profilermodule::stop ( class(profilertype this,
integer(i4b)  section_id 
)
private

Definition at line 206 of file Profiler.f90.

207  class(ProfilerType) :: this
208  integer(I4B) :: section_id
209  ! local
210  real(DP) :: end_time
211  type(MeasuredSectionType), pointer :: section
212 
213  if (this%pr_option == 0) return
214 
215  call cpu_time(end_time)
216 
217  ! nett result (c.f. start(...)) is adding (dt = end_time - start_time)
218  section => this%all_sections(section_id)
219  section%status = 0
220  section%walltime = section%walltime + end_time
221 
222  ! pop from call stack
223  call this%callstack%pop()
224 

Variable Documentation

◆ g_prof

type(profilertype), public profilermodule::g_prof

Definition at line 70 of file Profiler.f90.

70  type(ProfilerType), public :: g_prof !< the global timer object (to reduce trivial lines of code)

◆ len_section_title

integer(i4b), parameter, public profilermodule::len_section_title = 128

Definition at line 15 of file Profiler.f90.

15  integer(I4B), public, parameter :: LEN_SECTION_TITLE = 128

◆ max_sections_per_sln

integer(i4b), parameter profilermodule::max_sections_per_sln = 40
private

Definition at line 14 of file Profiler.f90.

14  integer(I4B), parameter :: MAX_SECTIONS_PER_SLN = 40