Commit 0ebe4802 authored by Lorenz Huedepohl's avatar Lorenz Huedepohl
Browse files

First implementation of an autotuning procedure

To be used like this

   class(elpa_t), pointer      :: e
   class(elpa_autotune_t), pointer :: tune_state

   e => elpa_allocate()
   call e%set(...)
   [...]
   assert_elpa_ok(e%setup())

   tune_state => e%autotune_setup(ELPA_AUTOTUNE_FAST, ELPA_AUTOTUNE_DOMAIN_REAL)

   ! Autotuning loop, continues until all combinations have been tried
   do while (e%autotune_step(tune_state))
     ! Do the steps that are representative of your calculation
     call e%eigenvectors(a, ev, z, error)
   end do

   ! Fix best parameters, and de-allocate the autotune object
   call e%autotune_set_best(tune_state)
   call elpa_autotune_deallocate(tune_state)
parent 67c4a5dd
...@@ -35,6 +35,7 @@ noinst_LTLIBRARIES += libelpa@SUFFIX@_private.la ...@@ -35,6 +35,7 @@ noinst_LTLIBRARIES += libelpa@SUFFIX@_private.la
libelpa@SUFFIX@_private_la_FCFLAGS = $(AM_FCFLAGS) $(FC_MODOUT)private_modules $(FC_MODINC)private_modules libelpa@SUFFIX@_private_la_FCFLAGS = $(AM_FCFLAGS) $(FC_MODOUT)private_modules $(FC_MODINC)private_modules
libelpa@SUFFIX@_private_la_SOURCES = \ libelpa@SUFFIX@_private_la_SOURCES = \
src/elpa_impl.F90 \ src/elpa_impl.F90 \
src/elpa_autotune_impl.F90 \
src/elpa_abstract_impl.F90 \ src/elpa_abstract_impl.F90 \
src/helpers/mod_precision.F90 \ src/helpers/mod_precision.F90 \
src/helpers/mod_mpi.F90 \ src/helpers/mod_mpi.F90 \
......
...@@ -98,3 +98,21 @@ enum ELPA_CONSTANTS { ...@@ -98,3 +98,21 @@ enum ELPA_CONSTANTS {
ELPA_2STAGE_NUMBER_OF_REAL_KERNELS = (0 ELPA_FOR_ALL_2STAGE_REAL_KERNELS(ELPA_ENUM_SUM)), ELPA_2STAGE_NUMBER_OF_REAL_KERNELS = (0 ELPA_FOR_ALL_2STAGE_REAL_KERNELS(ELPA_ENUM_SUM)),
}; };
#define ELPA_FOR_ALL_AUTOTUNE_LEVELS(X, ...) \
X(ELPA_AUTOTUNE_NOT_TUNABLE, 0) \
X(ELPA_AUTOTUNE_FAST, 1) \
X(ELPA_AUTOTUNE_MEDIUM, 2)
enum ELPA_AUTOTUNE_LEVELS {
ELPA_FOR_ALL_AUTOTUNE_LEVELS(ELPA_ENUM_ENTRY)
};
#define ELPA_FOR_ALL_AUTOTUNE_DOMAINS(X, ...) \
X(ELPA_AUTOTUNE_DOMAIN_REAL, 1) \
X(ELPA_AUTOTUNE_DOMAIN_COMPLEX, 2) \
X(ELPA_AUTOTUNE_DOMAIN_ANY, 3)
enum ELPA_AUTOTUNE_DOMAINS {
ELPA_FOR_ALL_AUTOTUNE_DOMAINS(ELPA_ENUM_ENTRY)
};
...@@ -123,3 +123,28 @@ for m, g, t, p, d, s, l in product( ...@@ -123,3 +123,28 @@ for m, g, t, p, d, s, l in product(
matrix_flag[m]] + extra_flags)) matrix_flag[m]] + extra_flags))
print("endif\n" * endifs) print("endif\n" * endifs)
for p, d in product(sorted(prec_flag.keys()), sorted(domain_flag.keys())):
endifs = 0
if (p == "single"):
if (d == "real"):
print("if WANT_SINGLE_PRECISION_REAL")
elif (d == "complex"):
print("if WANT_SINGLE_PRECISION_COMPLEX")
else:
raise Exception("Oh no!")
endifs += 1
name = "test_autotune_{0}_{1}".format(d, p)
print("noinst_PROGRAMS += " + name)
print("check_SCRIPTS += " + name + ".sh")
print(name + "_SOURCES = test/Fortran/test_autotune.F90")
print(name + "_LDADD = $(test_program_ldadd)")
print(name + "_FCFLAGS = $(test_program_fcflags) \\")
print(" " + " \\\n ".join([
domain_flag[d],
prec_flag[p]]))
print("endif\n" * endifs)
...@@ -218,4 +218,10 @@ module elpa ...@@ -218,4 +218,10 @@ module elpa
deallocate(obj) deallocate(obj)
end subroutine end subroutine
subroutine elpa_autotune_deallocate(obj)
class(elpa_autotune_t), pointer :: obj
call obj%destroy()
deallocate(obj)
end subroutine
end module end module
...@@ -72,6 +72,7 @@ module elpa_abstract_impl ...@@ -72,6 +72,7 @@ module elpa_abstract_impl
type, abstract, extends(elpa_t) :: elpa_abstract_impl_t type, abstract, extends(elpa_t) :: elpa_abstract_impl_t
#ifdef HAVE_DETAILED_TIMINGS #ifdef HAVE_DETAILED_TIMINGS
type(timer_t) :: timer type(timer_t) :: timer
type(timer_t) :: autotune_timer
#else #else
type(timer_dummy_t) :: timer type(timer_dummy_t) :: timer
#endif #endif
...@@ -79,21 +80,6 @@ module elpa_abstract_impl ...@@ -79,21 +80,6 @@ module elpa_abstract_impl
logical :: eigenvalues_only logical :: eigenvalues_only
contains contains
! set private fields in the index
generic, public :: set_private => &
elpa_set_private_integer, &
elpa_set_private_double
generic, public :: get_private => &
elpa_get_private_integer, &
elpa_get_private_double
procedure, private :: elpa_set_private_integer
procedure, private :: elpa_set_private_double
procedure, private :: elpa_get_private_integer
procedure, private :: elpa_get_private_double
procedure, public :: elpa_set_integer !< private methods to implement the setting of an integer/double key/value pair procedure, public :: elpa_set_integer !< private methods to implement the setting of an integer/double key/value pair
procedure, public :: elpa_set_double procedure, public :: elpa_set_double
...@@ -110,7 +96,7 @@ module elpa_abstract_impl ...@@ -110,7 +96,7 @@ module elpa_abstract_impl
!> \param name string, the key !> \param name string, the key
!> \param value integer, the value to be set !> \param value integer, the value to be set
!> \result error integer, the error code !> \result error integer, the error code
subroutine elpa_set_private_integer(self, name, value, error) subroutine elpa_set_integer(self, name, value, error)
use iso_c_binding use iso_c_binding
class(elpa_abstract_impl_t) :: self class(elpa_abstract_impl_t) :: self
character(*), intent(in) :: name character(*), intent(in) :: name
...@@ -118,7 +104,7 @@ module elpa_abstract_impl ...@@ -118,7 +104,7 @@ module elpa_abstract_impl
integer, optional :: error integer, optional :: error
integer :: actual_error integer :: actual_error
actual_error = elpa_index_set_int_value_c(self%index, name // c_null_char, value, 0) actual_error = elpa_index_set_int_value_c(self%index, name // c_null_char, value)
if (present(error)) then if (present(error)) then
error = actual_error error = actual_error
...@@ -135,7 +121,7 @@ module elpa_abstract_impl ...@@ -135,7 +121,7 @@ module elpa_abstract_impl
!> \param name string, the key !> \param name string, the key
!> \param value integer, the value of the key/vaue pair !> \param value integer, the value of the key/vaue pair
!> \param error integer, optional, to store an error code !> \param error integer, optional, to store an error code
subroutine elpa_get_private_integer(self, name, value, error) subroutine elpa_get_integer(self, name, value, error)
use iso_c_binding use iso_c_binding
class(elpa_abstract_impl_t) :: self class(elpa_abstract_impl_t) :: self
character(*), intent(in) :: name character(*), intent(in) :: name
...@@ -158,7 +144,7 @@ module elpa_abstract_impl ...@@ -158,7 +144,7 @@ module elpa_abstract_impl
!> \param name string, the key !> \param name string, the key
!> \param value double, the value to be set !> \param value double, the value to be set
!> \result error integer, the error code !> \result error integer, the error code
subroutine elpa_set_private_double(self, name, value, error) subroutine elpa_set_double(self, name, value, error)
use iso_c_binding use iso_c_binding
class(elpa_abstract_impl_t) :: self class(elpa_abstract_impl_t) :: self
character(*), intent(in) :: name character(*), intent(in) :: name
...@@ -166,7 +152,7 @@ module elpa_abstract_impl ...@@ -166,7 +152,7 @@ module elpa_abstract_impl
integer, optional :: error integer, optional :: error
integer :: actual_error integer :: actual_error
actual_error = elpa_index_set_double_value_c(self%index, name // c_null_char, value, 0) actual_error = elpa_index_set_double_value_c(self%index, name // c_null_char, value)
if (present(error)) then if (present(error)) then
error = actual_error error = actual_error
...@@ -182,7 +168,7 @@ module elpa_abstract_impl ...@@ -182,7 +168,7 @@ module elpa_abstract_impl
!> \param name string, the key !> \param name string, the key
!> \param value double, the value of the key/vaue pair !> \param value double, the value of the key/vaue pair
!> \param error integer, optional, to store an error code !> \param error integer, optional, to store an error code
subroutine elpa_get_private_double(self, name, value, error) subroutine elpa_get_double(self, name, value, error)
use iso_c_binding use iso_c_binding
class(elpa_abstract_impl_t) :: self class(elpa_abstract_impl_t) :: self
character(*), intent(in) :: name character(*), intent(in) :: name
...@@ -199,128 +185,4 @@ module elpa_abstract_impl ...@@ -199,128 +185,4 @@ module elpa_abstract_impl
end if end if
end subroutine end subroutine
subroutine elpa_set_integer(self, name, value, error)
use iso_c_binding
class(elpa_abstract_impl_t) :: self
character(*), intent(in) :: name
integer(kind=c_int), intent(in) :: value
integer, optional :: error
integer :: actual_error
integer :: is_private
is_private = elpa_index_int_is_private_c(name // C_NULL_CHAR)
if (is_private == 0) then
call self%set_private(name, value, error)
else
if (is_private == 1) then
actual_error = ELPA_ERROR_ENTRY_NOT_FOUND
else
actual_error = is_private
endif
if (present(error)) then
error = actual_error
else
write(error_unit,'(a)') "ELPA: Error setting option '" // name // "'" // &
" (got: " // elpa_strerr(actual_error) // ") and you did not check for errors!"
endif
endif
end subroutine
subroutine elpa_set_double(self, name, value, error)
use iso_c_binding
class(elpa_abstract_impl_t) :: self
character(*), intent(in) :: name
real(kind=c_double), intent(in) :: value
integer, optional :: error
integer :: actual_error
integer :: is_private
is_private = elpa_index_double_is_private_c(name // C_NULL_CHAR)
if (is_private == 0) then
call self%set_private(name, value, error)
else
if (is_private == 1) then
actual_error = ELPA_ERROR_ENTRY_NOT_FOUND
else
actual_error = is_private
endif
if (present(error)) then
error = actual_error
else
write(error_unit,'(a)') "ELPA: Error setting option '" // name // "'" // &
" (got: " // elpa_strerr(actual_error) // ") and you did not check for errors!"
endif
endif
end subroutine
subroutine elpa_get_integer(self, name, value, error)
use iso_c_binding
class(elpa_abstract_impl_t) :: self
character(*), intent(in) :: name
integer(kind=c_int) :: value
integer, intent(out), optional :: error
integer :: actual_error
integer :: is_private
is_private = elpa_index_int_is_private_c(name // C_NULL_CHAR)
if (is_private == 0) then
call self%get_private(name, value, error)
else
if (is_private == 1) then
actual_error = ELPA_ERROR_ENTRY_NOT_FOUND
else
actual_error = is_private
endif
if (present(error)) then
error = actual_error
else
write(error_unit,'(a)') "ELPA: Error getting option '" // name // "'" // &
" (got: " // elpa_strerr(actual_error) // ") and you did not check for errors!"
endif
endif
end subroutine
subroutine elpa_get_double(self, name, value, error)
use iso_c_binding
class(elpa_abstract_impl_t) :: self
character(*), intent(in) :: name
real(kind=c_double) :: value
integer, intent(out), optional :: error
integer :: actual_error
integer :: is_private
is_private = elpa_index_double_is_private_c(name // C_NULL_CHAR)
if (is_private == 0) then
call self%get_private(name, value, error)
else
if (is_private == 1) then
actual_error = ELPA_ERROR_ENTRY_NOT_FOUND
else
actual_error = is_private
endif
if (present(error)) then
error = actual_error
else
write(error_unit,'(a)') "ELPA: Error getting option '" // name // "'" // &
" (got: " // elpa_strerr(actual_error) // ") and you did not check for errors!"
endif
endif
end subroutine
end module end module
...@@ -136,6 +136,10 @@ module elpa_api ...@@ -136,6 +136,10 @@ module elpa_api
elpa_solve_tridiagonal_d, & !< matrix elpa_solve_tridiagonal_d, & !< matrix
elpa_solve_tridiagonal_f elpa_solve_tridiagonal_f
! Auto-tune
procedure(elpa_autotune_setup_i), deferred, public :: autotune_setup
procedure(elpa_autotune_step_i), deferred, public :: autotune_step
procedure(elpa_autotune_set_best_i), deferred, public :: autotune_set_best
!> \brief These method have to be public, in order to be overrideable in the extension types !> \brief These method have to be public, in order to be overrideable in the extension types
procedure(elpa_set_integer_i), deferred, public :: elpa_set_integer procedure(elpa_set_integer_i), deferred, public :: elpa_set_integer
...@@ -173,6 +177,13 @@ module elpa_api ...@@ -173,6 +177,13 @@ module elpa_api
procedure(elpa_solve_tridiagonal_f_i), deferred, public :: elpa_solve_tridiagonal_f procedure(elpa_solve_tridiagonal_f_i), deferred, public :: elpa_solve_tridiagonal_f
end type elpa_t end type elpa_t
type, abstract :: elpa_autotune_t
private
contains
procedure(elpa_autotune_destroy_i), deferred, public :: destroy
procedure(elpa_autotune_print_i), deferred, public :: print
end type
!> \brief definition of helper function to get C strlen !> \brief definition of helper function to get C strlen
!> Parameters !> Parameters
...@@ -188,6 +199,7 @@ module elpa_api ...@@ -188,6 +199,7 @@ module elpa_api
end function end function
end interface end interface
!> \brief abstract definition of setup method !> \brief abstract definition of setup method
!> Parameters !> Parameters
!> \details !> \details
...@@ -202,6 +214,39 @@ module elpa_api ...@@ -202,6 +214,39 @@ module elpa_api
end function end function
end interface end interface
abstract interface
function elpa_autotune_setup_i(self, level, domain) result(tune_state)
import elpa_t, elpa_autotune_t
implicit none
class(elpa_t), intent(inout), target :: self
integer, intent(in) :: level, domain
class(elpa_autotune_t), pointer :: tune_state
end function
end interface
abstract interface
function elpa_autotune_step_i(self, tune_state) result(unfinished)
import elpa_t, elpa_autotune_t
implicit none
class(elpa_t), intent(inout) :: self
class(elpa_autotune_t), intent(inout), target :: tune_state
logical :: unfinished
end function
end interface
abstract interface
subroutine elpa_autotune_set_best_i(self, tune_state)
import elpa_t, elpa_autotune_t
implicit none
class(elpa_t), intent(inout) :: self
class(elpa_autotune_t), intent(in), target :: tune_state
end subroutine
end interface
!> \brief abstract definition of set method for integer values !> \brief abstract definition of set method for integer values
!> Parameters !> Parameters
!> \details !> \details
...@@ -221,6 +266,7 @@ module elpa_api ...@@ -221,6 +266,7 @@ module elpa_api
end subroutine end subroutine
end interface end interface
!> \brief abstract definition of get method for integer values !> \brief abstract definition of get method for integer values
!> Parameters !> Parameters
!> \details !> \details
...@@ -240,6 +286,7 @@ module elpa_api ...@@ -240,6 +286,7 @@ module elpa_api
end subroutine end subroutine
end interface end interface
!> \brief abstract definition of is_set method for integer values !> \brief abstract definition of is_set method for integer values
!> Parameters !> Parameters
!> \details !> \details
...@@ -257,6 +304,7 @@ module elpa_api ...@@ -257,6 +304,7 @@ module elpa_api
end function end function
end interface end interface
!> \brief abstract definition of can_set method for integer values !> \brief abstract definition of can_set method for integer values
!> Parameters !> Parameters
!> \details !> \details
...@@ -276,6 +324,7 @@ module elpa_api ...@@ -276,6 +324,7 @@ module elpa_api
end function end function
end interface end interface
!> \brief abstract definition of set method for double values !> \brief abstract definition of set method for double values
!> Parameters !> Parameters
!> \details !> \details
...@@ -295,6 +344,7 @@ module elpa_api ...@@ -295,6 +344,7 @@ module elpa_api
end subroutine end subroutine
end interface end interface
!> \brief abstract definition of get method for double values !> \brief abstract definition of get method for double values
!> Parameters !> Parameters
!> \details !> \details
...@@ -314,6 +364,7 @@ module elpa_api ...@@ -314,6 +364,7 @@ module elpa_api
end subroutine end subroutine
end interface end interface
!> \brief abstract definition of associate method for integer pointers !> \brief abstract definition of associate method for integer pointers
!> Parameters !> Parameters
!> \details !> \details
...@@ -351,6 +402,7 @@ module elpa_api ...@@ -351,6 +402,7 @@ module elpa_api
end function end function
end interface end interface
!> \brief abstract definition of print method for timer !> \brief abstract definition of print method for timer
!> Parameters !> Parameters
!> \details !> \details
...@@ -364,6 +416,7 @@ module elpa_api ...@@ -364,6 +416,7 @@ module elpa_api
end subroutine end subroutine
end interface end interface
!> \brief abstract definition of the start method for timer !> \brief abstract definition of the start method for timer
!> Parameters !> Parameters
!> \details !> \details
...@@ -378,12 +431,12 @@ module elpa_api ...@@ -378,12 +431,12 @@ module elpa_api
end subroutine end subroutine
end interface end interface
!> \brief abstract definition of the stop method for timer !> \brief abstract definition of the stop method for timer
!> Parameters !> Parameters
!> \details !> \details
!> \param self class(elpa_t): the ELPA object !> \param self class(elpa_t): the ELPA object
!> \param name character(len=*) the name of the entry int the timer tree !> \param name character(len=*) the name of the entry int the timer tree
abstract interface abstract interface
subroutine elpa_timer_stop_i(self, name) subroutine elpa_timer_stop_i(self, name)
import elpa_t import elpa_t
...@@ -429,6 +482,7 @@ module elpa_api ...@@ -429,6 +482,7 @@ module elpa_api
end subroutine end subroutine
end interface end interface
!> \brief abstract definition of interface to solve single real eigenvalue problem !> \brief abstract definition of interface to solve single real eigenvalue problem
!> !>
!> The dimensions of the matrix a (locally ditributed and global), the block-cyclic distribution !> The dimensions of the matrix a (locally ditributed and global), the block-cyclic distribution
...@@ -462,6 +516,7 @@ module elpa_api ...@@ -462,6 +516,7 @@ module elpa_api
end subroutine end subroutine
end interface end interface
!> \brief abstract definition of interface to solve double complex eigenvalue problem !> \brief abstract definition of interface to solve double complex eigenvalue problem
!> !>
!> The dimensions of the matrix a (locally ditributed and global), the block-cyclic distribution !> The dimensions of the matrix a (locally ditributed and global), the block-cyclic distribution
...@@ -496,6 +551,7 @@ module elpa_api ...@@ -496,6 +551,7 @@ module elpa_api
end subroutine end subroutine
end interface end interface
!> \brief abstract definition of interface to solve single complex eigenvalue problem !> \brief abstract definition of interface to solve single complex eigenvalue problem
!> !>
!> The dimensions of the matrix a (locally ditributed and global), the block-cyclic distribution !> The dimensions of the matrix a (locally ditributed and global), the block-cyclic distribution
...@@ -530,8 +586,6 @@ module elpa_api ...@@ -530,8 +586,6 @@ module elpa_api
end interface end interface
!> \brief abstract definition of interface to solve double real eigenvalue problem !> \brief abstract definition of interface to solve double real eigenvalue problem
!> !>
!> The dimensions of the matrix a (locally ditributed and global), the block-cyclic distribution !> The dimensions of the matrix a (locally ditributed and global), the block-cyclic distribution
...@@ -564,6 +618,7 @@ module elpa_api ...@@ -564,6 +618,7 @@ module elpa_api
end subroutine end subroutine
end interface end interface
!> \brief abstract definition of interface to solve single real eigenvalue problem !> \brief abstract definition of interface to solve single real eigenvalue problem
!> !>
!> The dimensions of the matrix a (locally ditributed and global), the block-cyclic distribution !> The dimensions of the matrix a (locally ditributed and global), the block-cyclic distribution
...@@ -596,6 +651,7 @@ module elpa_api ...@@ -596,6 +651,7 @@ module elpa_api
end subroutine end subroutine
end interface end interface
!> \brief abstract definition of interface to solve double complex eigenvalue problem !> \brief abstract definition of interface to solve double complex eigenvalue problem
!> !>
!> The dimensions of the matrix a (locally ditributed and global), the block-cyclic distribution