Commit e81aabad authored by Lorenz Huedepohl's avatar Lorenz Huedepohl
Browse files

Add Doxygen support to configure, update documentation

parent 0896521b
...@@ -28,3 +28,5 @@ stamp-h1 ...@@ -28,3 +28,5 @@ stamp-h1
ftimings-*-*.pc ftimings-*-*.pc
ftimings_*_test ftimings_*_test
ftimings.mod ftimings.mod
docs/
Doxyfile
This diff is collapsed.
...@@ -30,3 +30,5 @@ ftimings_@FC@_test_LDADD = libftimings-@FTIMINGS_API_VERSION@-@FC@.la ...@@ -30,3 +30,5 @@ ftimings_@FC@_test_LDADD = libftimings-@FTIMINGS_API_VERSION@-@FC@.la
# other files to distribute # other files to distribute
filesdir = $(datadir)/@PACKAGE@-@FC@/examples filesdir = $(datadir)/@PACKAGE@-@FC@/examples
files_DATA = test/test_timings.F90 files_DATA = test/test_timings.F90
include doxygen.am
...@@ -22,13 +22,21 @@ LT_INIT ...@@ -22,13 +22,21 @@ LT_INIT
PKG_PROG_PKG_CONFIG PKG_PROG_PKG_CONFIG
DX_PDF_FEATURE(OFF)
DX_PS_FEATURE(OFF)
DX_MAN_FEATURE(ON)
DX_HTML_FEATURE(ON)
DX_INIT_DOXYGEN([ftimings], [Doxyfile], [docs])
AC_SUBST([FTIMINGS_SO_VERSION], [0:1:0]) AC_SUBST([FTIMINGS_SO_VERSION], [0:1:0])
AC_SUBST([FTIMINGS_API_VERSION], [0.1]) AC_SUBST([FTIMINGS_API_VERSION], [0.1])
AC_SUBST([AM_CFLAGS]) AC_SUBST([AM_CFLAGS])
AC_SUBST([AM_FCFLAGS]) AC_SUBST([AM_FCFLAGS])
AC_SUBST([AM_LDFLAGS]) AC_SUBST([AM_LDFLAGS])
AC_SUBST([DOXYGEN_OUTPUT_DIR], [docs])
AC_CONFIG_FILES([Makefile AC_CONFIG_FILES([Makefile
Doxyfile
ftimings-${FTIMINGS_API_VERSION}-${FC}.pc:ftimings.pc.in ftimings-${FTIMINGS_API_VERSION}-${FC}.pc:ftimings.pc.in
]) ])
AC_OUTPUT AC_OUTPUT
## --------------------------------- ##
## Format-independent Doxygen rules. ##
## --------------------------------- ##
if DX_COND_doc
## ------------------------------- ##
## Rules specific for HTML output. ##
## ------------------------------- ##
if DX_COND_html
DX_CLEAN_HTML = @DX_DOCDIR@/html
endif DX_COND_html
## ------------------------------ ##
## Rules specific for CHM output. ##
## ------------------------------ ##
if DX_COND_chm
DX_CLEAN_CHM = @DX_DOCDIR@/chm
if DX_COND_chi
DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi
endif DX_COND_chi
endif DX_COND_chm
## ------------------------------ ##
## Rules specific for MAN output. ##
## ------------------------------ ##
if DX_COND_man
DX_CLEAN_MAN = @DX_DOCDIR@/man
endif DX_COND_man
## ------------------------------ ##
## Rules specific for RTF output. ##
## ------------------------------ ##
if DX_COND_rtf
DX_CLEAN_RTF = @DX_DOCDIR@/rtf
endif DX_COND_rtf
## ------------------------------ ##
## Rules specific for XML output. ##
## ------------------------------ ##
if DX_COND_xml
DX_CLEAN_XML = @DX_DOCDIR@/xml
endif DX_COND_xml
## ----------------------------- ##
## Rules specific for PS output. ##
## ----------------------------- ##
if DX_COND_ps
DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps
DX_PS_GOAL = doxygen-ps
doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps
@DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag
cd @DX_DOCDIR@/latex; \
rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
$(DX_LATEX) refman.tex; \
$(MAKEINDEX_PATH) refman.idx; \
$(DX_LATEX) refman.tex; \
countdown=5; \
while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
refman.log > /dev/null 2>&1 \
&& test $$countdown -gt 0; do \
$(DX_LATEX) refman.tex; \
countdown=`expr $$countdown - 1`; \
done; \
$(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi
endif DX_COND_ps
## ------------------------------ ##
## Rules specific for PDF output. ##
## ------------------------------ ##
if DX_COND_pdf
DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf
DX_PDF_GOAL = doxygen-pdf
doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf
@DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag
cd @DX_DOCDIR@/latex; \
rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
$(DX_PDFLATEX) refman.tex; \
$(DX_MAKEINDEX) refman.idx; \
$(DX_PDFLATEX) refman.tex; \
countdown=5; \
while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
refman.log > /dev/null 2>&1 \
&& test $$countdown -gt 0; do \
$(DX_PDFLATEX) refman.tex; \
countdown=`expr $$countdown - 1`; \
done; \
mv refman.pdf ../@PACKAGE@.pdf
endif DX_COND_pdf
## ------------------------------------------------- ##
## Rules specific for LaTeX (shared for PS and PDF). ##
## ------------------------------------------------- ##
if DX_COND_latex
DX_CLEAN_LATEX = @DX_DOCDIR@/latex
endif DX_COND_latex
.PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL)
.INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag
doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
@DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS)
rm -rf @DX_DOCDIR@
$(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG)
DX_CLEANFILES = \
@DX_DOCDIR@/@PACKAGE@.tag \
-r \
$(DX_CLEAN_HTML) \
$(DX_CLEAN_CHM) \
$(DX_CLEAN_CHI) \
$(DX_CLEAN_MAN) \
$(DX_CLEAN_RTF) \
$(DX_CLEAN_XML) \
$(DX_CLEAN_PS) \
$(DX_CLEAN_PDF) \
$(DX_CLEAN_LATEX)
endif DX_COND_doc
!> Ftimings
!>
!> An almost pure-fortran attempt to play with graph structures :)
module ftimings module ftimings
use, intrinsic :: iso_c_binding, only : C_INT64_T, C_DOUBLE use, intrinsic :: iso_c_binding, only : C_INT64_T, C_DOUBLE
implicit none implicit none
save save
public timer_t
private private
! this is mainly needed for Doxygen...
public timer_start, timer_stop, timer_free, timer_print, &
timer_enable, timer_disable, timer_is_enabled, &
timer_in_entries, timer_get, timer_since, timer_sort
integer, parameter, private :: name_length = 32 integer, parameter, private :: name_length = 32
integer, parameter, private :: rk = C_DOUBLE integer, parameter, private :: rk = C_DOUBLE
...@@ -17,10 +24,12 @@ module ftimings ...@@ -17,10 +24,12 @@ module ftimings
end function end function
end interface end interface
! Private type node_t, representing a graph node
!
type node_t type node_t
character(len=name_length) :: name character(len=name_length) :: name ! Descriptive name, used when printing the timings
integer(kind=C_INT64_T) :: micros integer(kind=C_INT64_T) :: micros ! Cumulative microseconds spent in this node
integer :: count integer :: count ! Number of calls
type(node_t), pointer :: firstChild => NULL() type(node_t), pointer :: firstChild => NULL()
type(node_t), pointer :: lastChild => NULL() type(node_t), pointer :: lastChild => NULL()
type(node_t), pointer :: parent => NULL() type(node_t), pointer :: parent => NULL()
...@@ -37,11 +46,37 @@ module ftimings ...@@ -37,11 +46,37 @@ module ftimings
procedure, pass :: sort_children => node_sort_children procedure, pass :: sort_children => node_sort_children
end type end type
type timer_t !> Type for a timer instance.
!>
!> Typical usage:
!> \code{.f90}
!> type(timer_t) :: timer
!>
!> call timer%enable()
!>
!> call timer%start("section")
!> ...
!> call timer%start("subsection")
!> ...
!> call timer%stop("subsection")
!> ...
!> call timer%stop("section")
!>
!> call timer%print()
!> \endcode
!>
!> Every first call to timer%start() at a certain point in the graph
!> allocates a small amount of memory. If the timer is no longer needed,
!> all that memory can be freed again with
!>
!> \code{.f90}
!> call timer%free()
!> \endcode
type, public :: timer_t
private private
logical :: active = .false. logical :: active = .false. !< If set to .false., most operations return immediately without any action
type(node_t), pointer :: root => NULL() type(node_t), pointer :: root => NULL() !< Start of graph
type(node_t), pointer :: current_node => NULL() type(node_t), pointer :: current_node => NULL() !< Current position in the graph
contains contains
procedure, pass :: start => timer_start procedure, pass :: start => timer_start
procedure, pass :: stop => timer_stop procedure, pass :: stop => timer_stop
...@@ -56,8 +91,8 @@ module ftimings ...@@ -56,8 +91,8 @@ module ftimings
procedure, pass :: sort => timer_sort procedure, pass :: sort => timer_sort
end type end type
character(len=name_length), parameter :: own = "(own)" character(len=name_length), private, parameter :: own = "(own)"
character(len=name_length), parameter :: below = "(below threshold)" character(len=name_length), private, parameter :: below = "(below threshold)"
contains contains
...@@ -199,22 +234,25 @@ module ftimings ...@@ -199,22 +234,25 @@ module ftimings
enddo enddo
end function end function
subroutine node_print_line(indent_level, label, number, total) subroutine node_print_line(indent_level, label, number, total, unit)
integer, intent(in) :: indent_level integer, intent(in) :: indent_level
character(len=name_length), intent(in) :: label character(len=name_length), intent(in) :: label
integer(kind=C_INT64_T), intent(in) :: number, total integer(kind=C_INT64_T), intent(in) :: number, total
character(len=64) :: format_spec character(len=64) :: format_spec
integer :: unit
write(format_spec,'("(",i0,"x,""|_ "",a",i0,",2x,f10.6,10x,f5.3)")') (indent_level + 1) * 2 + 2, name_length write(format_spec,'("(",i0,"x,""|_ "",a",i0,",2x,f10.6,10x,f5.3)")') (indent_level + 1) * 2 + 2, name_length
write(*,format_spec) label, real(number, kind=rk) * 1e-6_rk, real(number, kind=rk) / real(total, kind=rk) write(unit,format_spec) label, real(number, kind=rk) * 1e-6_rk, real(number, kind=rk) / real(total, kind=rk)
end subroutine end subroutine
recursive subroutine node_print_graph(self, indent_level, threshold, is_sorted, total) recursive subroutine node_print_graph(self, indent_level, threshold, is_sorted, total, unit)
use, intrinsic :: iso_fortran_env, only : output_unit
class(node_t), intent(in) :: self class(node_t), intent(in) :: self
integer, intent(in) :: indent_level integer, intent(in) :: indent_level
real(kind=rk), intent(in), optional :: threshold real(kind=rk), intent(in), optional :: threshold
logical, intent(in), optional :: is_sorted logical, intent(in), optional :: is_sorted
integer(kind=C_INT64_T), intent(in), optional :: total integer(kind=C_INT64_T), intent(in), optional :: total
integer, optional :: unit
type(node_t), pointer :: node type(node_t), pointer :: node
character(len=64) :: format_spec character(len=64) :: format_spec
...@@ -223,6 +261,8 @@ module ftimings ...@@ -223,6 +261,8 @@ module ftimings
integer(kind=C_INT64_T) :: current_us, own_us, below_threshold_us, total_act integer(kind=C_INT64_T) :: current_us, own_us, below_threshold_us, total_act
real(kind=rk) :: threshold_act real(kind=rk) :: threshold_act
logical :: is_sorted_act, print_own, print_threshold logical :: is_sorted_act, print_own, print_threshold
integer :: unit_act
if (present(threshold)) then if (present(threshold)) then
threshold_act = threshold threshold_act = threshold
...@@ -244,6 +284,12 @@ module ftimings ...@@ -244,6 +284,12 @@ module ftimings
total_act = current_us total_act = current_us
endif endif
if (present(unit)) then
unit_act = unit
else
unit_act = output_unit
endif
own_us = current_us - self%sum_of_children() own_us = current_us - self%sum_of_children()
below_threshold_us = self%sum_of_children_below(threshold) below_threshold_us = self%sum_of_children_below(threshold)
...@@ -266,18 +312,29 @@ module ftimings ...@@ -266,18 +312,29 @@ module ftimings
do i = len(trim(name)) + 2, name_length do i = len(trim(name)) + 2, name_length
name(i:i) = "." name(i:i) = "."
end do end do
write(*,format_spec) name, real(current_us, kind=rk) * 1e-6_rk, real(current_us, kind=rk) / real(total_act, kind=rk) write(unit_act,format_spec) &
name, real(current_us, kind=rk) * 1e-6_rk, real(current_us, kind=rk) / real(total_act, kind=rk)
else else
return return
endif endif
! \todo this is very clumsy:
!
! we have to insert the (own) and (below threshold) entries
! i) at the start, if printing unsorted or
! ii) at the place they would appear, if sorted
!
! at the moment a lot of copied lines are necessary for this,
! and there is no correct sorting between (own) and (below threshold)
! in all cases
!
if ((.not. is_sorted_act) .and. print_own) then if ((.not. is_sorted_act) .and. print_own) then
call node_print_line(indent_level, own, own_us, current_us) call node_print_line(indent_level, own, own_us, current_us, unit_act)
print_own = .false. print_own = .false.
endif endif
if ((.not. is_sorted_act) .and. print_threshold) then if ((.not. is_sorted_act) .and. print_threshold) then
call node_print_line(indent_level, below, below_threshold_us, current_us) call node_print_line(indent_level, below, below_threshold_us, current_us, unit_act)
print_threshold = .false. print_threshold = .false.
endif endif
...@@ -286,49 +343,81 @@ module ftimings ...@@ -286,49 +343,81 @@ module ftimings
if (print_own) then if (print_own) then
if (node%get_micros() <= own_us) then if (node%get_micros() <= own_us) then
call node_print_line(indent_level, own, own_us, current_us) call node_print_line(indent_level, own, own_us, current_us, unit_act)
print_own = .false. print_own = .false.
endif endif
endif endif
if (print_threshold) then if (print_threshold) then
if (node%get_micros() <= below_threshold_us) then if (node%get_micros() <= below_threshold_us) then
call node_print_line(indent_level, below, below_threshold_us, current_us) call node_print_line(indent_level, below, below_threshold_us, current_us, unit_act)
print_threshold = .false. print_threshold = .false.
endif endif
endif endif
call node%print_graph(indent_level + 1, threshold, is_sorted, current_us) call node%print_graph(indent_level + 1, threshold, is_sorted, current_us, unit_act)
node => node%nextSibling node => node%nextSibling
end do end do
if (print_own) then if (print_own) then
call node_print_line(indent_level, own, own_us, current_us) call node_print_line(indent_level, own, own_us, current_us, unit_act)
endif endif
if (print_threshold) then if (print_threshold) then
call node_print_line(indent_level, below, below_threshold_us, current_us) call node_print_line(indent_level, below, below_threshold_us, current_us, unit_act)
endif endif
end subroutine end subroutine
! Public module interface: !> Active the timer, without this, most methods are no-ops.
!>
!> \implements timer_t::enable
subroutine timer_enable(self) subroutine timer_enable(self)
class(timer_t), intent(inout), target :: self class(timer_t), intent(inout), target :: self
self%active = .true. self%active = .true.
end subroutine end subroutine
!> Deactive the timer
!>
!> \implements timer_t::disable
subroutine timer_disable(self) subroutine timer_disable(self)
class(timer_t), intent(inout), target :: self class(timer_t), intent(inout), target :: self
self%active = .false. self%active = .false.
end subroutine end subroutine
!> \implements timer_t::is_enabled
function timer_is_enabled(self) result(is) function timer_is_enabled(self) result(is)
class(timer_t), intent(inout), target :: self class(timer_t), intent(inout), target :: self
logical :: is logical :: is
is = self%active is = self%active
end function end function
!> Start a timing section
!>
!> \param name A descriptive name
!> \param replace If .true. (default .false.), replace any entries at the
!> current position with the same name. If .false., add the
!> time to a possibly existing entry
!>
!> Care must be taken to balance any invocations of %start() and %stop(), e.g.
!> the following is valid
!>
!> \code{.f90}
!> call timer%start("A")
!> call timer%start("B")
!> call timer%stop("B")
!> call timer%stop("A")
!> \endcode
!>
!> while the following is not
!>
!> \code{.f90}
!> call timer%start("A")
!> call timer%start("B")
!> call timer%stop("A")
!> call timer%stop("B")
!> \endcode
!>
!> \implements timer_t::start
subroutine timer_start(self, name, replace) subroutine timer_start(self, name, replace)
class(timer_t), intent(inout), target :: self class(timer_t), intent(inout), target :: self
character(len=*), intent(in) :: name character(len=*), intent(in) :: name
...@@ -400,6 +489,11 @@ module ftimings ...@@ -400,6 +489,11 @@ module ftimings
end subroutine end subroutine
!> End a timing segment, \sa timer_start
!>
!> \param name The exact same name as was used for %start()
!>
!> \implements timer_t::stop
subroutine timer_stop(self, name) subroutine timer_stop(self, name)
class(timer_t), intent(inout), target :: self class(timer_t), intent(inout), target :: self
character(len=*), intent(in) :: name character(len=*), intent(in) :: name
...@@ -454,6 +548,9 @@ module ftimings ...@@ -454,6 +548,9 @@ module ftimings
end subroutine end subroutine
!> Deallocate all objects associated with (but not including) self
!>
!> \implements timer_t::free
subroutine timer_free(self) subroutine timer_free(self)
class(timer_t), intent(inout), target :: self class(timer_t), intent(inout), target :: self
if (associated(self%root)) then if (associated(self%root)) then
...@@ -463,6 +560,17 @@ module ftimings ...@@ -463,6 +560,17 @@ module ftimings
nullify(self%current_node) nullify(self%current_node)
end subroutine end subroutine
!> Print a timing graph
!>
!> \param name1 If given, first descend one level to the node with name name1
!> \param name2 If given, also descend another level to the node with name2 there
!> \param name3 etc.
!> \param name4 etc.
!> \param threshold If given, subsume any entries with a value of threshold
!> seconds in a single node "(below threshold)"
!> \param is_sorted Assume a sorted graph for inserting "(own)" and "(below threshold)"
!>
!> \implements timer_t::print
subroutine timer_print(self, name1, name2, name3, name4, threshold, is_sorted) subroutine timer_print(self, name1, name2, name3, name4, threshold, is_sorted)
class(timer_t), intent(in), target :: self class(timer_t), intent(in), target :: self
character(len=*), intent(in), optional :: name1, name2, name3, name4 character(len=*), intent(in), optional :: name1, name2, name3, name4
...@@ -512,10 +620,12 @@ module ftimings ...@@ -512,10 +620,12 @@ module ftimings
!> !>
!> \param name1, .., namei-1 The path to the starting node !> \param name1, .., namei-1 The path to the starting node
!> \param namei The name of all sub-entries below this !> \param namei The name of all sub-entries below this
!> node to which are summed together !> node which should be summed together
!> !>
!> For example timer%in_entries("foo", "bar", "parallel") returns !> For example timer%in_entries("foo", "bar", "parallel") returns