! This file is part of mctc-lib. ! ! Licensed under the Apache License, Version 2.0 (the "License"); ! you may not use this file except in compliance with the License. ! You may obtain a copy of the License at ! ! http://www.apache.org/licenses/LICENSE-2.0 ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. module mctc_env_timer use mctc_env_accuracy, only : wp, i8 implicit none private public :: timer_type, format_time type :: time_record character(len=:), allocatable :: label logical :: running = .false. real(wp) :: time = 0.0_wp end type time_record type :: timer_type integer :: n = 0 character(len=:), allocatable :: last type(time_record), allocatable :: record(:) contains procedure :: push procedure :: pop procedure :: get end type timer_type contains subroutine push(self, label) class(timer_type), intent(inout) :: self character(len=*), intent(in) :: label integer :: it if (.not.allocated(self%record)) call resize(self%record) it = find(self%record(:self%n), label) if (it == 0) then if (self%n >= size(self%record)) then call resize(self%record) end if self%n = self%n + 1 it = self%n self%record(it) = time_record(label) end if associate(record => self%record(it)) self%last = record%label record%time = record%time + timing() * merge(+1, -1, record%running) record%running = .not.record%running end associate end subroutine push subroutine pop(self) class(timer_type), intent(inout) :: self integer :: it if (.not.allocated(self%record)) return it = find(self%record(:self%n), self%last) if (it == 0) return associate(record => self%record(it)) record%time = record%time + timing() * merge(+1, -1, record%running) record%running = .not.record%running end associate if (allocated(self%last)) deallocate(self%last) end subroutine pop function get(self, label) result(time) class(timer_type), intent(in) :: self character(len=*), intent(in) :: label real(wp) :: time integer :: it time = 0.0_wp if (self%n <= 0) return it = find(self%record(:self%n), label) if (it == 0) return associate(record => self%record(it)) time = record%time if (record%running) then time = time + timing() end if end associate end function get pure function find(record, label) result(pos) type(time_record), intent(in) :: record(:) character(len=*), intent(in), optional :: label integer :: pos integer :: i pos = 0 if (present(label)) then do i = size(record), 1, -1 if (allocated(record(i)%label)) then if (label == record(i)%label) then pos = i exit end if end if end do else do i = size(record), 1, -1 if (record(i)%running) then pos = i exit end if end do end if end function find function format_time(time) result(string) real(wp), intent(in) :: time character(len=:), allocatable :: string real(wp) :: secs integer :: mins, hours, days secs = time days = int(secs/86400.0_wp) secs = secs - days*86400.0_wp hours = int(secs/3600.0_wp) secs = secs - hours*3600.0_wp mins = int(secs/60.0_wp) secs = time - mins*60.0_wp if (days > 0) then string = format_string_int(days, '(i0, " d,")') else string = repeat(" ", 4) end if if (hours > 0) then string = string // format_string_int(hours, '(1x, i2, " h,")') else string = string // repeat(" ", 6) end if if (mins > 0) then string = string // format_string_int(mins, '(1x, i2, " min,")') else string = string // repeat(" ", 8) end if string = string // format_string_real_dp(secs, '(f6.3)')//" sec" end function format_time function timing() result(time) real(wp) :: time integer(i8) :: time_count, time_rate, time_max call system_clock(time_count, time_rate, time_max) time = real(time_count, wp)/real(time_rate, wp) end function timing !> Reallocate list of timing records pure subroutine resize(var, n) !> Instance of the array to be resized type(time_record), allocatable, intent(inout) :: var(:) !> Dimension of the final array size integer, intent(in), optional :: n type(time_record), allocatable :: tmp(:) integer :: this_size, new_size integer, parameter :: initial_size = 20 if (allocated(var)) then this_size = size(var, 1) call move_alloc(var, tmp) else this_size = initial_size end if if (present(n)) then new_size = n else new_size = this_size + this_size/2 + 1 end if allocate(var(new_size)) if (allocated(tmp)) then this_size = min(size(tmp, 1), size(var, 1)) var(:this_size) = tmp(:this_size) deallocate(tmp) end if end subroutine resize pure function format_string_int(val, format) result(str) integer, intent(in) :: val character(len=*), intent(in) :: format character(len=:), allocatable :: str character(len=128) :: buffer integer :: stat write(buffer, format, iostat=stat) val if (stat == 0) then str = trim(buffer) else str = "*" end if end function format_string_int pure function format_string_real_dp(val, format) result(str) real(wp), intent(in) :: val character(len=*), intent(in) :: format character(len=:), allocatable :: str character(len=128) :: buffer integer :: stat write(buffer, format, iostat=stat) val if (stat == 0) then str = trim(buffer) else str = "*" end if end function format_string_real_dp end module mctc_env_timer