54 use,
intrinsic :: iso_fortran_env
81 integer,
parameter :: &
82 LABEL_LENGTH = 40, & !< Max. number of characters of tag label.
87 character(LABEL_LENGTH) :: label
88 real(real64) :: entry_time
89 real(real64) :: total_time
90 real(real64) :: min_time
91 real(real64) :: self_time
92 real(real64) :: op_count_current
93 real(real64) :: op_count
94 real(real64) :: op_count_child
95 real(real64) :: op_count_child_current
96 real(real64) :: tr_count_current
97 real(real64) :: tr_count
98 real(real64) :: tr_count_child
99 real(real64) :: tr_count_child_current
100 type(profile_t),
pointer :: parent
102 logical :: initialized = .false.
103 logical :: active = .false.
107 logical :: has_child(MAX_PROFILES)
108 real(real64) :: timings(MAX_PROFILES)
113 type(profile_t),
pointer :: p
141 integer,
parameter,
public :: &
142 PROFILING_TIME = 1, &
148 integer,
parameter :: MAX_MEMORY_VARS = 25
152 integer,
public :: mode
154 type(profile_pointer_t) :: current
155 type(profile_pointer_t) :: profile_list(MAX_PROFILES)
156 integer :: last_profile
158 integer(int64) :: alloc_count
159 integer(int64) :: dealloc_count
161 integer(int64) :: memory_limit = -1
162 integer(int64) :: total_memory
163 integer(int64) :: max_memory
164 character(len=256) :: max_memory_location
166 integer(int64) :: large_vars_size(MAX_MEMORY_VARS)
167 character(len=256) :: large_vars(MAX_MEMORY_VARS)
169 real(real64) :: start_time
172 character(len=256) :: output_dir
173 character(len=6) :: file_number
177 logical :: output_yaml
178 logical :: output_tree
180 character(LABEL_LENGTH),
allocatable :: likwid_regions(:)
196 character(len=256) :: label
282 prof_vars%memory_limit = int(ii, int64)*1024
290 namespace, action=
'write')
291 write(
prof_vars%mem_iunit,
'(5a16,a70)')
'Elapsed Time',
'Alloc/Dealloc',
'Size (words)',
'Prof Mem', &
292 'Sys Mem',
'Variable Name(Filename:Line)'
312 if (
parse_block(namespace,
'LikwidRegions', blk) == 0)
then
317 if (len_trim(label) > label_length)
then
318 message(1) =
"LikwidRegions: label " // trim(label) //
" is too long"
324 message(1) =
"LikwidRegions is ignored because ProfilingMode does not include likwid."
332 call likwid_markerinit()
385 real(real64),
parameter :: megabyte = 1048576.0_real64
387 integer(int64) :: io_open_count_red, io_close_count_red
396 prof_vars%profile_list(ii)%p%initialized = .false.
400 safe_deallocate_a(
prof_vars%likwid_regions)
404 write(
message(1),
'(a,i10)')
'Number of allocations = ',
prof_vars%alloc_count
405 write(
message(2),
'(a,i10)')
'Number of deallocations = ',
prof_vars%dealloc_count
406 write(
message(3),
'(a,f18.3,a)')
'Maximum total memory allocated = ',
prof_vars%max_memory/megabyte,
' Mbytes'
411 message(2) =
'Largest variables allocated:'
413 do ii = 1, max_memory_vars
421 write(
message(1),
'(a,i10,a,i10,a)')
"Not all memory was deallocated: ",
prof_vars%alloc_count, &
422 ' allocations and ',
prof_vars%dealloc_count,
' deallocations'
426 write(
message(1),
'(a,f18.3,a,f18.3,a)')
"Remaining allocated memory: ",
prof_vars%total_memory/megabyte, &
427 ' Mbytes (out of maximum ',
prof_vars%max_memory/megabyte,
' Mbytes)'
438 call likwid_markerclose()
450 write(
message(3),
'(a,i10)')
'Global number of file open = ', io_open_count_red
451 write(
message(4),
'(a,i10)')
'Global number of file close = ', io_close_count_red
463 type(
profile_t),
target,
intent(out) :: this
464 character(*),
intent(in) :: label
470 if(len_trim(label) > label_length)
then
471 message(1) =
"Label " // trim(label) //
" is too long for the internal profiler"
479 this%entry_time = huge(this%entry_time)
481 this%op_count_current =
m_zero
483 this%op_count_child =
m_zero
484 this%tr_count_current =
m_zero
486 this%tr_count_child =
m_zero
487 this%active = .false.
489 this%has_child = .false.
492 this%likwid = .false.
505 this%initialized = .
true.
508 if (
allocated(
prof_vars%likwid_regions))
then
509 this%likwid = any(
prof_vars%likwid_regions == this%label)
516 do iprofile = 1,
prof_vars%last_profile - 1
517 if (
prof_vars%profile_list(iprofile)%p%label == this%label)
then
518 message(1) =
"Label "//label//
" used more than once."
532 character(*),
intent(in) :: label
533 logical,
optional,
intent(in) :: exclude
537 class(*),
pointer :: profile_p
540 character(len=len(label)) :: label_
549 if (.not. found)
then
555 select type(this => profile_p)
557 if (.not. this%initialized)
then
561 if (this%active)
then
562 message(1) =
" The region with label "//trim(label)//
" is already active."
563 message(2) =
" This is likely caused by a missing or incorrect profiling_out call."
566 assert(.not. this%active)
571 if (
associated(
prof_vars%current%p))
then
574 this%parent%has_child(this%index) = .
true.
580 this%op_count_current =
m_zero
581 this%tr_count_current =
m_zero
582 this%op_count_child_current =
m_zero
583 this%tr_count_child_current =
m_zero
586 this%entry_time = now
590#if defined(HAVE_NVTX) || (defined(HAVE_HIP) && defined(__HIP_PLATFORM_AMD__))
595 if (this%likwid)
call likwid_markerstartregion(trim(label_))
607 character(*),
intent(in) :: label
609 real(real64) :: now, time_spent
610 class(*),
pointer :: profile_p
619 if (.not. found)
then
620 message(1) =
" No profiling region with label "//trim(label)//
" found."
621 message(2) =
" This is likely caused by a missing or incorrect profiling_in call."
626 select type(this => profile_p)
628 assert(this%initialized)
630 this%active = .false.
633 time_spent = now - this%entry_time
634 this%total_time = this%total_time + time_spent
635 this%self_time = this%self_time + time_spent
636 this%count = this%count + 1
637 if (time_spent < this%min_time)
then
638 this%min_time = time_spent
641 this%op_count = this%op_count + this%op_count_current
642 this%tr_count = this%tr_count + this%tr_count_current
643 this%op_count_child = this%op_count_child + this%op_count_child_current
644 this%tr_count_child = this%tr_count_child + this%tr_count_child_current
646 if (
associated(this%parent))
then
648 this%parent%self_time = this%parent%self_time - time_spent
649 if (this%exclude) this%parent%total_time = this%parent%total_time - time_spent
652 this%parent%op_count_child_current = this%parent%op_count_child_current &
653 + this%op_count_current + this%op_count_child_current
654 this%parent%tr_count_child_current = this%parent%tr_count_child_current &
655 + this%tr_count_current + this%tr_count_child_current
657 this%parent%timings(this%index) = this%parent%timings(this%index) + time_spent
667 if (this%likwid)
call likwid_markerstopregion(trim(this%label))
671#if defined(HAVE_NVTX) || (defined(HAVE_HIP) && defined(__HIP_PLATFORM_AMD__))
681 integer,
intent(in) :: ops
686 prof_vars%current%p%op_count_current =
prof_vars%current%p%op_count_current + real(ops, real64)
693 real(4),
intent(in) :: ops
698 prof_vars%current%p%op_count_current =
prof_vars%current%p%op_count_current + real(ops, real64)
705 real(real64),
intent(in) :: ops
718 integer(int64),
intent(in) :: trf
719 integer,
intent(in) :: type
724 prof_vars%current%p%tr_count_current =
prof_vars%current%p%tr_count_current + 4*real(trf, real64)
731 integer(int64),
intent(in) :: trf
732 integer(int64),
intent(in) :: type
737 prof_vars%current%p%tr_count_current =
prof_vars%current%p%tr_count_current + 8*real(trf, real64)
744 integer(int64),
intent(in) :: trf
745 real(4),
intent(in) :: type
750 prof_vars%current%p%tr_count_current =
prof_vars%current%p%tr_count_current + 4*real(trf, real64)
758 integer(int64),
intent(in) :: trf
759 real(real64),
intent(in) :: type
764 prof_vars%current%p%tr_count_current =
prof_vars%current%p%tr_count_current + 8*real(trf, real64)
772 integer(int64),
intent(in) :: trf
773 complex(4),
intent(in) :: type
786 integer(int64),
intent(in) :: trf
787 complex(real64),
intent(in) :: type
792 prof_vars%current%p%tr_count_current =
prof_vars%current%p%tr_count_current + 16*real(trf, real64)
800 integer(int64),
intent(in) :: trf
801 type(
type_t),
intent(in) :: type
814 integer,
intent(in) :: trf
815 integer,
intent(in) :: type
820 prof_vars%current%p%tr_count_current =
prof_vars%current%p%tr_count_current + 4*real(trf, real64)
827 integer,
intent(in) :: trf
828 integer(int64),
intent(in) :: type
833 prof_vars%current%p%tr_count_current =
prof_vars%current%p%tr_count_current + 8*real(trf, real64)
840 integer,
intent(in) :: trf
841 real(4),
intent(in) :: type
846 prof_vars%current%p%tr_count_current =
prof_vars%current%p%tr_count_current + 4*real(trf, real64)
854 integer,
intent(in) :: trf
855 real(real64),
intent(in) :: type
860 prof_vars%current%p%tr_count_current =
prof_vars%current%p%tr_count_current + 8*real(trf, real64)
868 integer,
intent(in) :: trf
869 complex(4),
intent(in) :: type
874 prof_vars%current%p%tr_count_current =
prof_vars%current%p%tr_count_current + 8*real(trf, real64)
882 integer,
intent(in) :: trf
883 complex(real64),
intent(in) :: type
888 prof_vars%current%p%tr_count_current =
prof_vars%current%p%tr_count_current + 16*real(trf, real64)
896 integer,
intent(in) :: trf
897 type(
type_t),
intent(in) :: type
907 real(real64) function profile_total_time(this)
910 push_sub(profile_total_time)
911 profile_total_time = this%total_time
913 pop_sub(profile_total_time)
918 real(real64) function profile_self_time(this)
929 real(real64) function profile_total_time_per_call(this)
940 real(real64) function profile_min_time(this)
951 real(real64) function profile_self_time_per_call(this)
962 real(real64) function profile_total_throughput(this)
965 push_sub(profile_throughput)
967 if (this%total_time > epsilon(this%total_time))
then
973 pop_sub(profile_throughput)
979 real(real64) function profile_total_bandwidth(this)
982 push_sub(profile_bandwidth)
984 if (this%total_time > epsilon(this%total_time))
then
990 pop_sub(profile_bandwidth)
998 push_sub(profile_throughput)
1000 if (this%self_time > epsilon(this%self_time))
then
1006 pop_sub(profile_throughput)
1014 push_sub(profile_bandwidth)
1016 if (this%self_time > epsilon(this%self_time))
then
1022 pop_sub(profile_bandwidth)
1056 type(namespace_t),
intent(in) :: namespace
1060 real(real64) :: total_time
1062 character(len=256) :: filename
1063 real(real64),
allocatable :: selftime(:)
1064 integer,
allocatable :: position(:)
1065 class(*),
pointer :: profile_p
1068 if (.not. in_profiling_mode)
return
1071 call mpi_world%barrier()
1073 if (.not.
prof_vars%all_nodes .and. .not. mpi_world%is_root())
then
1079 iunit = io_open(trim(filename), namespace, action=
'write', die=.false.)
1080 if (iunit == -1)
then
1081 message(1) =
'Failed to open file ' // trim(filename) //
' to write profiling results.'
1082 call messages_warning(1)
1087 write(iunit,
'(2a)') &
1088 ' CUMULATIVE TIME ', &
1090 write(iunit,
'(2a)') &
1091 ' ----------------------------------------------------------', &
1092 '----------------|-------------------------------------------------------------'
1093 write(iunit,
'(2a)') &
1094 'TAG NUM_CALLS TOTAL_TIME TIME_PER_CALL MIN_TIME ', &
1095 ' MFLOPS MBYTES/S %TIME | TOTAL_TIME TIME_PER_CALL MFLOPS MBYTES/S %TIME'
1096 write(iunit,
'(2a)') &
1097 '===================================================================================================', &
1098 '=================|============================================================='
1100 profile_p => sphash_lookup(
profiling_map,
"COMPLETE_RUN", found)
1102 select type(complete_run => profile_p)
1107 safe_allocate(selftime(1:
prof_vars%last_profile))
1108 safe_allocate(position(1:
prof_vars%last_profile))
1115 call sort(selftime, position)
1118 prof =>
prof_vars%profile_list(position(ii))%p
1119 if (.not. prof%initialized)
then
1120 write(message(1),
'(a,i6,a)')
"Internal error: Profile number ", position(ii),
" is not initialized."
1121 call messages_fatal(1)
1123 if (prof%active)
then
1124 write(message(1),
'(a)')
"Internal error: Profile '" // trim(
profile_label(prof)) // &
1125 "' is active, i.e. profiling_out was not called."
1126 call messages_warning(1)
1131 write(iunit,
'(a,i14,3f16.6,2f10.1,f8.1,a,2f16.6,2f10.1,f8.1)') &
1148 call io_close(iunit)
1152 iunit = io_open(trim(filename), namespace, action=
'write', die=.false.)
1153 if (iunit == -1)
then
1154 message(1) =
'Failed to open file ' // trim(filename) //
' to write profiling results.'
1155 call messages_warning(1)
1159 write(iunit,
'(2a)')
'schema: [num_calls, total_time, total_throughput, ', &
1160 'total_bandwidth, self_time, self_throughput, self_bandwidth]'
1161 write(iunit,
'(a)')
'data:'
1164 prof =>
prof_vars%profile_list(position(ii))%p
1166 write(iunit,
'(a,a,a,i6,a,e10.3,a,e10.3,a,e10.3,a,e10.3,a,e10.3,a,e10.3,a)') &
1177 call io_close(iunit)
1180 safe_deallocate_a(selftime)
1181 safe_deallocate_a(position)
1185 iunit = io_open(trim(filename), namespace, action=
'write', die=.false.)
1186 if (iunit == -1)
then
1187 message(1) =
'Failed to open file ' // trim(filename) //
' to write profiling results.'
1188 call messages_warning(1)
1192 write(iunit,
'(a40,a11,a11,a12)') &
1193 "Tree level, region ", &
1194 "% of total ",
"% of parent", &
1197 select type(complete_run => profile_p)
1200 write(iunit,
'(a,a25,a,f8.2,a,f8.2,a,f12.4)') &
1201 repeat(
'-', 0) //
'| ', &
1203 repeat(
' ', 15-0-2), &
1209 write(iunit,
'(a)')
"// modeline for vim to enable folding (put in ~/.vimrc: set modeline modelineexpr)"
1210 write(iunit,
'(a)')
"// vim: fdm=expr fde=getline(v\:lnum)=~'.*\|.*'?len(split(getline(v\:lnum))[0])-1\:0"
1211 call io_close(iunit)
1219 integer,
intent(in) :: level
1220 real(real64),
intent(in) :: total_time
1221 integer,
intent(in) :: iunit
1223 integer :: ichild, width
1229 if (profile%has_child(ichild))
then
1232 write(iunit,
'(a,a25,a,f8.2,a,f8.2,a,f12.4)') &
1233 repeat(
'-', level) //
'| ', &
1235 repeat(
' ', width-level-2), &
1236 profile%timings(ichild)/total_time * 100,
"% ", &
1237 profile%timings(ichild)/profile%total_time * 100,
"% ", &
1238 profile%timings(ichild)
1240 level+1, total_time, iunit)
1250 character(len=*),
intent(in) :: var
1251 character(len=*),
intent(in) :: file
1252 integer,
intent(in) :: line
1253 character(len=*),
intent(out) :: str
1255 integer :: ii, jj, nn
1260 if (var(jj:jj) ==
')')
then
1262 do ii = len(var)-1, 1, -1
1264 if (var(ii:ii) ==
')') nn = nn + 1
1265 if (var(ii:ii) ==
'(') nn = nn - 1
1269 message(1) =
"Internal Error in profiling_memory_log"
1270 call messages_fatal(1)
1273 ii = index(file,
'/', back=.
true.)+1
1275 write(str,
'(4a,i5,a)') var(1:jj),
"(", trim(file(ii:len(file))),
":", line,
")"
1283 character(len=*),
intent(in) :: type
1284 character(len=*),
intent(in) :: var
1285 character(len=*),
intent(in) :: file
1286 integer,
intent(in) :: line
1287 integer(int64),
intent(in) :: size
1289 character(len=256) :: str
1290 integer(int64) :: mem
1297 mem = loct_get_memory_usage()
1299 write(
prof_vars%mem_iunit,
'(f16.6,a16,3i16,a70)') loct_clock() -
prof_vars%start_time, &
1300 trim(type),
size,
prof_vars%total_memory, mem, trim(str)
1307 character(len=*),
intent(in) :: var
1308 character(len=*),
intent(in) :: file
1309 integer,
intent(in) :: line
1310 integer(int64),
intent(in) :: size_
1313 integer(int64) :: size
1314 character(len=256) :: str
1329 message(1) =
"Memory limit set in the input file was passed"
1330 call messages_fatal(1)
1343 if (str ==
prof_vars%large_vars(ii))
then
1344 if (
size >
prof_vars%large_vars_size(ii))
then
1361 if (
size >
prof_vars%large_vars_size(ii))
then
1378 character(len=*),
intent(in) :: var
1379 character(len=*),
intent(in) :: file
1380 integer,
intent(in) :: line
1381 integer(int64),
intent(in) :: size
1400 character(len=1),
intent(in) :: c_in
1401 character(len=1) :: c_out
1403 integer,
parameter :: difference=iachar(
'a')-iachar(
'A'), la=iachar(
'a'), lz=iachar(
'z')
1404 integer :: ascii_decimal
1407 ascii_decimal = iachar(c_in)
1408 if (ascii_decimal >= la .and. ascii_decimal <= lz)
then
1409 ascii_decimal = ascii_decimal - difference
1411 c_out = achar(ascii_decimal)
1415 pure function to_upper(string)
result(upper_string)
1416 character(len=*),
intent(in) :: string
1417 character(len=len(string)) :: upper_string
1422 do i = 1, len(string)
real(real64), parameter, public m_huge
real(real64), parameter, public m_zero
logical pure function, public not_in_openmp()
logical, public in_profiling_mode
Same for profiling mode.
integer(int64), save io_open_count
subroutine, public io_close(iunit, grp)
integer(int64) pure function, public io_get_close_count()
integer(int64), save io_close_count
subroutine, public io_mkdir(fname, namespace, parents)
integer(int64) pure function, public io_get_open_count()
integer function, public io_open(file, namespace, action, status, form, position, die, recl, grp)
System information (time, memory, sysname)
character(kind=c_char, len=1) function, dimension(len_trim(f_string)+1), private string_f_to_c(f_string)
convert a Fortran string to a C string
subroutine, public messages_print_with_emphasis(msg, iunit, namespace)
character(len=512), private msg
subroutine, public messages_warning(no_lines, all_nodes, namespace)
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
subroutine, public messages_input_error(namespace, var, details, row, column)
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
real(real64) function, public mpi_get_wtime()
. Returns an elapsed time on the calling processor.
type(mpi_grp_t), public mpi_world
subroutine, public parse_block_string(blk, l, c, res, convert_to_c)
integer function, public parse_block(namespace, name, blk, check_varinfo_)
/* To use the profiling module, call profiling_in and profiling_out with the label of the region as a...
subroutine profiling_count_tran_int(trf, type)
subroutine profiling_count_tran_int_8_l(trf, type)
subroutine, public profiling_end(namespace)
type(profile_vars_t), target, save, public prof_vars
real(real64) function profile_self_bandwidth(this)
real(real64) function profile_self_time(this)
pure character(len=1) function char_to_upper(c_in)
integer, parameter max_memory_vars
subroutine iprofiling_count_operations(ops)
integer, parameter, public profiling_memory
subroutine profiling_memory_log(type, var, file, line, size)
integer, parameter, public profiling_likwid
subroutine profiling_output(namespace)
Write profiling results of each node to profiling.NNN/profiling.nnn The format of each line is tag-la...
integer, parameter, public profiling_memory_full
subroutine profiling_count_tran_real_8(trf, type)
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
subroutine profiling_count_tran_real_8_l(trf, type)
subroutine profiling_count_tran_int_8(trf, type)
subroutine rprofiling_count_operations(ops)
integer, parameter, public profiling_io
real(real64) function profile_self_throughput(this)
subroutine profiling_count_tran_complex_8_l(trf, type)
subroutine profile_init(this, label)
Initialize a profile object and add it to the list.
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
real(real64) function profile_total_throughput(this)
subroutine, public profiling_init(namespace)
Create profiling subdirectory.
subroutine profiling_count_tran_real_4_l(trf, type)
subroutine dprofiling_count_operations(ops)
pure character(len=len(string)) function to_upper(string)
real(real64) function profile_total_time_per_call(this)
integer, parameter max_profiles
Max. number of tags.
subroutine profiling_count_tran_int_l(trf, type)
subroutine profiling_count_tran_complex_4_l(trf, type)
subroutine profiling_count_tran_real_4(trf, type)
type(sphash_t), save profiling_map
integer function profile_num_calls(this)
subroutine profiling_count_tran_type(trf, type)
real(real64) function profile_min_time(this)
subroutine, public profiling_memory_deallocate(var, file, line, size)
character(label_length) function profile_label(this)
real(real64) function profile_self_time_per_call(this)
subroutine, public profiling_memory_allocate(var, file, line, size_)
subroutine profiling_count_tran_type_l(trf, type)
subroutine profiling_make_position_str(var, file, line, str)
subroutine profiling_count_tran_complex_8(trf, type)
subroutine profiling_count_tran_complex_4(trf, type)
real(real64) function profile_total_bandwidth(this)
real(real64) function profile_total_time(this)
This module is intended to contain "only mathematical" functions and procedures.
This module implements a simple hash table for string valued keys and integer values using the C++ ST...
subroutine, public sphash_init(h)
Initialize a hash table h with size entries. Since we use separate chaining, the number of entries in...
subroutine, public sphash_insert(h, key, val, clone)
Insert a (key, val) pair into the hash table h. If clone=.true., the object will be copied.
subroutine, public sphash_end(h)
Free a hash table.
class(*) function, pointer, public sphash_lookup(h, key, found)
Look up a value in the hash table h. If found is present, it indicates if key could be found in the t...
integer pure function, public types_get_size(this)
subroutine get_output_dir()
recursive subroutine output_tree_level(profile, level, total_time, iunit)