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

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
libelpa@SUFFIX@_private_la_FCFLAGS = $(AM_FCFLAGS) $(FC_MODOUT)private_modules $(FC_MODINC)private_modules
libelpa@SUFFIX@_private_la_SOURCES = \
src/elpa_impl.F90 \
src/elpa_autotune_impl.F90 \
src/elpa_abstract_impl.F90 \
src/helpers/mod_precision.F90 \
src/helpers/mod_mpi.F90 \
......
......@@ -98,3 +98,21 @@ enum ELPA_CONSTANTS {
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(
matrix_flag[m]] + extra_flags))
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
deallocate(obj)
end subroutine
subroutine elpa_autotune_deallocate(obj)
class(elpa_autotune_t), pointer :: obj
call obj%destroy()
deallocate(obj)
end subroutine
end module
......@@ -72,6 +72,7 @@ module elpa_abstract_impl
type, abstract, extends(elpa_t) :: elpa_abstract_impl_t
#ifdef HAVE_DETAILED_TIMINGS
type(timer_t) :: timer
type(timer_t) :: autotune_timer
#else
type(timer_dummy_t) :: timer
#endif
......@@ -79,21 +80,6 @@ module elpa_abstract_impl
logical :: eigenvalues_only
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_double
......@@ -110,7 +96,7 @@ module elpa_abstract_impl
!> \param name string, the key
!> \param value integer, the value to be set
!> \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
class(elpa_abstract_impl_t) :: self
character(*), intent(in) :: name
......@@ -118,7 +104,7 @@ module elpa_abstract_impl
integer, optional :: 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
error = actual_error
......@@ -135,7 +121,7 @@ module elpa_abstract_impl
!> \param name string, the key
!> \param value integer, the value of the key/vaue pair
!> \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
class(elpa_abstract_impl_t) :: self
character(*), intent(in) :: name
......@@ -158,7 +144,7 @@ module elpa_abstract_impl
!> \param name string, the key
!> \param value double, the value to be set
!> \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
class(elpa_abstract_impl_t) :: self
character(*), intent(in) :: name
......@@ -166,7 +152,7 @@ module elpa_abstract_impl
integer, optional :: 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
error = actual_error
......@@ -182,7 +168,7 @@ module elpa_abstract_impl
!> \param name string, the key
!> \param value double, the value of the key/vaue pair
!> \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
class(elpa_abstract_impl_t) :: self
character(*), intent(in) :: name
......@@ -199,128 +185,4 @@ module elpa_abstract_impl
end if
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
......@@ -136,6 +136,10 @@ module elpa_api
elpa_solve_tridiagonal_d, & !< matrix
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
procedure(elpa_set_integer_i), deferred, public :: elpa_set_integer
......@@ -173,6 +177,13 @@ module elpa_api
procedure(elpa_solve_tridiagonal_f_i), deferred, public :: elpa_solve_tridiagonal_f
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
!> Parameters
......@@ -188,6 +199,7 @@ module elpa_api
end function
end interface
!> \brief abstract definition of setup method
!> Parameters
!> \details
......@@ -202,6 +214,39 @@ module elpa_api
end function
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
!> Parameters
!> \details
......@@ -221,6 +266,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of get method for integer values
!> Parameters
!> \details
......@@ -240,6 +286,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of is_set method for integer values
!> Parameters
!> \details
......@@ -257,6 +304,7 @@ module elpa_api
end function
end interface
!> \brief abstract definition of can_set method for integer values
!> Parameters
!> \details
......@@ -276,6 +324,7 @@ module elpa_api
end function
end interface
!> \brief abstract definition of set method for double values
!> Parameters
!> \details
......@@ -295,6 +344,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of get method for double values
!> Parameters
!> \details
......@@ -314,6 +364,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of associate method for integer pointers
!> Parameters
!> \details
......@@ -351,6 +402,7 @@ module elpa_api
end function
end interface
!> \brief abstract definition of print method for timer
!> Parameters
!> \details
......@@ -364,6 +416,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of the start method for timer
!> Parameters
!> \details
......@@ -378,12 +431,12 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of the stop method for timer
!> Parameters
!> \details
!> \param self class(elpa_t): the ELPA object
!> \param name character(len=*) the name of the entry int the timer tree
abstract interface
subroutine elpa_timer_stop_i(self, name)
import elpa_t
......@@ -429,6 +482,7 @@ module elpa_api
end subroutine
end interface
!> \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
......@@ -462,6 +516,7 @@ module elpa_api
end subroutine
end interface
!> \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
......@@ -496,6 +551,7 @@ module elpa_api
end subroutine
end interface
!> \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
......@@ -530,8 +586,6 @@ module elpa_api
end interface
!> \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
......@@ -564,6 +618,7 @@ module elpa_api
end subroutine
end interface
!> \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
......@@ -596,6 +651,7 @@ module elpa_api
end subroutine
end interface
!> \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
......@@ -629,6 +685,7 @@ module elpa_api
end subroutine
end interface
!> \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
......@@ -661,6 +718,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to compute C : = A**T * B for double real matrices
!> where A is a square matrix (self%a,self%na) which is optionally upper or lower triangular
!> B is a (self%na,ncb) matrix
......@@ -714,6 +772,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to compute C : = A**T * B
!> where A is a square matrix (self%na,self%na) which is optionally upper or lower triangular
!> B is a (self%na,ncb) matrix
......@@ -767,6 +826,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to compute C : = A**H * B
!> where A is a square matrix (self%na,self%a) which is optionally upper or lower triangular
!> B is a (self%na,ncb) matrix
......@@ -820,6 +880,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to compute C : = A**H * B
!> where A is a square matrix (self%na,self%na) which is optionally upper or lower triangular
!> B is a (self%na,ncb) matrix
......@@ -873,6 +934,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to do a cholesky decomposition of a double real matrix
!>
!> The dimensions of the matrix a (locally ditributed and global), the block-cylic-distribution
......@@ -898,6 +960,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to do a cholesky decomposition of a single real matrix
!>
!> The dimensions of the matrix a (locally ditributed and global), the block-cylic-distribution
......@@ -923,6 +986,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to do a cholesky decomposition of a double complex matrix
!>
!> The dimensions of the matrix a (locally ditributed and global), the block-cylic-distribution
......@@ -948,6 +1012,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to do a cholesky decomposition of a single complex matrix
!>
!> The dimensions of the matrix a (locally ditributed and global), the block-cylic-distribution
......@@ -973,6 +1038,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to invert a triangular double real matrix
!>
!> The dimensions of the matrix a (locally ditributed and global), the block-cylic-distribution
......@@ -998,6 +1064,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to invert a triangular single real matrix
!> Parameters
!>
......@@ -1023,6 +1090,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to invert a triangular double complex matrix
!>
!> The dimensions of the matrix a (locally ditributed and global), the block-cylic-distribution
......@@ -1048,6 +1116,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to invert a triangular single complex matrix
!>
!> The dimensions of the matrix a (locally ditributed and global), the block-cylic-distribution
......@@ -1073,6 +1142,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to solve the eigenvalue problem for a double-precision real valued tridiangular matrix
!>
!> The dimensions of the matrix a (locally ditributed and global), the block-cylic-distribution
......@@ -1102,6 +1172,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to solve the eigenvalue problem for a single-precision real valued tridiangular matrix
!>
!> The dimensions of the matrix a (locally ditributed and global), the block-cylic-distribution
......@@ -1131,6 +1202,7 @@ module elpa_api
end subroutine
end interface
!> \brief abstract definition of interface to destroy an ELPA object
!> Parameters
!> \param self class(elpa_t), the ELPA object
......@@ -1142,8 +1214,28 @@ module elpa_api
end subroutine
end interface
abstract interface
subroutine elpa_autotune_print_i(self)
import elpa_autotune_t
implicit none
class(elpa_autotune_t), intent(in) :: self
end subroutine
end interface
abstract interface
subroutine elpa_autotune_destroy_i(self)
import elpa_autotune_t
implicit none
class(elpa_autotune_t), intent(inout) :: self
end subroutine
end interface
contains
!> \brief function to intialize the ELPA library
!> Parameters
!> \param api_version integer: api_version that ELPA should use
......@@ -1165,6 +1257,7 @@ module elpa_api
endif
end function
!> \brief function to check whether the ELPA library has been correctly initialised
!> Parameters
!> \result state integer: state is either ELPA_OK or ELPA_ERROR, which can be queried with elpa_strerr
......@@ -1177,12 +1270,14 @@ module elpa_api
endif
end function
!> \brief subroutine to uninit the ELPA library. Does nothing at the moment. Might do sth. later