From ab189a2d2acc0029339667fb67bd293169d42c51 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Tue, 16 Apr 2019 15:56:31 +0200 Subject: [PATCH 01/88] libcusmm: remove indirect dependencies from requirements Fixes #167 --- src/acc/libsmm_acc/libcusmm/requirements.txt | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/acc/libsmm_acc/libcusmm/requirements.txt b/src/acc/libsmm_acc/libcusmm/requirements.txt index 778108ad0b1..86beed9e829 100644 --- a/src/acc/libsmm_acc/libcusmm/requirements.txt +++ b/src/acc/libsmm_acc/libcusmm/requirements.txt @@ -1,19 +1,6 @@ -attrs==18.2.0 -cycler==0.10.0 eli5==0.8.1 -graphviz==0.10.1 -Jinja2==2.10 joblib==0.13.1 -kiwisolver==1.0.1 -MarkupSafe==1.1.0 matplotlib==3.0.2 numpy==1.16.0 pandas==0.23.4 -pyparsing==2.3.1 -python-dateutil==2.7.5 -pytz==2018.9 scikit-learn==0.20.2 -scipy==1.2.0 -six==1.12.0 -tabulate==0.8.2 -typing==3.6.6 From 1b4cb7b623f12852cbbad1bd09ecb439c15362d6 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Tue, 16 Apr 2019 17:49:28 +0200 Subject: [PATCH 02/88] libcusmm: fix typos in autotuning README --- src/acc/libsmm_acc/libcusmm/tune.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/acc/libsmm_acc/libcusmm/tune.md b/src/acc/libsmm_acc/libcusmm/tune.md index 242943a6b00..38a569fe736 100644 --- a/src/acc/libsmm_acc/libcusmm/tune.md +++ b/src/acc/libsmm_acc/libcusmm/tune.md @@ -12,7 +12,7 @@ If you are about to autotune parameters for a new GPU (i.e. a GPU for which ther --- -### Predictive modeling procedure +### Autotuning procedure #### 1. Go to the libcusmm directory @@ -192,4 +192,4 @@ Submit a pull request updating the appropriate `parameters_GPU.json` file to the **Contribute autotuning data** -See [instructions](https://github.com/cp2k/dbcsr-data#contributing)in DBCSR's [data repository](https://github.com/cp2k/dbcsr-data). +See [instructions](https://github.com/cp2k/dbcsr-data#contributing) in DBCSR's [data repository](https://github.com/cp2k/dbcsr-data). From 6654e308c21674581c6a932bde0b72097cdd1ce0 Mon Sep 17 00:00:00 2001 From: alazzaro Date: Wed, 17 Apr 2019 06:36:55 -0500 Subject: [PATCH 03/88] Use MPI_COMM_WORLD everywhere --- examples/dbcsr_example_2.F | 2 +- examples/dbcsr_example_3.F | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/dbcsr_example_2.F b/examples/dbcsr_example_2.F index 652f538e25f..f7213a0684b 100644 --- a/examples/dbcsr_example_2.F +++ b/examples/dbcsr_example_2.F @@ -167,7 +167,7 @@ PROGRAM dbcsr_example_2 !*************************************************************************************** ! ! finalize libdbcsr - CALL dbcsr_finalize_lib(mpi_comm_world) + CALL dbcsr_finalize_lib(MPI_COMM_WORLD) ! free comm CALL mpi_comm_free(group, ierr) diff --git a/examples/dbcsr_example_3.F b/examples/dbcsr_example_3.F index 4e76464437c..08433934ab8 100644 --- a/examples/dbcsr_example_3.F +++ b/examples/dbcsr_example_3.F @@ -194,7 +194,7 @@ PROGRAM dbcsr_example_3 DEALLOCATE (row_blk_sizes, col_blk_sizes) ! finalize libdbcsr - CALL dbcsr_finalize_lib(mpi_comm_world) + CALL dbcsr_finalize_lib(MPI_COMM_WORLD) ! free comm CALL mpi_comm_free(group, ierr) From 22e274011d9b31bbfd851d6eee71a9d102ce0f98 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Wed, 17 Apr 2019 14:20:42 +0200 Subject: [PATCH 04/88] libcusmm/notebooks: remove indirect dependencies from requirements Fixes #167 --- .../libsmm_acc/libcusmm/notebooks/requirements.txt | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/acc/libsmm_acc/libcusmm/notebooks/requirements.txt b/src/acc/libsmm_acc/libcusmm/notebooks/requirements.txt index b4d8d789134..ea06f358b26 100644 --- a/src/acc/libsmm_acc/libcusmm/notebooks/requirements.txt +++ b/src/acc/libsmm_acc/libcusmm/notebooks/requirements.txt @@ -1,19 +1,6 @@ bokeh==1.0.4 -cycler==0.10.0 -Jinja2==2.10 -kiwisolver==1.0.1 -MarkupSafe==1.1.0 matplotlib==3.0.2 numpy==1.16.0 -packaging==18.0 pandas==0.23.4 pandas-profiling==1.4.1 -Pillow==5.4.1 -pyparsing==2.3.1 -python-dateutil==2.7.5 -pytz==2018.9 -PyYAML==4.2b1 -scipy==1.2.0 seaborn==0.9.0 -six==1.12.0 -tornado==5.1.1 From 6909ccd00964fa74600a128014e97180441a604f Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Mon, 15 Apr 2019 12:04:05 +0200 Subject: [PATCH 05/88] remove timers to core/ --- src/PACKAGE | 2 +- src/{extras => core}/dbcsr_cuda_nvtx_cu.cu | 0 src/{extras => core}/dbcsr_cuda_profiling.F | 0 src/{extras => core}/dbcsr_dict.F | 0 src/{extras => core}/dbcsr_dict.fypp | 0 src/{extras => core}/dbcsr_error_handling.F | 0 src/{extras => core}/dbcsr_iter_types.F | 0 src/{extras => core}/dbcsr_list.F | 0 src/{extras => core}/dbcsr_list.fypp | 0 src/{extras => core}/dbcsr_list_callstackentry.F | 0 src/{extras => core}/dbcsr_list_routinereport.F | 0 src/{extras => core}/dbcsr_list_routinestat.F | 0 src/{extras => core}/dbcsr_list_timerenv.F | 0 src/{extras => core}/dbcsr_log_handling.F | 0 src/{extras => core}/dbcsr_memory_utilities.F | 0 src/{extras => core}/dbcsr_print_messages.F | 0 src/{extras => core}/dbcsr_timings.F | 0 src/{extras => core}/dbcsr_timings_base_type.F | 0 src/{extras => core}/dbcsr_timings_report.F | 0 src/{extras => core}/dbcsr_timings_types.F | 0 src/extras/PACKAGE | 5 ----- 21 files changed, 1 insertion(+), 6 deletions(-) rename src/{extras => core}/dbcsr_cuda_nvtx_cu.cu (100%) rename src/{extras => core}/dbcsr_cuda_profiling.F (100%) rename src/{extras => core}/dbcsr_dict.F (100%) rename src/{extras => core}/dbcsr_dict.fypp (100%) rename src/{extras => core}/dbcsr_error_handling.F (100%) rename src/{extras => core}/dbcsr_iter_types.F (100%) rename src/{extras => core}/dbcsr_list.F (100%) rename src/{extras => core}/dbcsr_list.fypp (100%) rename src/{extras => core}/dbcsr_list_callstackentry.F (100%) rename src/{extras => core}/dbcsr_list_routinereport.F (100%) rename src/{extras => core}/dbcsr_list_routinestat.F (100%) rename src/{extras => core}/dbcsr_list_timerenv.F (100%) rename src/{extras => core}/dbcsr_log_handling.F (100%) rename src/{extras => core}/dbcsr_memory_utilities.F (100%) rename src/{extras => core}/dbcsr_print_messages.F (100%) rename src/{extras => core}/dbcsr_timings.F (100%) rename src/{extras => core}/dbcsr_timings_base_type.F (100%) rename src/{extras => core}/dbcsr_timings_report.F (100%) rename src/{extras => core}/dbcsr_timings_types.F (100%) delete mode 100644 src/extras/PACKAGE diff --git a/src/PACKAGE b/src/PACKAGE index f5abf398b6b..1221238b45c 100644 --- a/src/PACKAGE +++ b/src/PACKAGE @@ -1,7 +1,7 @@ { "description": "Distributed Block Compressed Sparse Row, A sparse matrix library", "archive": "libdbcsr", -"requires": ["acc/cublaswrap", "extras", "base", "mpi", "data", "dist", +"requires": ["acc/cublaswrap", "base", "mpi", "data", "dist", "block", "ops", "mm", "core", "utils", "work"], "public": ["*.F"] } diff --git a/src/extras/dbcsr_cuda_nvtx_cu.cu b/src/core/dbcsr_cuda_nvtx_cu.cu similarity index 100% rename from src/extras/dbcsr_cuda_nvtx_cu.cu rename to src/core/dbcsr_cuda_nvtx_cu.cu diff --git a/src/extras/dbcsr_cuda_profiling.F b/src/core/dbcsr_cuda_profiling.F similarity index 100% rename from src/extras/dbcsr_cuda_profiling.F rename to src/core/dbcsr_cuda_profiling.F diff --git a/src/extras/dbcsr_dict.F b/src/core/dbcsr_dict.F similarity index 100% rename from src/extras/dbcsr_dict.F rename to src/core/dbcsr_dict.F diff --git a/src/extras/dbcsr_dict.fypp b/src/core/dbcsr_dict.fypp similarity index 100% rename from src/extras/dbcsr_dict.fypp rename to src/core/dbcsr_dict.fypp diff --git a/src/extras/dbcsr_error_handling.F b/src/core/dbcsr_error_handling.F similarity index 100% rename from src/extras/dbcsr_error_handling.F rename to src/core/dbcsr_error_handling.F diff --git a/src/extras/dbcsr_iter_types.F b/src/core/dbcsr_iter_types.F similarity index 100% rename from src/extras/dbcsr_iter_types.F rename to src/core/dbcsr_iter_types.F diff --git a/src/extras/dbcsr_list.F b/src/core/dbcsr_list.F similarity index 100% rename from src/extras/dbcsr_list.F rename to src/core/dbcsr_list.F diff --git a/src/extras/dbcsr_list.fypp b/src/core/dbcsr_list.fypp similarity index 100% rename from src/extras/dbcsr_list.fypp rename to src/core/dbcsr_list.fypp diff --git a/src/extras/dbcsr_list_callstackentry.F b/src/core/dbcsr_list_callstackentry.F similarity index 100% rename from src/extras/dbcsr_list_callstackentry.F rename to src/core/dbcsr_list_callstackentry.F diff --git a/src/extras/dbcsr_list_routinereport.F b/src/core/dbcsr_list_routinereport.F similarity index 100% rename from src/extras/dbcsr_list_routinereport.F rename to src/core/dbcsr_list_routinereport.F diff --git a/src/extras/dbcsr_list_routinestat.F b/src/core/dbcsr_list_routinestat.F similarity index 100% rename from src/extras/dbcsr_list_routinestat.F rename to src/core/dbcsr_list_routinestat.F diff --git a/src/extras/dbcsr_list_timerenv.F b/src/core/dbcsr_list_timerenv.F similarity index 100% rename from src/extras/dbcsr_list_timerenv.F rename to src/core/dbcsr_list_timerenv.F diff --git a/src/extras/dbcsr_log_handling.F b/src/core/dbcsr_log_handling.F similarity index 100% rename from src/extras/dbcsr_log_handling.F rename to src/core/dbcsr_log_handling.F diff --git a/src/extras/dbcsr_memory_utilities.F b/src/core/dbcsr_memory_utilities.F similarity index 100% rename from src/extras/dbcsr_memory_utilities.F rename to src/core/dbcsr_memory_utilities.F diff --git a/src/extras/dbcsr_print_messages.F b/src/core/dbcsr_print_messages.F similarity index 100% rename from src/extras/dbcsr_print_messages.F rename to src/core/dbcsr_print_messages.F diff --git a/src/extras/dbcsr_timings.F b/src/core/dbcsr_timings.F similarity index 100% rename from src/extras/dbcsr_timings.F rename to src/core/dbcsr_timings.F diff --git a/src/extras/dbcsr_timings_base_type.F b/src/core/dbcsr_timings_base_type.F similarity index 100% rename from src/extras/dbcsr_timings_base_type.F rename to src/core/dbcsr_timings_base_type.F diff --git a/src/extras/dbcsr_timings_report.F b/src/core/dbcsr_timings_report.F similarity index 100% rename from src/extras/dbcsr_timings_report.F rename to src/core/dbcsr_timings_report.F diff --git a/src/extras/dbcsr_timings_types.F b/src/core/dbcsr_timings_types.F similarity index 100% rename from src/extras/dbcsr_timings_types.F rename to src/core/dbcsr_timings_types.F diff --git a/src/extras/PACKAGE b/src/extras/PACKAGE deleted file mode 100644 index 971c0cc54d0..00000000000 --- a/src/extras/PACKAGE +++ /dev/null @@ -1,5 +0,0 @@ -{ -"description": "Extras routines, DBCSR can conceptually be compiled without them", -"archive": "libdbcsr", -"requires": ["../base", "../utils", "../core", "../mpi" ] -} From 41e9a95d77dbf5289a20de454dd50ef8a0910bf6 Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Tue, 16 Apr 2019 15:03:18 +0200 Subject: [PATCH 06/88] refactor timers and grid --- src/core/dbcsr_lib.F | 107 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 1c02bcf8101..dd00ba8a337 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -29,6 +29,21 @@ MODULE dbcsr_lib USE dbcsr_multiply_api, ONLY: dbcsr_multiply_clear_mempools, & dbcsr_multiply_lib_finalize, & dbcsr_multiply_lib_init + + + USE dbcsr_timings, ONLY: add_timer_env, & + rm_timer_env, & + timings_register_hooks + USE dbcsr_timings_report, ONLY: cost_type_time, & + timings_report_callgraph, & + timings_report_print + + USE dbcsr_log_handling, ONLY: dbcsr_add_default_logger, & + dbcsr_logger_create, & + dbcsr_logger_release, & + dbcsr_logger_type, & + dbcsr_rm_default_logger + #include "base/dbcsr_base_uses.f90" !$ USE OMP_LIB, ONLY: omp_get_thread_num, omp_get_num_threads @@ -39,11 +54,103 @@ MODULE dbcsr_lib CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'dbcsr_lib' PUBLIC :: dbcsr_init_lib, dbcsr_finalize_lib, dbcsr_clear_mempools + PUBLIC :: dbcsr_init_timer,dbcsr_kill_timer, dbcsr_print_timer + LOGICAL, PRIVATE, SAVE :: is_initialized = .FALSE. + + TYPE(dbcsr_logger_type), POINTER :: logger + TYPE(dbcsr_mp_obj) , save :: mp_env + CONTAINS +! ************************************************************************************************** +!> \brief Initialize timers +!> +!> Prepares the DBCSR library for use. +! ************************************************************************************************** + subroutine dbcsr_init_timer(mp_comm, npdims, group) + integer , intent(in) :: mp_comm + integer, dimension(2), intent(in) :: npdims + INTEGER, intent(in) :: group + integer, dimension(2) :: myploc + INTEGER, DIMENSION(:, :), POINTER :: pgrid + integer :: io_unit, mynode, numnodes, pcol, prow + + CALL mp_environ(numnodes, mynode, mp_comm) +! CALL mp_cart_create(mp_comm, 2, npdims, myploc, group) + + ALLOCATE (pgrid(0:npdims(1) - 1, 0:npdims(2) - 1)) + DO prow = 0, npdims(1) - 1 + DO pcol = 0, npdims(2) - 1 + CALL mp_cart_rank(group, (/prow, pcol/), pgrid(prow, pcol)) + ENDDO + ENDDO + + CALL dbcsr_mp_new_prv(mp_env, pgrid, group, mynode, numnodes, & + myprow=myploc(1), mypcol=myploc(2)) + DEALLOCATE (pgrid) + + + io_unit = 0 + IF (mynode .EQ. mp_env%mp%source) io_unit = default_output_unit + + ! + ! Timers + NULLIFY (logger) + CALL dbcsr_logger_create(logger, mp_env=mp_env, & + default_global_unit_nr=io_unit, & + close_global_unit_on_dealloc=.FALSE.) + CALL dbcsr_add_default_logger(logger) + CALL dbcsr_logger_release(logger) + CALL dbcsr_error_handling_setup() + CALL timings_register_hooks() + CALL add_mp_perf_env() + CALL add_timer_env() + + end subroutine + + +! ************************************************************************************************** +!> \brief Kill timers +!> +!> Prepares the DBCSR library for use. +! ************************************************************************************************** + subroutine dbcsr_kill_timer() + ! clean mp environment + CALL dbcsr_mp_release(mp_env) + + ! + ! Remove logger + CALL dbcsr_rm_default_logger() + + ! + ! + ! Remove timers + CALL rm_mp_perf_env() + CALL rm_timer_env() + end subroutine + + +! ************************************************************************************************** +!> \brief Print timers +!> +!> Prepares the DBCSR library for use. +! ************************************************************************************************** + subroutine dbcsr_print_timer(mp_comm) + integer , intent(in) :: mp_comm + integer :: io_unit, mynode, numnodes + + CALL mp_environ(numnodes, mynode, mp_comm) + + io_unit = 0 + IF (mynode .EQ. mp_env%mp%source) io_unit = default_output_unit + + CALL timings_report_print(io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) + end subroutine + + ! ************************************************************************************************** !> \brief Initialize the DBCSR library !> From 51f2b64c1f4f66cdcb981dcf47348951cca5399d Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Tue, 16 Apr 2019 16:39:58 +0200 Subject: [PATCH 07/88] refactor grid init --- src/core/dbcsr_lib.F | 10 +++++++--- src/mpi/dbcsr_mp_methods.F | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index dd00ba8a337..319203a6b0d 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -20,7 +20,8 @@ MODULE dbcsr_lib USE dbcsr_kinds, ONLY: int_1_size, & int_2_size, & int_4_size, & - int_8_size + int_8_size, dp + USE dbcsr_machine, ONLY: default_output_unit USE dbcsr_mpiwrap, ONLY: add_mp_perf_env, & describe_mp_perf_env, & @@ -43,6 +44,9 @@ MODULE dbcsr_lib dbcsr_logger_release, & dbcsr_logger_type, & dbcsr_rm_default_logger + + use dbcsr_types, only: dbcsr_mp_obj + #include "base/dbcsr_base_uses.f90" @@ -88,8 +92,8 @@ subroutine dbcsr_init_timer(mp_comm, npdims, group) ENDDO ENDDO - CALL dbcsr_mp_new_prv(mp_env, pgrid, group, mynode, numnodes, & - myprow=myploc(1), mypcol=myploc(2)) + CALL dbcsr_mp_new(mp_env, pgrid, group, mynode, numnodes, & + myploc(1), myploc(2)) DEALLOCATE (pgrid) diff --git a/src/mpi/dbcsr_mp_methods.F b/src/mpi/dbcsr_mp_methods.F index 1a3a7ce307b..a702488cbc3 100644 --- a/src/mpi/dbcsr_mp_methods.F +++ b/src/mpi/dbcsr_mp_methods.F @@ -118,6 +118,7 @@ SUBROUTINE dbcsr_mp_new(mp_env, pgrid, mp_group, mynode, numnodes, myprow, & ENDDO column_loop ENDIF mp_env%mp%subgroups_defined = .FALSE. + call dbcsr_mp_grid_setup(mp_env) END SUBROUTINE dbcsr_mp_new ! ************************************************************************************************** From d902e56934c739dbab57224c1af7c63bea411f7e Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Wed, 17 Apr 2019 14:17:44 +0200 Subject: [PATCH 08/88] test case --- src/core/dbcsr_lib.F | 83 +++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 319203a6b0d..a188917e53a 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -26,7 +26,8 @@ MODULE dbcsr_lib USE dbcsr_mpiwrap, ONLY: add_mp_perf_env, & describe_mp_perf_env, & has_mp_perf_env, & - mp_environ + mp_environ, mp_cart_rank,& + rm_mp_perf_env USE dbcsr_multiply_api, ONLY: dbcsr_multiply_clear_mempools, & dbcsr_multiply_lib_finalize, & dbcsr_multiply_lib_init @@ -47,6 +48,9 @@ MODULE dbcsr_lib use dbcsr_types, only: dbcsr_mp_obj + use dbcsr_mp_methods, only: dbcsr_mp_new,dbcsr_mp_release + + use dbcsr_error_handling, only: dbcsr_error_handling_setup #include "base/dbcsr_base_uses.f90" @@ -74,32 +78,12 @@ MODULE dbcsr_lib !> !> Prepares the DBCSR library for use. ! ************************************************************************************************** - subroutine dbcsr_init_timer(mp_comm, npdims, group) - integer , intent(in) :: mp_comm - integer, dimension(2), intent(in) :: npdims - INTEGER, intent(in) :: group - integer, dimension(2) :: myploc - INTEGER, DIMENSION(:, :), POINTER :: pgrid - integer :: io_unit, mynode, numnodes, pcol, prow - - CALL mp_environ(numnodes, mynode, mp_comm) -! CALL mp_cart_create(mp_comm, 2, npdims, myploc, group) - - ALLOCATE (pgrid(0:npdims(1) - 1, 0:npdims(2) - 1)) - DO prow = 0, npdims(1) - 1 - DO pcol = 0, npdims(2) - 1 - CALL mp_cart_rank(group, (/prow, pcol/), pgrid(prow, pcol)) - ENDDO - ENDDO - - CALL dbcsr_mp_new(mp_env, pgrid, group, mynode, numnodes, & - myploc(1), myploc(2)) - DEALLOCATE (pgrid) - + subroutine dbcsr_init_timer(mp_env) + TYPE(dbcsr_mp_obj) , intent(in) :: mp_env + integer :: io_unit io_unit = 0 - IF (mynode .EQ. mp_env%mp%source) io_unit = default_output_unit - + IF (mp_env % mp % mynode .EQ. mp_env % mp % source) io_unit = default_output_unit ! ! Timers NULLIFY (logger) @@ -112,7 +96,6 @@ subroutine dbcsr_init_timer(mp_comm, npdims, group) CALL timings_register_hooks() CALL add_mp_perf_env() CALL add_timer_env() - end subroutine @@ -142,14 +125,11 @@ subroutine dbcsr_kill_timer() !> !> Prepares the DBCSR library for use. ! ************************************************************************************************** - subroutine dbcsr_print_timer(mp_comm) - integer , intent(in) :: mp_comm - integer :: io_unit, mynode, numnodes - - CALL mp_environ(numnodes, mynode, mp_comm) + subroutine dbcsr_print_timer() + integer :: io_unit io_unit = 0 - IF (mynode .EQ. mp_env%mp%source) io_unit = default_output_unit + IF (mp_env % mp % mynode .EQ. mp_env % mp % source) io_unit = default_output_unit CALL timings_report_print(io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) end subroutine @@ -160,15 +140,44 @@ subroutine dbcsr_print_timer(mp_comm) !> !> Prepares the DBCSR library for use. ! ************************************************************************************************** - SUBROUTINE dbcsr_init_lib() + SUBROUTINE dbcsr_init_lib(mp_comm) + INTEGER, INTENT(IN) :: mp_comm CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_init_lib', routineP = moduleN//':'//routineN INTEGER :: error_handle, ithread, nthreads + integer :: comm + + integer, dimension(2), :: npdims + integer, dimension(2) :: myploc + INTEGER, DIMENSION(:, :), POINTER :: pgrid + integer :: io_unit, group, numnodes, pcol, prow, mynode + + if (present(mp_comm)) then + comm = mp_comm + else + comm = MPI_COMM_WORLD + endif + + npdims(1) = numnodes + npdims(2) = 1 + + CALL mp_environ(numnodes, mynode, mp_comm) + CALL mp_cart_create(mp_comm, 2, npdims, myploc, group) + + ALLOCATE (pgrid(0:npdims(1) - 1, 0:npdims(2) - 1)) + DO prow = 0, npdims(1) - 1 + DO pcol = 0, npdims(2) - 1 + CALL mp_cart_rank(group, (/prow, pcol/), pgrid(prow, pcol)) + ENDDO + ENDDO + + CALL dbcsr_mp_new(mp_env, pgrid, group, mynode, numnodes, & + myploc(1), myploc(2)) + + DEALLOCATE (pgrid) -!n_stack_buffers,mem_type, n_stack_mem_regions, stack_size, nstacks,& -!INTEGER, DIMENSION(3) :: nxstacks -! --------------------------------------------------------------------------- + call dbcsr_init_timer(mp_env) IF (is_initialized) RETURN CALL timeset(routineN, error_handle) @@ -257,6 +266,8 @@ SUBROUTINE dbcsr_finalize_lib(group, output_unit) is_initialized = .FALSE. CALL timestop(error_handle) + + call dbcsr_kill_timer() END SUBROUTINE dbcsr_finalize_lib ! ************************************************************************************************** From 32523dc2155c9221eb287809801ac2dac5590cb9 Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Wed, 17 Apr 2019 15:56:10 +0200 Subject: [PATCH 09/88] continue refactor --- src/core/dbcsr_lib.F | 31 +++---------------- src/mpi/dbcsr_mp_methods.F | 57 +++++++++++++++++++++++++++++++++++ src/ops/dbcsr_tests.F | 62 ++------------------------------------ 3 files changed, 64 insertions(+), 86 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index a188917e53a..8b5ff59d078 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -48,7 +48,9 @@ MODULE dbcsr_lib use dbcsr_types, only: dbcsr_mp_obj - use dbcsr_mp_methods, only: dbcsr_mp_new,dbcsr_mp_release + use dbcsr_mp_methods, only: dbcsr_mp_new, dbcsr_mp_release, & + dbcsr_mp_make_env + use dbcsr_error_handling, only: dbcsr_error_handling_setup @@ -69,7 +71,7 @@ MODULE dbcsr_lib TYPE(dbcsr_logger_type), POINTER :: logger TYPE(dbcsr_mp_obj) , save :: mp_env - + integer, save :: group CONTAINS @@ -146,36 +148,13 @@ SUBROUTINE dbcsr_init_lib(mp_comm) CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_init_lib', routineP = moduleN//':'//routineN INTEGER :: error_handle, ithread, nthreads - integer :: comm integer, dimension(2), :: npdims integer, dimension(2) :: myploc INTEGER, DIMENSION(:, :), POINTER :: pgrid integer :: io_unit, group, numnodes, pcol, prow, mynode - if (present(mp_comm)) then - comm = mp_comm - else - comm = MPI_COMM_WORLD - endif - - npdims(1) = numnodes - npdims(2) = 1 - - CALL mp_environ(numnodes, mynode, mp_comm) - CALL mp_cart_create(mp_comm, 2, npdims, myploc, group) - - ALLOCATE (pgrid(0:npdims(1) - 1, 0:npdims(2) - 1)) - DO prow = 0, npdims(1) - 1 - DO pcol = 0, npdims(2) - 1 - CALL mp_cart_rank(group, (/prow, pcol/), pgrid(prow, pcol)) - ENDDO - ENDDO - - CALL dbcsr_mp_new(mp_env, pgrid, group, mynode, numnodes, & - myploc(1), myploc(2)) - - DEALLOCATE (pgrid) + call dbcsr_mp_make_env(mp_env, group, mp_comm) call dbcsr_init_timer(mp_env) diff --git a/src/mpi/dbcsr_mp_methods.F b/src/mpi/dbcsr_mp_methods.F index a702488cbc3..37aad1bc833 100644 --- a/src/mpi/dbcsr_mp_methods.F +++ b/src/mpi/dbcsr_mp_methods.F @@ -156,6 +156,63 @@ SUBROUTINE dbcsr_mp_grid_setup(mp_env) ENDIF END SUBROUTINE dbcsr_mp_grid_setup +! ************************************************************************************************** +!> \brief Creates a sane mp_obj from the given MPI comm that is not a cartesian one (hack) +!> \param[out] mp_env Message-passing environment object to create +!> \param[out] cart_group the created cartesian group (to be freed by the user) +!> \param[in] mp_group MPI group +!> \param[in] nprocs (optional) Number of processes +!> \param[in] pgrid_dims (optional) Dimensions of MPI group +! ************************************************************************************************** + SUBROUTINE dbcsr_mp_make_env(mp_env, cart_group, mp_group, & + nprocs, pgrid_dims) + TYPE(dbcsr_mp_obj), INTENT(OUT) :: mp_env + INTEGER, INTENT(OUT) :: cart_group + INTEGER, INTENT(IN) :: mp_group + INTEGER, INTENT(IN), OPTIONAL :: nprocs + INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL :: pgrid_dims + + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_mp_make_env', & + routineP = moduleN//':'//routineN + + INTEGER :: error_handle, mynode, numnodes, pcol, & + prow + INTEGER, ALLOCATABLE, DIMENSION(:, :) :: pgrid + INTEGER, DIMENSION(2) :: coord, myploc, npdims + LOGICAL :: alive + +! --------------------------------------------------------------------------- + + CALL timeset(routineN, error_handle) + CALL mp_environ(numnodes, mynode, mp_group) + IF (PRESENT(nprocs)) THEN + IF (nprocs > numnodes) & + DBCSR_ABORT("Can not grow processes.") + numnodes = nprocs + ENDIF + ! + IF (PRESENT(pgrid_dims)) THEN + npdims(:) = pgrid_dims + ELSE + npdims(:) = 0 + CALL mp_dims_create(numnodes, npdims) + ENDIF + CALL mp_cart_create(mp_group, 2, npdims, myploc, cart_group) + CALL mp_environ(numnodes, mynode, cart_group) + ALLOCATE (pgrid(0:npdims(1) - 1, 0:npdims(2) - 1)) + DO prow = 0, npdims(1) - 1 + DO pcol = 0, npdims(2) - 1 + coord = (/prow, pcol/) + CALL mp_cart_rank(cart_group, coord, pgrid(prow, pcol)) + ENDDO + ENDDO + CALL dbcsr_mp_new(mp_env, pgrid, cart_group, mynode, numnodes, & + myprow=myploc(1), mypcol=myploc(2)) + CALL timestop(error_handle) + END SUBROUTINE dbcsr_mp_make_env + + + ! ************************************************************************************************** !> \brief Marks another use of the mp_env !> \param[in,out] mp_env multiprocessor environment diff --git a/src/ops/dbcsr_tests.F b/src/ops/dbcsr_tests.F index b64b74d10c0..ada9288066d 100644 --- a/src/ops/dbcsr_tests.F +++ b/src/ops/dbcsr_tests.F @@ -44,7 +44,8 @@ MODULE dbcsr_tests dbcsr_mp_new, & dbcsr_mp_npcols, & dbcsr_mp_nprows, & - dbcsr_mp_release + dbcsr_mp_release,& + dbcsr_mp_make_env USE dbcsr_mpiwrap, ONLY: & mp_cart_create, mp_cart_rank, mp_comm_free, mp_comm_null, mp_dims_create, mp_environ, & mp_max, mp_sum, mp_sync @@ -499,64 +500,5 @@ SUBROUTINE test_multiplies_multiproc(group_sizes, & CALL timestop(error_handle) END SUBROUTINE test_multiplies_multiproc -! ************************************************************************************************** -!> \brief Creates a sane mp_obj from the given MPI comm that is not a cartesian one (hack) -!> \param[out] mp_env Message-passing environment object to create -!> \param[out] cart_group the created cartesian group (to be freed by the user) -!> \param[in] mp_group MPI group -!> \param[in] nprocs (optional) Number of processes -!> \param[in] pgrid_dims (optional) Dimensions of MPI group -! ************************************************************************************************** - SUBROUTINE dbcsr_mp_make_env(mp_env, cart_group, mp_group, & - nprocs, pgrid_dims) - TYPE(dbcsr_mp_obj), INTENT(OUT) :: mp_env - INTEGER, INTENT(OUT) :: cart_group - INTEGER, INTENT(IN) :: mp_group - INTEGER, INTENT(IN), OPTIONAL :: nprocs - INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL :: pgrid_dims - - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_mp_make_env', & - routineP = moduleN//':'//routineN - - INTEGER :: error_handle, mynode, numnodes, pcol, & - prow - INTEGER, ALLOCATABLE, DIMENSION(:, :) :: pgrid - INTEGER, DIMENSION(2) :: coord, myploc, npdims - LOGICAL :: alive - -! --------------------------------------------------------------------------- - - CALL timeset(routineN, error_handle) - CALL mp_environ(numnodes, mynode, mp_group) - IF (PRESENT(nprocs)) THEN - IF (nprocs > numnodes) & - DBCSR_ABORT("Can not grow processes.") - numnodes = nprocs - ENDIF - ! - IF (PRESENT(pgrid_dims)) THEN - npdims(:) = pgrid_dims - ELSE - npdims(:) = 0 - CALL mp_dims_create(numnodes, npdims) - ENDIF - CALL mp_cart_create(mp_group, 2, npdims, myploc, cart_group) - alive = cart_group .NE. mp_comm_null - IF (alive) THEN - CALL mp_environ(numnodes, mynode, cart_group) - ALLOCATE (pgrid(0:npdims(1) - 1, 0:npdims(2) - 1)) - DO prow = 0, npdims(1) - 1 - DO pcol = 0, npdims(2) - 1 - coord = (/prow, pcol/) - CALL mp_cart_rank(cart_group, coord, pgrid(prow, pcol)) - ENDDO - ENDDO - CALL dbcsr_mp_new(mp_env, pgrid, cart_group, mynode, numnodes, & - myprow=myploc(1), mypcol=myploc(2)) - ELSE - CALL dbcsr_mp_init(mp_env) - ENDIF - CALL timestop(error_handle) - END SUBROUTINE dbcsr_mp_make_env END MODULE dbcsr_tests From fd28abd7b7534e7fffe76bd6e096b1905c71591e Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Thu, 18 Apr 2019 18:45:08 +0200 Subject: [PATCH 10/88] abort after finalize_timer: list_timerenv_peek: list is not initialized. --- src/core/dbcsr_lib.F | 31 +++++++++++++-------- src/mpi/dbcsr_mp_methods.F | 8 ++++-- tests/dbcsr_performance_driver.F | 44 ++++++++++++++++-------------- tests/dbcsr_tensor_unittest.F | 2 +- tests/dbcsr_test_csr_conversions.F | 2 +- tests/dbcsr_unittest1.F | 2 +- tests/dbcsr_unittest2.F | 2 +- tests/dbcsr_unittest3.F | 2 +- 8 files changed, 53 insertions(+), 40 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 8b5ff59d078..8b08af280db 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -27,7 +27,8 @@ MODULE dbcsr_lib describe_mp_perf_env, & has_mp_perf_env, & mp_environ, mp_cart_rank,& - rm_mp_perf_env + rm_mp_perf_env,& + mp_comm_free USE dbcsr_multiply_api, ONLY: dbcsr_multiply_clear_mempools, & dbcsr_multiply_lib_finalize, & dbcsr_multiply_lib_init @@ -64,14 +65,13 @@ MODULE dbcsr_lib CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'dbcsr_lib' PUBLIC :: dbcsr_init_lib, dbcsr_finalize_lib, dbcsr_clear_mempools - PUBLIC :: dbcsr_init_timer,dbcsr_kill_timer, dbcsr_print_timer LOGICAL, PRIVATE, SAVE :: is_initialized = .FALSE. TYPE(dbcsr_logger_type), POINTER :: logger TYPE(dbcsr_mp_obj) , save :: mp_env - integer, save :: group + integer, save :: default_group CONTAINS @@ -106,19 +106,22 @@ subroutine dbcsr_init_timer(mp_env) !> !> Prepares the DBCSR library for use. ! ************************************************************************************************** - subroutine dbcsr_kill_timer() + subroutine dbcsr_finalize_timer() ! clean mp environment - CALL dbcsr_mp_release(mp_env) + !CALL dbcsr_mp_release(mp_env) + print *,"1" ! ! Remove logger CALL dbcsr_rm_default_logger() + print *,"2" ! ! ! Remove timers CALL rm_mp_perf_env() CALL rm_timer_env() + print *,"3" end subroutine @@ -149,12 +152,12 @@ SUBROUTINE dbcsr_init_lib(mp_comm) INTEGER :: error_handle, ithread, nthreads - integer, dimension(2), :: npdims - integer, dimension(2) :: myploc - INTEGER, DIMENSION(:, :), POINTER :: pgrid - integer :: io_unit, group, numnodes, pcol, prow, mynode + !integer, dimension(2) :: npdims + !integer, dimension(2) :: myploc + !INTEGER, DIMENSION(:, :), POINTER :: pgrid + !integer :: io_unit, group, numnodes, pcol, prow, mynode - call dbcsr_mp_make_env(mp_env, group, mp_comm) + call dbcsr_mp_make_env(mp_env, default_group, mp_comm) call dbcsr_init_timer(mp_env) @@ -246,7 +249,13 @@ SUBROUTINE dbcsr_finalize_lib(group, output_unit) CALL timestop(error_handle) - call dbcsr_kill_timer() + call dbcsr_print_timer() + call dbcsr_finalize_timer() + print *,"3.5" + call dbcsr_mp_release(mp_env) + print *,"4" + call mp_comm_free(default_group) + print *,"5" END SUBROUTINE dbcsr_finalize_lib ! ************************************************************************************************** diff --git a/src/mpi/dbcsr_mp_methods.F b/src/mpi/dbcsr_mp_methods.F index 37aad1bc833..eaace11a29b 100644 --- a/src/mpi/dbcsr_mp_methods.F +++ b/src/mpi/dbcsr_mp_methods.F @@ -17,7 +17,10 @@ MODULE dbcsr_mp_methods dbcsr_mp_release USE dbcsr_mpiwrap, ONLY: mp_cart_create, & mp_cart_sub, & - mp_comm_free + mp_comm_free,& + mp_environ,& + mp_cart_rank,& + mp_dims_create USE dbcsr_types, ONLY: dbcsr_mp_obj !$ USE OMP_LIB, ONLY: omp_get_max_threads, omp_get_thread_num, omp_get_num_threads @@ -37,7 +40,7 @@ MODULE dbcsr_mp_methods dbcsr_mp_my_row_group, dbcsr_mp_my_col_group, & dbcsr_mp_has_subgroups, dbcsr_mp_get_process, & dbcsr_mp_grid_setup, dbcsr_mp_grid_remove, & - dbcsr_mp_init, dbcsr_mp_active + dbcsr_mp_init, dbcsr_mp_active, dbcsr_mp_make_env CONTAINS @@ -179,7 +182,6 @@ SUBROUTINE dbcsr_mp_make_env(mp_env, cart_group, mp_group, & prow INTEGER, ALLOCATABLE, DIMENSION(:, :) :: pgrid INTEGER, DIMENSION(2) :: coord, myploc, npdims - LOGICAL :: alive ! --------------------------------------------------------------------------- diff --git a/tests/dbcsr_performance_driver.F b/tests/dbcsr_performance_driver.F index 5d48772f847..2cafddcba2b 100644 --- a/tests/dbcsr_performance_driver.F +++ b/tests/dbcsr_performance_driver.F @@ -61,7 +61,7 @@ PROGRAM dbcsr_performance_driver CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_check_multiply' - TYPE(dbcsr_logger_type), POINTER :: logger + !TYPE(dbcsr_logger_type), POINTER :: logger !*************************************************************************************** @@ -110,24 +110,25 @@ PROGRAM dbcsr_performance_driver ! ! Timers - NULLIFY (logger) - CALL dbcsr_logger_create(logger, mp_env=mp_env, & - default_global_unit_nr=io_unit, & - close_global_unit_on_dealloc=.FALSE.) - CALL dbcsr_add_default_logger(logger) - CALL dbcsr_logger_release(logger) - CALL dbcsr_error_handling_setup() - CALL timings_register_hooks() - CALL add_mp_perf_env() - CALL add_timer_env() + !NULLIFY (logger) + !CALL dbcsr_logger_create(logger, mp_env=mp_env, & + !default_global_unit_nr=io_unit, & + !close_global_unit_on_dealloc=.FALSE.) + !CALL dbcsr_add_default_logger(logger) + !CALL dbcsr_logger_release(logger) + !CALL dbcsr_error_handling_setup() + !CALL timings_register_hooks() + !CALL add_mp_perf_env() + !CALL add_timer_env() ! - ! initialize libdbcsr errors - CALL timeset(routineN, handle) ! ! initialize libdbcsr - CALL dbcsr_init_lib() + CALL dbcsr_init_lib(mp_comm) + + ! initialize libdbcsr errors + CALL timeset(routineN, handle) ! ! print DBCSR configuration @@ -143,17 +144,18 @@ PROGRAM dbcsr_performance_driver END SELECT ! - ! finalize libdbcsr - CALL dbcsr_finalize_lib(mp_comm, io_unit) ! ! finalize libdbcsr errors CALL timestop(handle) + + ! finalize libdbcsr + CALL dbcsr_finalize_lib(mp_comm, io_unit) ! Print timers - CALL timings_report_print(io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) + !CALL timings_report_print(io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) ! Dump callgraph - CALL timings_report_callgraph("test.callgraph") + !CALL timings_report_callgraph("test.callgraph") ! ! clean mp environment @@ -161,7 +163,7 @@ PROGRAM dbcsr_performance_driver ! ! Remove logger - CALL dbcsr_rm_default_logger() + !CALL dbcsr_rm_default_logger() ! ! free comm @@ -169,8 +171,8 @@ PROGRAM dbcsr_performance_driver ! ! Remove timers - CALL rm_mp_perf_env() - CALL rm_timer_env() + !CALL rm_mp_perf_env() + !CALL rm_timer_env() ! ! finalize mpi diff --git a/tests/dbcsr_tensor_unittest.F b/tests/dbcsr_tensor_unittest.F index 52ac3b1ef67..1580ef3f0b5 100644 --- a/tests/dbcsr_tensor_unittest.F +++ b/tests/dbcsr_tensor_unittest.F @@ -72,7 +72,7 @@ PROGRAM dbcsr_tensor_unittest IF (mynode .EQ. 0) io_unit = default_output_unit ! initialize libdbcsr - CALL dbcsr_init_lib() + CALL dbcsr_init_lib(mp_comm) ! Process grid diff --git a/tests/dbcsr_test_csr_conversions.F b/tests/dbcsr_test_csr_conversions.F index 4b9c52a8d6d..7c85a672b4d 100644 --- a/tests/dbcsr_test_csr_conversions.F +++ b/tests/dbcsr_test_csr_conversions.F @@ -61,7 +61,7 @@ PROGRAM dbcsr_test_csr_conversions npdims(:) = 0 CALL mp_cart_create(mp_comm, 2, npdims, myploc, group) CALL mp_environ(numnodes, mynode, group) - CALL dbcsr_init_lib() + CALL dbcsr_init_lib(mp_comm) io_unit = 0 IF (mynode .EQ. 0) io_unit = default_output_unit diff --git a/tests/dbcsr_unittest1.F b/tests/dbcsr_unittest1.F index 73a2282ffae..ced8110d036 100644 --- a/tests/dbcsr_unittest1.F +++ b/tests/dbcsr_unittest1.F @@ -83,7 +83,7 @@ PROGRAM dbcsr_unittest ! initialize libdbcsr IF (acc_get_ndevices() > 0) & CALL acc_set_active_device(MOD(mynode, acc_get_ndevices())) - CALL dbcsr_init_lib() + CALL dbcsr_init_lib(mp_comm) CALL dbcsr_reset_randmat_seed() diff --git a/tests/dbcsr_unittest2.F b/tests/dbcsr_unittest2.F index 896581831d3..8ef7bd64ea0 100644 --- a/tests/dbcsr_unittest2.F +++ b/tests/dbcsr_unittest2.F @@ -82,7 +82,7 @@ PROGRAM dbcsr_unittest ! initialize libdbcsr IF (acc_get_ndevices() > 0) & CALL acc_set_active_device(MOD(mynode, acc_get_ndevices())) - CALL dbcsr_init_lib() + CALL dbcsr_init_lib(mp_comm) CALL dbcsr_reset_randmat_seed() diff --git a/tests/dbcsr_unittest3.F b/tests/dbcsr_unittest3.F index a8309184a0b..f72610f24c2 100644 --- a/tests/dbcsr_unittest3.F +++ b/tests/dbcsr_unittest3.F @@ -83,7 +83,7 @@ PROGRAM dbcsr_unittest ! initialize libdbcsr IF (acc_get_ndevices() > 0) & CALL acc_set_active_device(MOD(mynode, acc_get_ndevices())) - CALL dbcsr_init_lib() + CALL dbcsr_init_lib(mp_comm) CALL dbcsr_reset_randmat_seed() From 2d0140fb5bc0de3c4d98b7ef865e3aecc78fb129 Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Wed, 24 Apr 2019 18:49:15 +0200 Subject: [PATCH 11/88] seems to be finished --- src/CMakeLists.txt | 32 ++++++------- src/core/dbcsr_lib.F | 76 +++++++++++++----------------- tests/dbcsr_performance_driver.F | 4 +- tests/dbcsr_tensor_unittest.F | 4 +- tests/dbcsr_test_csr_conversions.F | 12 +++-- tests/dbcsr_unittest1.F | 15 +++--- tests/dbcsr_unittest2.F | 23 +++++---- tests/dbcsr_unittest3.F | 21 +++++---- 8 files changed, 94 insertions(+), 93 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e94038426b5..e92a9aed59d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -25,6 +25,22 @@ add_fypp_sources(DBCSR_SRCS core/dbcsr_lib.F core/dbcsr_methods.F core/dbcsr_types.F + core/dbcsr_cuda_profiling.F + core/dbcsr_dict.F + core/dbcsr_error_handling.F + core/dbcsr_iter_types.F + core/dbcsr_list_callstackentry.F + core/dbcsr_list.F + core/dbcsr_list_routinereport.F + core/dbcsr_list_routinestat.F + core/dbcsr_list_timerenv.F + core/dbcsr_log_handling.F + core/dbcsr_memory_utilities.F + core/dbcsr_print_messages.F + core/dbcsr_timings_base_type.F + core/dbcsr_timings.F + core/dbcsr_timings_report.F + core/dbcsr_timings_types.F data/dbcsr_data_operations.F data/dbcsr_data_methods.F data/dbcsr_data_methods_low.F @@ -34,22 +50,6 @@ add_fypp_sources(DBCSR_SRCS dist/dbcsr_dist_methods.F dist/dbcsr_dist_operations.F dist/dbcsr_dist_util.F - extras/dbcsr_cuda_profiling.F - extras/dbcsr_dict.F - extras/dbcsr_error_handling.F - extras/dbcsr_iter_types.F - extras/dbcsr_list_callstackentry.F - extras/dbcsr_list.F - extras/dbcsr_list_routinereport.F - extras/dbcsr_list_routinestat.F - extras/dbcsr_list_timerenv.F - extras/dbcsr_log_handling.F - extras/dbcsr_memory_utilities.F - extras/dbcsr_print_messages.F - extras/dbcsr_timings_base_type.F - extras/dbcsr_timings.F - extras/dbcsr_timings_report.F - extras/dbcsr_timings_types.F mm/dbcsr_acc_operations.F mm/dbcsr_mm_3d.F mm/dbcsr_mm_accdrv.F diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 8b08af280db..ed0f26f16d4 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -71,8 +71,8 @@ MODULE dbcsr_lib TYPE(dbcsr_logger_type), POINTER :: logger TYPE(dbcsr_mp_obj) , save :: mp_env - integer, save :: default_group - + integer, save :: default_group, ext_io_unit + CONTAINS ! ************************************************************************************************** @@ -80,14 +80,16 @@ MODULE dbcsr_lib !> !> Prepares the DBCSR library for use. ! ************************************************************************************************** - subroutine dbcsr_init_timer(mp_env) - TYPE(dbcsr_mp_obj) , intent(in) :: mp_env - integer :: io_unit + subroutine dbcsr_init_timer(mp_comm, io_unit) + INTEGER, INTENT(IN) :: mp_comm + integer, intent(in) :: io_unit - io_unit = 0 - IF (mp_env % mp % mynode .EQ. mp_env % mp % source) io_unit = default_output_unit + call dbcsr_mp_make_env(mp_env, default_group, mp_comm) + !io_unit = 0 + !IF (mp_env % mp % mynode .EQ. mp_env % mp % source) io_unit = default_output_unit ! ! Timers + NULLIFY (logger) CALL dbcsr_logger_create(logger, mp_env=mp_env, & default_global_unit_nr=io_unit, & @@ -107,21 +109,11 @@ subroutine dbcsr_init_timer(mp_env) !> Prepares the DBCSR library for use. ! ************************************************************************************************** subroutine dbcsr_finalize_timer() - ! clean mp environment - !CALL dbcsr_mp_release(mp_env) - - print *,"1" - ! - ! Remove logger CALL dbcsr_rm_default_logger() - - print *,"2" - ! - ! - ! Remove timers + CALL dbcsr_mp_release(mp_env) + call mp_comm_free(default_group) CALL rm_mp_perf_env() CALL rm_timer_env() - print *,"3" end subroutine @@ -131,12 +123,12 @@ subroutine dbcsr_finalize_timer() !> Prepares the DBCSR library for use. ! ************************************************************************************************** subroutine dbcsr_print_timer() - integer :: io_unit + !integer :: io_unit - io_unit = 0 - IF (mp_env % mp % mynode .EQ. mp_env % mp % source) io_unit = default_output_unit + !io_unit = 0 + !IF (mp_env % mp % mynode .EQ. mp_env % mp % source) io_unit = default_output_unit - CALL timings_report_print(io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) + CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) end subroutine @@ -145,8 +137,9 @@ subroutine dbcsr_print_timer() !> !> Prepares the DBCSR library for use. ! ************************************************************************************************** - SUBROUTINE dbcsr_init_lib(mp_comm) + SUBROUTINE dbcsr_init_lib(mp_comm, io_unit) INTEGER, INTENT(IN) :: mp_comm + integer, intent(in) :: io_unit CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_init_lib', routineP = moduleN//':'//routineN @@ -157,9 +150,9 @@ SUBROUTINE dbcsr_init_lib(mp_comm) !INTEGER, DIMENSION(:, :), POINTER :: pgrid !integer :: io_unit, group, numnodes, pcol, prow, mynode - call dbcsr_mp_make_env(mp_env, default_group, mp_comm) + ext_io_unit = io_unit - call dbcsr_init_timer(mp_env) + call dbcsr_init_timer(mp_comm, io_unit) IF (is_initialized) RETURN CALL timeset(routineN, error_handle) @@ -203,25 +196,25 @@ END SUBROUTINE dbcsr_init_lib !> \param group ... !> \param output_unit ... ! ************************************************************************************************** - SUBROUTINE dbcsr_finalize_lib(group, output_unit) - INTEGER, INTENT(IN) :: group - INTEGER, INTENT(IN), OPTIONAL :: output_unit + SUBROUTINE dbcsr_finalize_lib() + !INTEGER, INTENT(IN) :: group + !INTEGER, INTENT(IN), OPTIONAL :: output_unit CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_finalize_lib', & routineP = moduleN//':'//routineN - INTEGER :: error_handle, io_unit, ithread, mynode + INTEGER :: error_handle, io_unit, ithread !, mynode IF (.NOT. is_initialized) RETURN CALL timeset(routineN, error_handle) - io_unit = 0 - IF (PRESENT(output_unit)) THEN - io_unit = output_unit - ELSE - CALL mp_environ(taskid=mynode, groupid=group) - IF (mynode .EQ. 0) io_unit = default_output_unit - ENDIF + io_unit = ext_io_unit + !IF (PRESENT(output_unit)) THEN + !io_unit = output_unit + !ELSE + !CALL mp_environ(taskid=mynode, groupid=default_group) + !IF (mynode .EQ. 0) io_unit = default_output_unit + !ENDIF IF (io_unit > 0) THEN WRITE (UNIT=io_unit, FMT="(/,T2,A)") REPEAT("-", 79) @@ -231,8 +224,8 @@ SUBROUTINE dbcsr_finalize_lib(group, output_unit) WRITE (UNIT=io_unit, FMT="(T2,A)") REPEAT("-", 79) END IF -!$OMP PARALLEL DEFAULT(NONE) PRIVATE(ithread) SHARED(io_unit, group, cublas_handles) - CALL dbcsr_multiply_lib_finalize(group, io_unit) +!$OMP PARALLEL DEFAULT(NONE) PRIVATE(ithread) SHARED(io_unit, default_group, cublas_handles) + CALL dbcsr_multiply_lib_finalize(default_group, io_unit) ithread = 0 !$ ithread = omp_get_thread_num() CALL cublas_handle_destroy(cublas_handles(ithread + 1)) @@ -251,11 +244,6 @@ SUBROUTINE dbcsr_finalize_lib(group, output_unit) call dbcsr_print_timer() call dbcsr_finalize_timer() - print *,"3.5" - call dbcsr_mp_release(mp_env) - print *,"4" - call mp_comm_free(default_group) - print *,"5" END SUBROUTINE dbcsr_finalize_lib ! ************************************************************************************************** diff --git a/tests/dbcsr_performance_driver.F b/tests/dbcsr_performance_driver.F index 2cafddcba2b..99e22fb675c 100644 --- a/tests/dbcsr_performance_driver.F +++ b/tests/dbcsr_performance_driver.F @@ -125,7 +125,7 @@ PROGRAM dbcsr_performance_driver ! ! initialize libdbcsr - CALL dbcsr_init_lib(mp_comm) + CALL dbcsr_init_lib(mp_comm,io_unit) ! initialize libdbcsr errors CALL timeset(routineN, handle) @@ -150,7 +150,6 @@ PROGRAM dbcsr_performance_driver CALL timestop(handle) ! finalize libdbcsr - CALL dbcsr_finalize_lib(mp_comm, io_unit) ! Print timers !CALL timings_report_print(io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) @@ -169,6 +168,7 @@ PROGRAM dbcsr_performance_driver ! free comm CALL mp_comm_free(group) + CALL dbcsr_finalize_lib() ! ! Remove timers !CALL rm_mp_perf_env() diff --git a/tests/dbcsr_tensor_unittest.F b/tests/dbcsr_tensor_unittest.F index 1580ef3f0b5..325502b5b5f 100644 --- a/tests/dbcsr_tensor_unittest.F +++ b/tests/dbcsr_tensor_unittest.F @@ -72,7 +72,7 @@ PROGRAM dbcsr_tensor_unittest IF (mynode .EQ. 0) io_unit = default_output_unit ! initialize libdbcsr - CALL dbcsr_init_lib(mp_comm) + CALL dbcsr_init_lib(mp_comm, io_unit) ! Process grid @@ -753,7 +753,7 @@ PROGRAM dbcsr_tensor_unittest !--------------------------------------------------------------------------------------------------! ! finalize libdbcsr - CALL dbcsr_finalize_lib(mp_comm, io_unit) + CALL dbcsr_finalize_lib() ! ! finalize mpi diff --git a/tests/dbcsr_test_csr_conversions.F b/tests/dbcsr_test_csr_conversions.F index 7c85a672b4d..452c57891f6 100644 --- a/tests/dbcsr_test_csr_conversions.F +++ b/tests/dbcsr_test_csr_conversions.F @@ -58,13 +58,15 @@ PROGRAM dbcsr_test_csr_conversions ! Set up everything as in the dbcsr example codes CALL mp_world_init(mp_comm) + + io_unit = 0 + IF (mynode .EQ. 0) io_unit = default_output_unit + + CALL dbcsr_init_lib(mp_comm, io_unit) + npdims(:) = 0 CALL mp_cart_create(mp_comm, 2, npdims, myploc, group) CALL mp_environ(numnodes, mynode, group) - CALL dbcsr_init_lib(mp_comm) - - io_unit = 0 - IF (mynode .EQ. 0) io_unit = default_output_unit ! Set seed for random number generator CALL RANDOM_SEED(size=seedsz) @@ -132,8 +134,8 @@ PROGRAM dbcsr_test_csr_conversions DEALLOCATE (seed) - CALL dbcsr_finalize_lib(mp_comm, io_unit) CALL mp_comm_free(group) + CALL dbcsr_finalize_lib() CALL mp_world_finalize() CONTAINS diff --git a/tests/dbcsr_unittest1.F b/tests/dbcsr_unittest1.F index ced8110d036..0e95048dc42 100644 --- a/tests/dbcsr_unittest1.F +++ b/tests/dbcsr_unittest1.F @@ -54,8 +54,6 @@ PROGRAM dbcsr_unittest !*************************************************************************************** ! - ! initialize libdbcsr errors - CALL timeset(routineN, handle) ! ! initialize mpi @@ -83,8 +81,12 @@ PROGRAM dbcsr_unittest ! initialize libdbcsr IF (acc_get_ndevices() > 0) & CALL acc_set_active_device(MOD(mynode, acc_get_ndevices())) - CALL dbcsr_init_lib(mp_comm) + + CALL dbcsr_init_lib(mp_comm, io_unit) + ! initialize libdbcsr errors + CALL timeset(routineN, handle) + CALL dbcsr_reset_randmat_seed() ! run tests @@ -314,9 +316,8 @@ PROGRAM dbcsr_unittest ! end of test cases --------------------------------------------------------- - ! finalize libdbcsr - CALL dbcsr_finalize_lib(mp_comm, io_unit) + CALL timestop(handle) ! ! clean mp environment CALL dbcsr_mp_release(mp_env) @@ -324,10 +325,12 @@ PROGRAM dbcsr_unittest ! ! finalize mpi CALL mp_comm_free(group) + + ! finalize libdbcsr + CALL dbcsr_finalize_lib() CALL mp_world_finalize() ! ! finalize libdbcsr errors - CALL timestop(handle) END PROGRAM dbcsr_unittest diff --git a/tests/dbcsr_unittest2.F b/tests/dbcsr_unittest2.F index 8ef7bd64ea0..603a909df36 100644 --- a/tests/dbcsr_unittest2.F +++ b/tests/dbcsr_unittest2.F @@ -52,9 +52,6 @@ PROGRAM dbcsr_unittest !*************************************************************************************** - ! - ! initialize libdbcsr errors - CALL timeset(routineN, handle) ! ! initialize mpi @@ -82,8 +79,13 @@ PROGRAM dbcsr_unittest ! initialize libdbcsr IF (acc_get_ndevices() > 0) & CALL acc_set_active_device(MOD(mynode, acc_get_ndevices())) - CALL dbcsr_init_lib(mp_comm) + CALL dbcsr_init_lib(mp_comm, io_unit) + + ! + ! initialize libdbcsr errors + CALL timeset(routineN, handle) + CALL dbcsr_reset_randmat_seed() ! run tests @@ -122,9 +124,11 @@ PROGRAM dbcsr_unittest ! end of test cases --------------------------------------------------------- - ! finalize libdbcsr - CALL dbcsr_finalize_lib(mp_comm, io_unit) + ! + ! finalize libdbcsr errors + CALL timestop(handle) + ! ! clean mp environment CALL dbcsr_mp_release(mp_env) @@ -132,10 +136,11 @@ PROGRAM dbcsr_unittest ! ! finalize mpi CALL mp_comm_free(group) + + ! finalize libdbcsr + CALL dbcsr_finalize_lib() + CALL mp_world_finalize() - ! - ! finalize libdbcsr errors - CALL timestop(handle) END PROGRAM dbcsr_unittest diff --git a/tests/dbcsr_unittest3.F b/tests/dbcsr_unittest3.F index f72610f24c2..2b9418a05e5 100644 --- a/tests/dbcsr_unittest3.F +++ b/tests/dbcsr_unittest3.F @@ -53,9 +53,6 @@ PROGRAM dbcsr_unittest !*************************************************************************************** - ! - ! initialize libdbcsr errors - CALL timeset(routineN, handle) ! ! initialize mpi @@ -83,7 +80,11 @@ PROGRAM dbcsr_unittest ! initialize libdbcsr IF (acc_get_ndevices() > 0) & CALL acc_set_active_device(MOD(mynode, acc_get_ndevices())) - CALL dbcsr_init_lib(mp_comm) + + CALL dbcsr_init_lib(mp_comm, io_unit) + ! + ! initialize libdbcsr errors + CALL timeset(routineN, handle) CALL dbcsr_reset_randmat_seed() @@ -125,8 +126,9 @@ PROGRAM dbcsr_unittest ! end of test cases --------------------------------------------------------- - ! finalize libdbcsr - CALL dbcsr_finalize_lib(mp_comm, io_unit) + ! + ! finalize libdbcsr errors + CALL timestop(handle) ! ! clean mp environment @@ -135,10 +137,11 @@ PROGRAM dbcsr_unittest ! ! finalize mpi CALL mp_comm_free(group) + + ! finalize libdbcsr + CALL dbcsr_finalize_lib() + CALL mp_world_finalize() - ! - ! finalize libdbcsr errors - CALL timestop(handle) END PROGRAM dbcsr_unittest From 55ef6b5250f19424b0f89fc8b77693e637e673c7 Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Thu, 25 Apr 2019 15:54:13 +0200 Subject: [PATCH 12/88] fixed examples --- examples/dbcsr_example_1.F | 6 +++--- examples/dbcsr_example_2.F | 6 +++--- examples/dbcsr_example_3.F | 6 +++--- examples/dbcsr_example_3.cpp | 6 ++++-- src/core/dbcsr_lib.F | 21 +++++++++++---------- src/dbcsr.h | 10 +++++----- src/dbcsr_api_c.F | 13 +++++++------ 7 files changed, 36 insertions(+), 32 deletions(-) diff --git a/examples/dbcsr_example_1.F b/examples/dbcsr_example_1.F index 679418d6954..16bc625297b 100644 --- a/examples/dbcsr_example_1.F +++ b/examples/dbcsr_example_1.F @@ -71,7 +71,7 @@ PROGRAM dbcsr_example_1 !*************************************************************************************** ! ! initialize libdbcsr - CALL dbcsr_init_lib() + CALL dbcsr_init_lib(MPI_COMM_WORLD) ! ! the matrix will contain nblkrows_total row blocks and nblkcols_total column blocks @@ -125,13 +125,13 @@ PROGRAM dbcsr_example_1 !*************************************************************************************** ! - ! finalize libdbcsr - CALL dbcsr_finalize_lib(MPI_COMM_WORLD) ! free comm CALL mpi_comm_free(group, ierr) IF (ierr /= 0) STOP "Error in MPI_Comm_free" + ! finalize libdbcsr + CALL dbcsr_finalize_lib() ! ! finalize mpi CALL mpi_finalize(ierr) diff --git a/examples/dbcsr_example_2.F b/examples/dbcsr_example_2.F index f7213a0684b..0fe80797731 100644 --- a/examples/dbcsr_example_2.F +++ b/examples/dbcsr_example_2.F @@ -75,7 +75,7 @@ PROGRAM dbcsr_example_2 !*************************************************************************************** ! ! initialize libdbcsr - CALL dbcsr_init_lib() + CALL dbcsr_init_lib(MPI_COMM_WORLD) ! ! the matrix will contain nblkrows_total row blocks and nblkcols_total column blocks @@ -166,13 +166,13 @@ PROGRAM dbcsr_example_2 !*************************************************************************************** ! - ! finalize libdbcsr - CALL dbcsr_finalize_lib(MPI_COMM_WORLD) ! free comm CALL mpi_comm_free(group, ierr) IF (ierr /= 0) STOP "Error in MPI_Comm_free" + ! finalize libdbcsr + CALL dbcsr_finalize_lib() ! ! finalize mpi CALL mpi_finalize(ierr) diff --git a/examples/dbcsr_example_3.F b/examples/dbcsr_example_3.F index 08433934ab8..3cc955487a6 100644 --- a/examples/dbcsr_example_3.F +++ b/examples/dbcsr_example_3.F @@ -76,7 +76,7 @@ PROGRAM dbcsr_example_3 !*************************************************************************************** ! ! initialize libdbcsr - CALL dbcsr_init_lib() + CALL dbcsr_init_lib(MPI_COMM_WORLD) ! ! the matrix will contain nblkrows_total row blocks and nblkcols_total column blocks @@ -193,13 +193,13 @@ PROGRAM dbcsr_example_3 CALL dbcsr_distribution_release(dist) DEALLOCATE (row_blk_sizes, col_blk_sizes) - ! finalize libdbcsr - CALL dbcsr_finalize_lib(MPI_COMM_WORLD) ! free comm CALL mpi_comm_free(group, ierr) IF (ierr /= 0) STOP "Error in MPI_Comm_free" + ! finalize libdbcsr + CALL dbcsr_finalize_lib() ! ! finalize mpi CALL mpi_finalize(ierr) diff --git a/examples/dbcsr_example_3.cpp b/examples/dbcsr_example_3.cpp index c6728b9c338..9f00627f435 100644 --- a/examples/dbcsr_example_3.cpp +++ b/examples/dbcsr_example_3.cpp @@ -58,7 +58,7 @@ int main(int argc, char* argv[]) << ", (" << coord[0] << ", " << coord[1] << ") in the 2D grid" << std::endl; - c_dbcsr_init_lib(); + c_dbcsr_init_lib(MPI_COMM_WORLD, nullptr); // Total number of blocks int nblkrows_total = 4; @@ -139,9 +139,11 @@ int main(int argc, char* argv[]) c_dbcsr_distribution_release(&dist); - c_dbcsr_finalize_lib(group); MPI_Comm_free(&group); + + c_dbcsr_finalize_lib(); + MPI_Finalize(); return 0; diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index ed0f26f16d4..c7da6934eec 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -139,7 +139,7 @@ subroutine dbcsr_print_timer() ! ************************************************************************************************** SUBROUTINE dbcsr_init_lib(mp_comm, io_unit) INTEGER, INTENT(IN) :: mp_comm - integer, intent(in) :: io_unit + integer, intent(in), optional :: io_unit CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_init_lib', routineP = moduleN//':'//routineN @@ -148,11 +148,18 @@ SUBROUTINE dbcsr_init_lib(mp_comm, io_unit) !integer, dimension(2) :: npdims !integer, dimension(2) :: myploc !INTEGER, DIMENSION(:, :), POINTER :: pgrid - !integer :: io_unit, group, numnodes, pcol, prow, mynode + integer :: numnodes, mynode + + IF (PRESENT(io_unit)) THEN + ext_io_unit = io_unit + ELSE + ext_io_unit = 0 + CALL mp_environ(numnodes, mynode, mp_comm) + IF (mynode .EQ. 0) ext_io_unit = default_output_unit + ENDIF - ext_io_unit = io_unit - call dbcsr_init_timer(mp_comm, io_unit) + call dbcsr_init_timer(mp_comm, ext_io_unit) IF (is_initialized) RETURN CALL timeset(routineN, error_handle) @@ -209,12 +216,6 @@ SUBROUTINE dbcsr_finalize_lib() CALL timeset(routineN, error_handle) io_unit = ext_io_unit - !IF (PRESENT(output_unit)) THEN - !io_unit = output_unit - !ELSE - !CALL mp_environ(taskid=mynode, groupid=default_group) - !IF (mynode .EQ. 0) io_unit = default_output_unit - !ENDIF IF (io_unit > 0) THEN WRITE (UNIT=io_unit, FMT="(/,T2,A)") REPEAT("-", 79) diff --git a/src/dbcsr.h b/src/dbcsr.h index 1dccbc64949..886259809e2 100644 --- a/src/dbcsr.h +++ b/src/dbcsr.h @@ -16,16 +16,16 @@ #ifdef __cplusplus extern "C" { #endif - void c_dbcsr_init_lib(); + void c_dbcsr_init_lib_internal(MPI_Fint* fcomm, int* io_unit); - void c_dbcsr_finalize_lib_aux(MPI_Fint* fcomm); - - static void c_dbcsr_finalize_lib(MPI_Comm comm) + static void c_dbcsr_init_lib(MPI_Comm comm, int* io_unit) { MPI_Fint fcomm = MPI_Comm_c2f(comm); - c_dbcsr_finalize_lib_aux(&fcomm); + c_dbcsr_init_lib_internal(&fcomm, io_unit); } + void c_dbcsr_finalize_lib(); + void c_dbcsr_distribution_new_aux(void** dist, MPI_Fint* fcomm, int* row_dist, int row_dist_size, int* col_dist, int col_dist_size); diff --git a/src/dbcsr_api_c.F b/src/dbcsr_api_c.F index afc1137d55b..f989b5e443f 100644 --- a/src/dbcsr_api_c.F +++ b/src/dbcsr_api_c.F @@ -54,8 +54,11 @@ END SUBROUTINE c_f_string !> \param C ... !> \param name="c_dbcsr_init_lib" ... ! ************************************************************************************************** - SUBROUTINE c_dbcsr_init_lib() bind(C, name="c_dbcsr_init_lib") - CALL dbcsr_init_lib() + SUBROUTINE c_dbcsr_init_lib(fcomm, io_unit) bind(C, name="c_dbcsr_init_lib_internal") + INTEGER(kind=c_int), INTENT(in) :: fcomm + INTEGER(kind=c_int), INTENT(in), optional :: io_unit + + CALL dbcsr_init_lib(fcomm, io_unit) END SUBROUTINE ! ************************************************************************************************** @@ -64,10 +67,8 @@ SUBROUTINE c_dbcsr_init_lib() bind(C, name="c_dbcsr_init_lib") !> \param C ... !> \param name="c_dbcsr_finalize_lib_aux" ... ! ************************************************************************************************** - SUBROUTINE c_dbcsr_finalise_lib(fcomm) bind(C, name="c_dbcsr_finalize_lib_aux") - INTEGER(kind=c_int), INTENT(in) :: fcomm - - CALL dbcsr_finalize_lib(fcomm) + SUBROUTINE c_dbcsr_finalise_lib() bind(C, name="c_dbcsr_finalize_lib") + CALL dbcsr_finalize_lib() END SUBROUTINE ! ************************************************************************************************** From 2512f6159c9f85f137770820ff0832899fd275c7 Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Thu, 25 Apr 2019 18:05:22 +0200 Subject: [PATCH 13/88] fixes --- src/core/dbcsr_timings.F | 2 ++ tests/dbcsr_tas_unittest.F | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/core/dbcsr_timings.F b/src/core/dbcsr_timings.F index ad8653d0320..f574fa04b77 100644 --- a/src/core/dbcsr_timings.F +++ b/src/core/dbcsr_timings.F @@ -136,6 +136,8 @@ SUBROUTINE rm_timer_env() timer_env => list_pop(timers_stack) CALL timer_env_release(timer_env) IF (list_size(timers_stack) == 0) CALL list_destroy(timers_stack) + timeset_hook => NULL() + timestop_hook => NULL() END SUBROUTINE rm_timer_env ! ************************************************************************************************** diff --git a/tests/dbcsr_tas_unittest.F b/tests/dbcsr_tas_unittest.F index a88480c4924..e1c58c27c13 100644 --- a/tests/dbcsr_tas_unittest.F +++ b/tests/dbcsr_tas_unittest.F @@ -47,7 +47,12 @@ PROGRAM dbcsr_tas_unittest CALL mp_world_init(mp_comm) - CALL dbcsr_init_lib() + CALL mp_environ(numnodes, mynode, mp_comm) + + io_unit = 0 + IF (mynode .EQ. 0) io_unit = default_output_unit + + CALL dbcsr_init_lib(mp_comm, io_unit) CALL dbcsr_tas_random_bsizes([13, 8, 5, 25, 12], 2, bsize_m) CALL dbcsr_tas_random_bsizes([3, 78, 33, 12, 3, 15], 1, bsize_n) @@ -141,7 +146,7 @@ PROGRAM dbcsr_tas_unittest CALL mp_comm_free(mp_comm_C) CALL mp_comm_free(mp_comm_Ct) - CALL dbcsr_finalize_lib(mp_comm, io_unit) + CALL dbcsr_finalize_lib() CALL mp_world_finalize() From 233a99ef8f278bed7552df0f3201c0a59ab5f458 Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Fri, 26 Apr 2019 16:42:32 +0200 Subject: [PATCH 14/88] added external timer functionality in dbcsr_init_lib --- src/core/dbcsr_lib.F | 100 ++++++++++++++++++++--------- src/core/dbcsr_timings.F | 31 ++++++++- src/mpi/dbcsr_mp_methods.F | 10 ++- tests/dbcsr_performance_driver.F | 28 -------- tests/dbcsr_test_csr_conversions.F | 2 + 5 files changed, 106 insertions(+), 65 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index c7da6934eec..97ea55cd287 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -47,6 +47,13 @@ MODULE dbcsr_lib dbcsr_logger_type, & dbcsr_rm_default_logger + USE dbcsr_base_hooks, ONLY: timeset_hook, & + timestop_hook,& + dbcsr_abort_hook,& + dbcsr_warn_hook,& + dbcsr_abort_interface, dbcsr_warn_interface, & + timeset_interface, timestop_interface + use dbcsr_types, only: dbcsr_mp_obj use dbcsr_mp_methods, only: dbcsr_mp_new, dbcsr_mp_release, & @@ -72,7 +79,11 @@ MODULE dbcsr_lib TYPE(dbcsr_logger_type), POINTER :: logger TYPE(dbcsr_mp_obj) , save :: mp_env integer, save :: default_group, ext_io_unit - + + interface dbcsr_init_lib + module procedure dbcsr_init_lib_def + module procedure dbcsr_init_lib_ext + end interface CONTAINS ! ************************************************************************************************** @@ -82,27 +93,34 @@ MODULE dbcsr_lib ! ************************************************************************************************** subroutine dbcsr_init_timer(mp_comm, io_unit) INTEGER, INTENT(IN) :: mp_comm - integer, intent(in) :: io_unit + integer, intent(in), optional :: io_unit + integer :: numnodes, mynode + + IF (PRESENT(io_unit)) THEN + ext_io_unit = io_unit + ELSE + ext_io_unit = 0 + CALL mp_environ(numnodes, mynode, mp_comm) + IF (mynode .EQ. 0) ext_io_unit = default_output_unit + ENDIF call dbcsr_mp_make_env(mp_env, default_group, mp_comm) - !io_unit = 0 - !IF (mp_env % mp % mynode .EQ. mp_env % mp % source) io_unit = default_output_unit - ! - ! Timers NULLIFY (logger) CALL dbcsr_logger_create(logger, mp_env=mp_env, & - default_global_unit_nr=io_unit, & + default_global_unit_nr=ext_io_unit, & close_global_unit_on_dealloc=.FALSE.) CALL dbcsr_add_default_logger(logger) CALL dbcsr_logger_release(logger) CALL dbcsr_error_handling_setup() - CALL timings_register_hooks() - CALL add_mp_perf_env() - CALL add_timer_env() end subroutine - + +! final part of timer initialization + subroutine add_envs() + CALL add_mp_perf_env() + CALL add_timer_env() + end subroutine ! ************************************************************************************************** !> \brief Kill timers !> @@ -123,12 +141,46 @@ subroutine dbcsr_finalize_timer() !> Prepares the DBCSR library for use. ! ************************************************************************************************** subroutine dbcsr_print_timer() - !integer :: io_unit + CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) + end subroutine - !io_unit = 0 - !IF (mp_env % mp % mynode .EQ. mp_env % mp % source) io_unit = default_output_unit - CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) + +! ************************************************************************************************** +!> \brief Initialize the DBCSR library using internal timer callbacks +! ************************************************************************************************** + SUBROUTINE dbcsr_init_lib_def(mp_comm, io_unit) + INTEGER, INTENT(IN) :: mp_comm + integer, intent(in), optional :: io_unit + + call dbcsr_init_timer(mp_comm, io_unit) + call timings_register_hooks() + call add_envs() + + call dbcsr_init_lib_only() + end subroutine + + +! ************************************************************************************************** +!> \brief Initialize the DBCSR library using external timer callbacks +! ************************************************************************************************** + SUBROUTINE dbcsr_init_lib_ext(mp_comm, io_unit, & + in_timeset_hook, in_timestop_hook, & + in_abort_hook, in_warn_hook) + INTEGER, INTENT(IN) :: mp_comm + integer, intent(in), optional :: io_unit + + PROCEDURE(timeset_interface), INTENT(IN), POINTER :: in_timeset_hook + PROCEDURE(timestop_interface), INTENT(IN), POINTER :: in_timestop_hook + PROCEDURE(dbcsr_abort_interface), INTENT(IN), POINTER :: in_abort_hook + PROCEDURE(dbcsr_warn_interface), INTENT(IN), POINTER :: in_warn_hook + + call dbcsr_init_timer(mp_comm, io_unit) + call timings_register_hooks(in_timeset_hook, in_timestop_hook,& + in_abort_hook, in_warn_hook) + call add_envs() + + call dbcsr_init_lib_only() end subroutine @@ -137,9 +189,7 @@ subroutine dbcsr_print_timer() !> !> Prepares the DBCSR library for use. ! ************************************************************************************************** - SUBROUTINE dbcsr_init_lib(mp_comm, io_unit) - INTEGER, INTENT(IN) :: mp_comm - integer, intent(in), optional :: io_unit + SUBROUTINE dbcsr_init_lib_only() CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_init_lib', routineP = moduleN//':'//routineN @@ -148,18 +198,6 @@ SUBROUTINE dbcsr_init_lib(mp_comm, io_unit) !integer, dimension(2) :: npdims !integer, dimension(2) :: myploc !INTEGER, DIMENSION(:, :), POINTER :: pgrid - integer :: numnodes, mynode - - IF (PRESENT(io_unit)) THEN - ext_io_unit = io_unit - ELSE - ext_io_unit = 0 - CALL mp_environ(numnodes, mynode, mp_comm) - IF (mynode .EQ. 0) ext_io_unit = default_output_unit - ENDIF - - - call dbcsr_init_timer(mp_comm, ext_io_unit) IF (is_initialized) RETURN CALL timeset(routineN, error_handle) @@ -194,7 +232,7 @@ SUBROUTINE dbcsr_init_lib(mp_comm, io_unit) is_initialized = .TRUE. CALL timestop(error_handle) - END SUBROUTINE dbcsr_init_lib + END SUBROUTINE ! ************************************************************************************************** !> \brief Finalize the DBCSR library diff --git a/src/core/dbcsr_timings.F b/src/core/dbcsr_timings.F index f574fa04b77..bca7265e551 100644 --- a/src/core/dbcsr_timings.F +++ b/src/core/dbcsr_timings.F @@ -18,7 +18,11 @@ ! ************************************************************************************************** MODULE dbcsr_timings USE dbcsr_base_hooks, ONLY: timeset_hook, & - timestop_hook + timestop_hook,& + dbcsr_abort_hook,& + dbcsr_warn_hook, & + dbcsr_abort_interface, dbcsr_warn_interface, & + timeset_interface, timestop_interface USE dbcsr_cuda_profiling, ONLY: cuda_mem_info, & cuda_nvtx_range_pop, & cuda_nvtx_range_push @@ -65,16 +69,37 @@ MODULE dbcsr_timings INTEGER, PUBLIC, PARAMETER :: default_timings_level = 1 INTEGER, PUBLIC, SAVE :: global_timings_level = default_timings_level + interface timings_register_hooks + MODULE PROCEDURE timings_register_hooks_def + MODULE PROCEDURE timings_register_hooks_ext + end interface CONTAINS ! ************************************************************************************************** !> \brief Registers handlers with base_hooks.F !> \author Ole Schuett ! ************************************************************************************************** - SUBROUTINE timings_register_hooks() + SUBROUTINE timings_register_hooks_def() timeset_hook => timeset_handler timestop_hook => timestop_handler - END SUBROUTINE timings_register_hooks + dbcsr_abort_hook => NULL() + dbcsr_warn_hook => NULL() + END SUBROUTINE timings_register_hooks_def + + + SUBROUTINE timings_register_hooks_ext(in_timeset_hook, in_timestop_hook, & + in_abort_hook, in_warn_hook) + PROCEDURE(timeset_interface), INTENT(IN), POINTER :: in_timeset_hook + PROCEDURE(timestop_interface), INTENT(IN), POINTER :: in_timestop_hook + PROCEDURE(dbcsr_abort_interface), INTENT(IN), POINTER :: in_abort_hook + PROCEDURE(dbcsr_warn_interface), INTENT(IN), POINTER :: in_warn_hook + + timeset_hook => in_timeset_hook + timestop_hook => in_timestop_hook + dbcsr_abort_hook => in_abort_hook + dbcsr_warn_hook => in_warn_hook + + END SUBROUTINE timings_register_hooks_ext ! ************************************************************************************************** !> \brief adds the given timer_env to the top of the stack diff --git a/src/mpi/dbcsr_mp_methods.F b/src/mpi/dbcsr_mp_methods.F index eaace11a29b..4f74c066f9d 100644 --- a/src/mpi/dbcsr_mp_methods.F +++ b/src/mpi/dbcsr_mp_methods.F @@ -121,7 +121,7 @@ SUBROUTINE dbcsr_mp_new(mp_env, pgrid, mp_group, mynode, numnodes, myprow, & ENDDO column_loop ENDIF mp_env%mp%subgroups_defined = .FALSE. - call dbcsr_mp_grid_setup(mp_env) + !call dbcsr_mp_grid_setup(mp_env) END SUBROUTINE dbcsr_mp_new ! ************************************************************************************************** @@ -145,10 +145,14 @@ SUBROUTINE dbcsr_mp_grid_setup(mp_env) CALL mp_cart_create(mp_env%mp%mp_group, ndims, & dims, my_pos, & tmp_group) - IF (my_pos(1) .NE. mp_env%mp%myprow) & + IF (my_pos(1) .NE. mp_env%mp%myprow) then + error stop 1 DBCSR_ABORT("Got different MPI process grid") - IF (my_pos(2) .NE. mp_env%mp%mypcol) & + endif + IF (my_pos(2) .NE. mp_env%mp%mypcol) then + error stop 1 DBCSR_ABORT("Got different MPI process grid") + endif ! remain = (/.FALSE., .TRUE./) CALL mp_cart_sub(tmp_group, remain, mp_env%mp%prow_group) diff --git a/tests/dbcsr_performance_driver.F b/tests/dbcsr_performance_driver.F index 99e22fb675c..1a7bc8074cc 100644 --- a/tests/dbcsr_performance_driver.F +++ b/tests/dbcsr_performance_driver.F @@ -61,7 +61,6 @@ PROGRAM dbcsr_performance_driver CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_check_multiply' - !TYPE(dbcsr_logger_type), POINTER :: logger !*************************************************************************************** @@ -108,21 +107,6 @@ PROGRAM dbcsr_performance_driver io_unit = 0 IF (mynode .EQ. mp_env%mp%source) io_unit = default_output_unit - ! - ! Timers - !NULLIFY (logger) - !CALL dbcsr_logger_create(logger, mp_env=mp_env, & - !default_global_unit_nr=io_unit, & - !close_global_unit_on_dealloc=.FALSE.) - !CALL dbcsr_add_default_logger(logger) - !CALL dbcsr_logger_release(logger) - !CALL dbcsr_error_handling_setup() - !CALL timings_register_hooks() - !CALL add_mp_perf_env() - !CALL add_timer_env() - - ! - ! ! initialize libdbcsr CALL dbcsr_init_lib(mp_comm,io_unit) @@ -143,19 +127,11 @@ PROGRAM dbcsr_performance_driver DBCSR_ABORT("operation not found") END SELECT - ! ! ! finalize libdbcsr errors CALL timestop(handle) - ! finalize libdbcsr - - ! Print timers - !CALL timings_report_print(io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) - ! Dump callgraph - !CALL timings_report_callgraph("test.callgraph") - ! ! clean mp environment CALL dbcsr_mp_release(mp_env) @@ -170,10 +146,6 @@ PROGRAM dbcsr_performance_driver CALL dbcsr_finalize_lib() ! - ! Remove timers - !CALL rm_mp_perf_env() - !CALL rm_timer_env() - ! ! finalize mpi CALL mp_world_finalize() diff --git a/tests/dbcsr_test_csr_conversions.F b/tests/dbcsr_test_csr_conversions.F index 452c57891f6..f2c708fc3bb 100644 --- a/tests/dbcsr_test_csr_conversions.F +++ b/tests/dbcsr_test_csr_conversions.F @@ -59,6 +59,8 @@ PROGRAM dbcsr_test_csr_conversions ! Set up everything as in the dbcsr example codes CALL mp_world_init(mp_comm) + CALL mp_environ(numnodes, mynode, mp_comm) + io_unit = 0 IF (mynode .EQ. 0) io_unit = default_output_unit From bd593cc5adb57ed6319e3f26d840c4bd96bfefb9 Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Fri, 26 Apr 2019 18:42:55 +0200 Subject: [PATCH 15/88] fix --- src/core/dbcsr_lib.F | 35 ++++++++++++++++------------------- src/mpi/dbcsr_mp_methods.F | 8 ++------ 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 97ea55cd287..51ccfcea7ba 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -86,12 +86,9 @@ MODULE dbcsr_lib end interface CONTAINS -! ************************************************************************************************** -!> \brief Initialize timers -!> -!> Prepares the DBCSR library for use. -! ************************************************************************************************** - subroutine dbcsr_init_timer(mp_comm, io_unit) + + + subroutine base_init(mp_comm, io_unit) INTEGER, INTENT(IN) :: mp_comm integer, intent(in), optional :: io_unit integer :: numnodes, mynode @@ -105,7 +102,13 @@ subroutine dbcsr_init_timer(mp_comm, io_unit) ENDIF call dbcsr_mp_make_env(mp_env, default_group, mp_comm) - + end subroutine +! ************************************************************************************************** +!> \brief Initialize timers +!> +!> Prepares the DBCSR library for use. +! ************************************************************************************************** + subroutine dbcsr_init_timer() NULLIFY (logger) CALL dbcsr_logger_create(logger, mp_env=mp_env, & default_global_unit_nr=ext_io_unit, & @@ -113,14 +116,12 @@ subroutine dbcsr_init_timer(mp_comm, io_unit) CALL dbcsr_add_default_logger(logger) CALL dbcsr_logger_release(logger) CALL dbcsr_error_handling_setup() + call timings_register_hooks() + CALL add_mp_perf_env() + CALL add_timer_env() end subroutine -! final part of timer initialization - subroutine add_envs() - CALL add_mp_perf_env() - CALL add_timer_env() - end subroutine ! ************************************************************************************************** !> \brief Kill timers !> @@ -152,11 +153,8 @@ subroutine dbcsr_print_timer() SUBROUTINE dbcsr_init_lib_def(mp_comm, io_unit) INTEGER, INTENT(IN) :: mp_comm integer, intent(in), optional :: io_unit - - call dbcsr_init_timer(mp_comm, io_unit) - call timings_register_hooks() - call add_envs() - + call base_init(mp_comm, io_unit) + call dbcsr_init_timer() call dbcsr_init_lib_only() end subroutine @@ -175,10 +173,9 @@ SUBROUTINE dbcsr_init_lib_ext(mp_comm, io_unit, & PROCEDURE(dbcsr_abort_interface), INTENT(IN), POINTER :: in_abort_hook PROCEDURE(dbcsr_warn_interface), INTENT(IN), POINTER :: in_warn_hook - call dbcsr_init_timer(mp_comm, io_unit) + call base_init(mp_comm, io_unit) call timings_register_hooks(in_timeset_hook, in_timestop_hook,& in_abort_hook, in_warn_hook) - call add_envs() call dbcsr_init_lib_only() end subroutine diff --git a/src/mpi/dbcsr_mp_methods.F b/src/mpi/dbcsr_mp_methods.F index 4f74c066f9d..82aa117bb09 100644 --- a/src/mpi/dbcsr_mp_methods.F +++ b/src/mpi/dbcsr_mp_methods.F @@ -145,14 +145,10 @@ SUBROUTINE dbcsr_mp_grid_setup(mp_env) CALL mp_cart_create(mp_env%mp%mp_group, ndims, & dims, my_pos, & tmp_group) - IF (my_pos(1) .NE. mp_env%mp%myprow) then - error stop 1 + IF (my_pos(1) .NE. mp_env%mp%myprow) & DBCSR_ABORT("Got different MPI process grid") - endif - IF (my_pos(2) .NE. mp_env%mp%mypcol) then - error stop 1 + IF (my_pos(2) .NE. mp_env%mp%mypcol) & DBCSR_ABORT("Got different MPI process grid") - endif ! remain = (/.FALSE., .TRUE./) CALL mp_cart_sub(tmp_group, remain, mp_env%mp%prow_group) From a4e02ccc90ba80a1d91455338e29b5cbc34ace8b Mon Sep 17 00:00:00 2001 From: alazzaro Date: Mon, 29 Apr 2019 09:22:09 +0200 Subject: [PATCH 16/88] Remove unsed variables --- examples/dbcsr_example_1.F | 4 ++-- examples/dbcsr_example_2.F | 4 ++-- examples/dbcsr_example_3.F | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/dbcsr_example_1.F b/examples/dbcsr_example_1.F index 16bc625297b..7f9882d0400 100644 --- a/examples/dbcsr_example_1.F +++ b/examples/dbcsr_example_1.F @@ -33,7 +33,7 @@ PROGRAM dbcsr_example_1 INTEGER, DIMENSION(:), POINTER :: col_blk_sizes, row_blk_sizes INTEGER :: group, numnodes, mynode, nblkrows_total, & nblkcols_total, ierr - INTEGER, DIMENSION(2) :: npdims, myploc + INTEGER, DIMENSION(2) :: npdims INTEGER, DIMENSION(:), POINTER :: col_dist, row_dist TYPE(dbcsr_distribution_type) :: dist LOGICAL, DIMENSION(2) :: period = .TRUE. @@ -53,7 +53,7 @@ PROGRAM dbcsr_example_1 !$ STOP "MPI library does not support the requested level of threading (MPI_THREAD_FUNNELED)." !$ ENDIF !$ ENDIF - + ! ! setup the mp environment CALL mpi_comm_size(MPI_COMM_WORLD, numnodes, ierr) diff --git a/examples/dbcsr_example_2.F b/examples/dbcsr_example_2.F index 0fe80797731..321f5be281c 100644 --- a/examples/dbcsr_example_2.F +++ b/examples/dbcsr_example_2.F @@ -34,8 +34,8 @@ PROGRAM dbcsr_example_2 INTEGER, DIMENSION(:), POINTER :: col_blk_sizes, row_blk_sizes INTEGER :: group, numnodes, mynode, ierr, & nblkrows_total, nblkcols_total, node_holds_blk, max_nze, nze, & - row, col, row_s, col_s, io_unit, max_row_size, max_col_size - INTEGER, DIMENSION(2) :: npdims, myploc + row, col, row_s, col_s, max_row_size, max_col_size + INTEGER, DIMENSION(2) :: npdims INTEGER, DIMENSION(:), POINTER :: col_dist, row_dist TYPE(dbcsr_distribution_type) :: dist REAL(KIND=KIND(0.0D0)), DIMENSION(:), ALLOCATABLE :: values diff --git a/examples/dbcsr_example_3.F b/examples/dbcsr_example_3.F index 3cc955487a6..aebf476214f 100644 --- a/examples/dbcsr_example_3.F +++ b/examples/dbcsr_example_3.F @@ -35,8 +35,8 @@ PROGRAM dbcsr_example_3 INTEGER :: group, numnodes, mynode, ierr, & nblkrows_total, nblkcols_total, & node_holds_blk, max_nze, nze, row, col, row_s, col_s, & - io_unit, max_row_size, max_col_size - INTEGER, DIMENSION(2) :: npdims, myploc + max_row_size, max_col_size + INTEGER, DIMENSION(2) :: npdims INTEGER, DIMENSION(:), POINTER :: col_dist, row_dist TYPE(dbcsr_distribution_type) :: dist REAL(KIND=KIND(0.0D0)), DIMENSION(:), ALLOCATABLE :: values From 688c0c1e17212a06f7feef5eeedad163f74cbb02 Mon Sep 17 00:00:00 2001 From: alazzaro Date: Mon, 29 Apr 2019 10:14:51 +0200 Subject: [PATCH 17/88] Clean up and pretty --- tests/dbcsr_performance_driver.F | 29 +++++------------------------ tests/dbcsr_tas_unittest.F | 5 +---- tests/dbcsr_test_csr_conversions.F | 4 ++-- tests/dbcsr_unittest1.F | 7 +++---- tests/dbcsr_unittest2.F | 11 ++++------- tests/dbcsr_unittest3.F | 8 +++----- 6 files changed, 18 insertions(+), 46 deletions(-) diff --git a/tests/dbcsr_performance_driver.F b/tests/dbcsr_performance_driver.F index 1a7bc8074cc..daa0265a6b6 100644 --- a/tests/dbcsr_performance_driver.F +++ b/tests/dbcsr_performance_driver.F @@ -18,32 +18,19 @@ ! ************************************************************************************************** PROGRAM dbcsr_performance_driver USE dbcsr_config, ONLY: dbcsr_print_config - USE dbcsr_error_handling, ONLY: dbcsr_error_handling_setup USE dbcsr_files, ONLY: open_file - USE dbcsr_kinds, ONLY: default_string_length, & - dp + USE dbcsr_kinds, ONLY: default_string_length USE dbcsr_lib, ONLY: dbcsr_finalize_lib, & dbcsr_init_lib - USE dbcsr_log_handling, ONLY: dbcsr_add_default_logger, & - dbcsr_logger_create, & - dbcsr_logger_release, & - dbcsr_logger_type, & - dbcsr_rm_default_logger USE dbcsr_machine, ONLY: default_output_unit, & m_getarg, & m_iargc USE dbcsr_mp_methods, ONLY: dbcsr_mp_new, & dbcsr_mp_release USE dbcsr_mpiwrap, ONLY: & - add_mp_perf_env, mp_bcast, mp_cart_create, mp_cart_rank, mp_comm_free, mp_environ, & - mp_world_finalize, mp_world_init, rm_mp_perf_env + mp_bcast, mp_cart_create, mp_cart_rank, mp_comm_free, mp_environ, & + mp_world_finalize, mp_world_init USE dbcsr_performance_multiply, ONLY: dbcsr_perf_multiply - USE dbcsr_timings, ONLY: add_timer_env, & - rm_timer_env, & - timings_register_hooks - USE dbcsr_timings_report, ONLY: cost_type_time, & - timings_report_callgraph, & - timings_report_print USE dbcsr_toollib, ONLY: atoi USE dbcsr_types, ONLY: dbcsr_mp_obj #include "base/dbcsr_base_uses.f90" @@ -61,7 +48,6 @@ PROGRAM dbcsr_performance_driver CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_check_multiply' - !*************************************************************************************** ! @@ -110,7 +96,7 @@ PROGRAM dbcsr_performance_driver ! ! initialize libdbcsr CALL dbcsr_init_lib(mp_comm,io_unit) - + ! initialize libdbcsr errors CALL timeset(routineN, handle) @@ -127,19 +113,14 @@ PROGRAM dbcsr_performance_driver DBCSR_ABORT("operation not found") END SELECT - ! ! finalize libdbcsr errors CALL timestop(handle) - + ! ! clean mp environment CALL dbcsr_mp_release(mp_env) - ! - ! Remove logger - !CALL dbcsr_rm_default_logger() - ! ! free comm CALL mp_comm_free(group) diff --git a/tests/dbcsr_tas_unittest.F b/tests/dbcsr_tas_unittest.F index e1c58c27c13..ab24aee9e79 100644 --- a/tests/dbcsr_tas_unittest.F +++ b/tests/dbcsr_tas_unittest.F @@ -51,7 +51,7 @@ PROGRAM dbcsr_tas_unittest io_unit = 0 IF (mynode .EQ. 0) io_unit = default_output_unit - + CALL dbcsr_init_lib(mp_comm, io_unit) CALL dbcsr_tas_random_bsizes([13, 8, 5, 25, 12], 2, bsize_m) @@ -60,9 +60,6 @@ PROGRAM dbcsr_tas_unittest CALL mp_environ(numnodes, mynode, mp_comm) - io_unit = 0 - IF (mynode .EQ. 0) io_unit = default_output_unit - CALL dbcsr_tas_setup_test_matrix(A, mp_comm_A, mp_comm, m, k, bsize_m, bsize_k, [5, 1], "A", sparsity) CALL dbcsr_tas_setup_test_matrix(At, mp_comm_At, mp_comm, k, m, bsize_k, bsize_m, [3, 8], "A^t", sparsity) CALL dbcsr_tas_setup_test_matrix(B, mp_comm_B, mp_comm, n, m, bsize_n, bsize_m, [3, 2], "B", sparsity) diff --git a/tests/dbcsr_test_csr_conversions.F b/tests/dbcsr_test_csr_conversions.F index f2c708fc3bb..35fafe7f8fe 100644 --- a/tests/dbcsr_test_csr_conversions.F +++ b/tests/dbcsr_test_csr_conversions.F @@ -58,12 +58,12 @@ PROGRAM dbcsr_test_csr_conversions ! Set up everything as in the dbcsr example codes CALL mp_world_init(mp_comm) - + CALL mp_environ(numnodes, mynode, mp_comm) io_unit = 0 IF (mynode .EQ. 0) io_unit = default_output_unit - + CALL dbcsr_init_lib(mp_comm, io_unit) npdims(:) = 0 diff --git a/tests/dbcsr_unittest1.F b/tests/dbcsr_unittest1.F index 0e95048dc42..ada8d9bf975 100644 --- a/tests/dbcsr_unittest1.F +++ b/tests/dbcsr_unittest1.F @@ -81,12 +81,12 @@ PROGRAM dbcsr_unittest ! initialize libdbcsr IF (acc_get_ndevices() > 0) & CALL acc_set_active_device(MOD(mynode, acc_get_ndevices())) - + CALL dbcsr_init_lib(mp_comm, io_unit) ! initialize libdbcsr errors CALL timeset(routineN, handle) - + CALL dbcsr_reset_randmat_seed() ! run tests @@ -316,7 +316,6 @@ PROGRAM dbcsr_unittest ! end of test cases --------------------------------------------------------- - CALL timestop(handle) ! ! clean mp environment @@ -325,7 +324,7 @@ PROGRAM dbcsr_unittest ! ! finalize mpi CALL mp_comm_free(group) - + ! finalize libdbcsr CALL dbcsr_finalize_lib() CALL mp_world_finalize() diff --git a/tests/dbcsr_unittest2.F b/tests/dbcsr_unittest2.F index 603a909df36..b6a56fd47c6 100644 --- a/tests/dbcsr_unittest2.F +++ b/tests/dbcsr_unittest2.F @@ -52,7 +52,6 @@ PROGRAM dbcsr_unittest !*************************************************************************************** - ! ! initialize mpi CALL mp_world_init(mp_comm) @@ -85,7 +84,7 @@ PROGRAM dbcsr_unittest ! ! initialize libdbcsr errors CALL timeset(routineN, handle) - + CALL dbcsr_reset_randmat_seed() ! run tests @@ -124,11 +123,10 @@ PROGRAM dbcsr_unittest ! end of test cases --------------------------------------------------------- - ! ! finalize libdbcsr errors CALL timestop(handle) - + ! ! clean mp environment CALL dbcsr_mp_release(mp_env) @@ -136,11 +134,10 @@ PROGRAM dbcsr_unittest ! ! finalize mpi CALL mp_comm_free(group) - + ! finalize libdbcsr CALL dbcsr_finalize_lib() - - CALL mp_world_finalize() + CALL mp_world_finalize() END PROGRAM dbcsr_unittest diff --git a/tests/dbcsr_unittest3.F b/tests/dbcsr_unittest3.F index 2b9418a05e5..64383d00993 100644 --- a/tests/dbcsr_unittest3.F +++ b/tests/dbcsr_unittest3.F @@ -53,7 +53,6 @@ PROGRAM dbcsr_unittest !*************************************************************************************** - ! ! initialize mpi CALL mp_world_init(mp_comm) @@ -80,7 +79,7 @@ PROGRAM dbcsr_unittest ! initialize libdbcsr IF (acc_get_ndevices() > 0) & CALL acc_set_active_device(MOD(mynode, acc_get_ndevices())) - + CALL dbcsr_init_lib(mp_comm, io_unit) ! ! initialize libdbcsr errors @@ -137,11 +136,10 @@ PROGRAM dbcsr_unittest ! ! finalize mpi CALL mp_comm_free(group) - + ! finalize libdbcsr CALL dbcsr_finalize_lib() - - CALL mp_world_finalize() + CALL mp_world_finalize() END PROGRAM dbcsr_unittest From c4beefec480e57ee842d706e62ce9f7966cc0bdc Mon Sep 17 00:00:00 2001 From: shoshijak Date: Mon, 15 Apr 2019 19:02:01 +0200 Subject: [PATCH 18/88] libcusmm: fix unused variable --- .../libsmm_acc/libcusmm/libcusmm_benchmark.cu | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/acc/libsmm_acc/libcusmm/libcusmm_benchmark.cu b/src/acc/libsmm_acc/libcusmm/libcusmm_benchmark.cu index 7f66b51266d..cff7ab13172 100644 --- a/src/acc/libsmm_acc/libcusmm/libcusmm_benchmark.cu +++ b/src/acc/libsmm_acc/libcusmm/libcusmm_benchmark.cu @@ -25,23 +25,23 @@ void libcusmm_benchmark_init(libcusmm_benchmark_t** handle, benchmark_mode mode, h->mode = mode; switch(mode) { - case tune: - case timing: + case tune: + case timing: h->n_a = 10000; h->n_b = 10000; h->n_c = 1000; h->n_stack = 16005; h->n_stack_trs_a = 0; h->n_stack_trs_b = 0; - break; - case test: + break; + case test: h->n_a = 100; h->n_b = 100; h->n_c = 10; h->n_stack = 100; h->n_stack_trs_a = h->n_a; h->n_stack_trs_b = h->n_b; - break; + break; } h->max_m = max_m; @@ -214,18 +214,18 @@ double checkSum(double* mat_c, int n_c, int mat_m, int mat_n){ //=========================================================================== double checkSumTransp(double* mat, int n, int n_stack, int mat_m, int mat_n){ - // for transposition, a regular checkSum does not inform about the - // transpose's correctness. Instead, we perform a checkSum on a + // for transposition, a regular checkSum does not inform about the + // transpose's correctness. Instead, we perform a checkSum on a // sample of elements. double res = 0; int size = mat_m * mat_n; int n_samples = size / 3; - int step = size / n_samples; + int step = size / n_samples; for(int s=0; s < n_stack; s++){ - int offset = s * size; + int offset = s * size; for(int idx=s%step; idx < size; idx+=step) - res += mat[offset + idx]; - } + res += mat[offset + idx]; + } return res; } @@ -255,7 +255,7 @@ int libcusmm_benchmark(libcusmm_benchmark_t* h, exit(1); } std::vector blocksizes; - get_libcusmm_triplets(blocksizes, ht); + get_libcusmm_triplets(blocksizes, ht); auto it = std::find(std::begin(blocksizes), std::end(blocksizes), Triplet({ mat_m, mat_n, mat_k })); if(it == std::end(blocksizes) && h->mode != tune){ printf("Triplet %i x %i x %i is not defined in libcusmm\n", mat_m, mat_n, mat_k); @@ -263,7 +263,7 @@ int libcusmm_benchmark(libcusmm_benchmark_t* h, } - int n_iter, n_warm; + int n_iter, n_warm; switch(h->mode) { case tune: case timing: // for larger matrices few iteration give enough statistics @@ -285,7 +285,6 @@ int libcusmm_benchmark(libcusmm_benchmark_t* h, double sumCPU, sumGPU; float t_duration; char descr[1000], msg_prefix[100]=""; - cudaError_t cudaError; memset(h->mat_c, 0, h->n_c * mat_m * mat_n * sizeof(double)); matInit(h->mat_a, h->n_a, mat_m, mat_k, 42); @@ -366,8 +365,8 @@ int libcusmm_benchmark(libcusmm_benchmark_t* h, //=========================================================================== int libcusmm_benchmark_transpose_(int n_stack, int* stack, int* d_stack, - double* mat, double* mat_trs, double* d_mat, - int n, int mat_m, int mat_n, + double* mat, double* mat_trs, double* d_mat, + int n, int mat_m, int mat_n, CUevent start, CUevent stop, char** kernel_descr, TransposeLauncher* launcher){ if(mat_m > MAX_BLOCK_DIM || mat_n > MAX_BLOCK_DIM){ From bfaf74a0e4bb3efe47c04040bff1c7017f51bc2a Mon Sep 17 00:00:00 2001 From: shoshijak Date: Wed, 17 Apr 2019 10:33:08 +0200 Subject: [PATCH 19/88] libcusmm/tune: refer to jupyter notebook in autotuning README --- src/acc/libsmm_acc/libcusmm/tune.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/acc/libsmm_acc/libcusmm/tune.md b/src/acc/libsmm_acc/libcusmm/tune.md index 38a569fe736..706c39690a8 100644 --- a/src/acc/libsmm_acc/libcusmm/tune.md +++ b/src/acc/libsmm_acc/libcusmm/tune.md @@ -1,6 +1,6 @@ -# Autotuning Procedure for Finding Optimal Cuda Kernel Parameters in `libcusmm` +# Autotuning Procedure for Finding Optimal CUDA Kernel Parameters in `libcusmm` -The performance of the matrix-matrix multiplication kernels is highly dependant on the choice of algorithm and parameters, this is why autotuning is used to find optimal kernel parameters. +The performance of the matrix-matrix multiplication kernels is highly dependent on the choice of algorithm and parameters, this is why autotuning is used to find optimal kernel parameters. --- @@ -14,13 +14,13 @@ If you are about to autotune parameters for a new GPU (i.e. a GPU for which ther ### Autotuning procedure -#### 1. Go to the libcusmm directory +#### 1. Go to the `libcusmm` directory ```bash $ cd dbcsr/src/acc/libsmm_acc/libcusmm ``` -#### 2. Adapt tune_setup.py to your environment +#### 2. Adapt `tune_setup.py` to your environment The `tune_setup.py` script generates job files. You have to adapt the script to the environment of your supercomputer and your personal settings. @@ -59,7 +59,7 @@ The `tune_setup.py` script generates job files. You have to adapt the script to #### 3. Run the script `tune_setup.py` -Specify which GPU you are autotuning for by passing the appropriate `parameters_GPU.json` file as an argument with `-p`. In addition, the script takes as arguments the blocksizes you want to add to libcusmm. For example, if the system you want to autotune for contains blocks of size 5 and 8, run: +Specify which GPU you are autotuning for by passing the appropriate `parameters_GPU.json` file as an argument with `-p`. In addition, the script takes as arguments the block sizes you want to add to `libcusmm`. For example, if the system you want to autotune for contains blocks of size 5 and 8, run: ```bash $ ./tune_setup.py 5 8 -p parameters_P100.json @@ -73,7 +73,7 @@ Found 248 parameter sets for 8x8x5 Found 424 parameter sets for 8x8x8 ``` -The script will create a directory for each combination of the blocksizes: +The script will create a directory for each combination of the block sizes: ```bash $ ls -d tune_* @@ -94,7 +94,7 @@ tune_8x8x8_exe0_part4.cu tune_8x8x8.job ``` -For each possible parameter-set a *launcher* is generated. A launcher is a small snippet of C code, which launches the kernel by using the cuda specific `<<< >>>`-notation. It also instantiates the C++ template which contains the actual kernel code. +For each possible parameter-set a *launcher* is generated. A launcher is a small snippet of C code, which launches the kernel by using the CUDA specific `<<< >>>`-notation. It also instantiates the C++ template which contains the actual kernel code. In order to parallelize the benchmarking, the launchers are distributed over multiple executables. Currently, up to 10'000 launchers are benchmarked by one *executable*. Each executable is linked together from several `tune_*_part???.o` and a `tune_*_main.o`. Each part-files contains up to 100 launchers. This allows to parallelize the compilation over multiple CPU cores. @@ -184,7 +184,11 @@ Wrote parameters.new.json The file `parameters.new.json` can now be used as a parameter file. Rename it to `parameters_GPU.json`, with the appropriate `GPU`. -#### 8. Contribute parameters to the community +#### 8. (optional) Explore the data + +Explore the data interactively using the [provided Jupyter Notebook](notebooks/inspect_training_data.ipynb). + +#### 9. Contribute parameters to the community **Contribute new optimal parameters** From fd5d1cdee4a435f4433e6f9ada0735bd862eaa89 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Fri, 8 Feb 2019 13:07:12 +0100 Subject: [PATCH 20/88] libcusmm: add V100 GPU properties And add "V100" to lists of options of GPU cards --- CMakeLists.txt | 3 ++- Makefile | 4 ++- Makefile.inc | 2 +- README.md | 2 +- .../libcusmm/kernels/cusmm_predict.py | 1 + .../libcusmm/kernels/gpu_properties.json | 26 +++++++++++++++++-- .../inspect_autotuned_parameters.ipynb | 2 +- .../libsmm_acc/libcusmm/predict_collect.py | 2 +- .../libsmm_acc/libcusmm/predict_derivepars.py | 2 +- 9 files changed, 35 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 00d1103e93e..d5b054c0a91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ set_property(CACHE USE_SMM PROPERTY STRINGS blas libxsmm) option(USE_CUDA "Build with CUDA support" OFF) cmake_dependent_option(USE_CUBLAS "Build with CUBLAS support" OFF "USE_CUDA" OFF) set(WITH_GPU "P100" CACHE STRING "Set the CUDA GPU architecture if CUDA is enabled (default: P100)") -set_property(CACHE WITH_GPU PROPERTY STRINGS K20X K40 K80 P100) +set_property(CACHE WITH_GPU PROPERTY STRINGS K20X K40 K80 P100 V100) enable_language(Fortran) enable_testing() # enables the `make test` target @@ -37,6 +37,7 @@ if (USE_CUDA) set(CUDA_ARCH_NUMBER_K40 35) set(CUDA_ARCH_NUMBER_K80 37) set(CUDA_ARCH_NUMBER_P100 60) + set(CUDA_ARCH_NUMBER_V100 70) set(CUDA_ARCH_NUMBER ${CUDA_ARCH_NUMBER_${WITH_GPU}}) # assume that the backend compiler for nvcc understands the -std=c++11 diff --git a/Makefile b/Makefile index 943a445c8e4..44e6ef6110f 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,9 @@ else ifeq ($(GPUVER),K80) ARCH_NUMBER = 37 else ifeq ($(GPUVER),P100) ARCH_NUMBER = 60 -else ifeq ($(GPUVER),) # Default to the newest GPU +else ifeq ($(GPUVER),V100) + ARCH_NUMBER = 70 +else ifeq ($(GPUVER),) # Default to the P100 ARCH_NUMBER = 60 else $(error GPUVER not recognized) diff --git a/Makefile.inc b/Makefile.inc index 498ee6c5965..8c6bd2ebff6 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -17,7 +17,7 @@ # a) set the NVCC variable, e.g. NVCC = nvcc # b) specify -D__DBCSR_ACC in FCFLAGS variable # c) set the GPUVER variable, e.g. GPUVER = P100 for P100 card -# (possible values are K20X, K40, K80, P100) +# (possible values are K20X, K40, K80, P100, V100) # d) set the NVFLAGS variable, # e.g. NVFLAGS = -O3 -g -w --std=c++11 # in the Makefile, the -arch will be appended with the correct compute version diff --git a/README.md b/README.md index d6f5d9b03a5..25fbc27d024 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ The configuration flags are (default first): -DUSE_CUDA= -DWITH_C_API= -DWITH_EXAMPLES= - -DWITH_GPU= + -DWITH_GPU= -DTEST_MPI_RANKS= ## Contributing to DBCSR diff --git a/src/acc/libsmm_acc/libcusmm/kernels/cusmm_predict.py b/src/acc/libsmm_acc/libcusmm/kernels/cusmm_predict.py index 4cc1f413f16..6c0be2136e6 100644 --- a/src/acc/libsmm_acc/libcusmm/kernels/cusmm_predict.py +++ b/src/acc/libsmm_acc/libcusmm/kernels/cusmm_predict.py @@ -37,6 +37,7 @@ "parameters_K40.json": 35, "parameters_K80.json": 37, "parameters_P100.json": 60, + "parameters_V100.json": 70, } diff --git a/src/acc/libsmm_acc/libcusmm/kernels/gpu_properties.json b/src/acc/libsmm_acc/libcusmm/kernels/gpu_properties.json index a61546f1a40..172ad237f52 100644 --- a/src/acc/libsmm_acc/libcusmm/kernels/gpu_properties.json +++ b/src/acc/libsmm_acc/libcusmm/kernels/gpu_properties.json @@ -4,7 +4,9 @@ "source": "CUDA occupancy calculator", "source_url": [ "http://developer.download.nvidia.com/compute/cuda/CUDA_Occupancy_calculator.xls", - "https://devblogs.nvidia.com/inside-pascal/" + "https://devblogs.nvidia.com/inside-pascal/", + "https://www.nvidia.com/content/dam/en-zz/Solutions/Data-Center/tesla-product-literature/volta-architecture-whitepaper.pdf", + "https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#features-and-technical-specifications" ] }, "sm_35": { @@ -62,10 +64,30 @@ "Register_Allocation_Granularity": "warp", "Max_Registers_/_Thread": 255, "Shared_Memory_Allocation_Unit_Size": 256, - "Warp_Allocation_Granularity": 4, + "Warp_Allocation_Granularity": 2, "Max_Thread_Block_Size": 1024, "Shared_Memory_Size_Configurations_(bytes)": 65536, "Warp_register_allocation_granularities": 256 + }, + "sm_70": { + "Compute_Capability": 7.0, + "SM_Version": "sm_70", + "Threads_/_Warp" : 32, + "Warps_/_Multiprocessor": 64, + "Threads_/_Multiprocessor": 2048, + "Thread_Blocks_/_Multiprocessor": 32, + "Shared_Memory_/_Multiprocessor_(bytes)": 114688, + "Max_Shared_Memory_/_Block_(bytes)": 49152, + "Register_File_Size_/_Multiprocessor_(32-bit_registers)": 65536, + "Max_Registers_/_Block": 65536, + "Register_Allocation_Unit_Size": 256, + "Register_Allocation_Granularity": "warp", + "Max_Registers_/_Thread": 255, + "Shared_Memory_Allocation_Unit_Size": 256, + "Warp_Allocation_Granularity": 4, + "Max_Thread_Block_Size": 1024, + "Shared_Memory_Size_Configurations_(bytes)": 114688, + "Warp_register_allocation_granularities": 256 } } diff --git a/src/acc/libsmm_acc/libcusmm/notebooks/inspect_autotuned_parameters.ipynb b/src/acc/libsmm_acc/libcusmm/notebooks/inspect_autotuned_parameters.ipynb index eeecb385b1e..ede527e185a 100644 --- a/src/acc/libsmm_acc/libcusmm/notebooks/inspect_autotuned_parameters.ipynb +++ b/src/acc/libsmm_acc/libcusmm/notebooks/inspect_autotuned_parameters.ipynb @@ -54,7 +54,7 @@ "metadata": {}, "outputs": [], "source": [ - "GPU = 'P100' # Options: K20X, K40, K80, P100" + "GPU = 'P100' # Options: K20X, K40, K80, P100, V100" ] }, { diff --git a/src/acc/libsmm_acc/libcusmm/predict_collect.py b/src/acc/libsmm_acc/libcusmm/predict_collect.py index 774a6011045..af954a2d946 100755 --- a/src/acc/libsmm_acc/libcusmm/predict_collect.py +++ b/src/acc/libsmm_acc/libcusmm/predict_collect.py @@ -403,7 +403,7 @@ def print_merging_commands(kernel_folders, kernel_folder_pattern, tunedir): metavar="ARCHITECTURE_NUMBER", type=int, default=60, - help="CUDA architecture number. Options: 35, 37, 60", + help="CUDA architecture number. Options: 35, 37, 60, 70", ) args = parser.parse_args() diff --git a/src/acc/libsmm_acc/libcusmm/predict_derivepars.py b/src/acc/libsmm_acc/libcusmm/predict_derivepars.py index 20010131330..2a34be2c9fe 100755 --- a/src/acc/libsmm_acc/libcusmm/predict_derivepars.py +++ b/src/acc/libsmm_acc/libcusmm/predict_derivepars.py @@ -185,7 +185,7 @@ def main(tunedir, arch): metavar="ARCHITECTURE_NUMBER", type=int, default="60", - help="CUDA architecture number. Options: 35, 37, 60", + help="CUDA architecture number. Options: 35, 37, 60, 70", ) args = parser.parse_args() From 3c9908a4fa9105aedab8d3c7452490cfd7e9e5a3 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Tue, 5 Mar 2019 10:09:56 +0100 Subject: [PATCH 21/88] libcusmm: add libcusmm kernel parameters for the V100 --- .../libsmm_acc/libcusmm/parameters_V100.json | 1237 +++++++++++++++++ 1 file changed, 1237 insertions(+) create mode 100644 src/acc/libsmm_acc/libcusmm/parameters_V100.json diff --git a/src/acc/libsmm_acc/libcusmm/parameters_V100.json b/src/acc/libsmm_acc/libcusmm/parameters_V100.json new file mode 100644 index 00000000000..c4c5689ad5c --- /dev/null +++ b/src/acc/libsmm_acc/libcusmm/parameters_V100.json @@ -0,0 +1,1237 @@ +[ +{"m": 4, "n": 4, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 1, "algorithm": "medium", "perf": 246.008, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 3, "algorithm": "medium", "perf": 295.995, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 6, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 7, "minblocks": 14, "algorithm": "medium", "perf": 346.775, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 7, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 7, "minblocks": 13, "algorithm": "medium", "perf": 392.944, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 8, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 7, "minblocks": 16, "algorithm": "medium", "perf": 405.137, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 9, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 2, "algorithm": "medium", "perf": 385.195, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 10, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 22, "algorithm": "medium", "perf": 404.271, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 13, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 438.293, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 15, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 471.731, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 22, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 24, "algorithm": "medium", "perf": 479.602, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 23, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 25, "algorithm": "medium", "perf": 469.625, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 26, "algorithm": "medium", "perf": 468.707, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 472.517, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 28, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 12, "algorithm": "medium", "perf": 473.832, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 32, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 16, "algorithm": "medium", "perf": 469.584, "source": "autotuned"}, +{"m": 4, "n": 4, "k": 45, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 440.299, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 1, "algorithm": "medium", "perf": 308.153, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 14, "algorithm": "medium", "perf": 364.843, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 6, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 7, "minblocks": 15, "algorithm": "medium", "perf": 421.298, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 7, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 16, "algorithm": "medium", "perf": 386.405, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 8, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 4, "algorithm": "medium", "perf": 406.801, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 9, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 7, "algorithm": "medium", "perf": 422.926, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 26, "algorithm": "medium", "perf": 462.967, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 22, "tile_m": 1, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 18, "algorithm": "medium", "perf": 492.481, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 23, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 5, "minblocks": 16, "algorithm": "medium", "perf": 490.113, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 486.609, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 481.038, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 28, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 489.975, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 32, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 14, "algorithm": "medium", "perf": 496.239, "source": "autotuned"}, +{"m": 4, "n": 5, "k": 45, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 1, "minblocks": 6, "algorithm": "medium", "perf": 468.896, "source": "autotuned"}, +{"m": 4, "n": 6, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 8, "algorithm": "medium", "perf": 368.735, "source": "autotuned"}, +{"m": 4, "n": 6, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 13, "algorithm": "medium", "perf": 435.89, "source": "autotuned"}, +{"m": 4, "n": 6, "k": 6, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 6, "algorithm": "medium", "perf": 407.925, "source": "autotuned"}, +{"m": 4, "n": 6, "k": 7, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 22, "algorithm": "medium", "perf": 462.645, "source": "autotuned"}, +{"m": 4, "n": 6, "k": 8, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 9, "algorithm": "medium", "perf": 479.855, "source": "autotuned"}, +{"m": 4, "n": 6, "k": 9, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 485.334, "source": "autotuned"}, +{"m": 4, "n": 7, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 3, "algorithm": "medium", "perf": 428.942, "source": "autotuned"}, +{"m": 4, "n": 7, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 13, "algorithm": "medium", "perf": 412.602, "source": "autotuned"}, +{"m": 4, "n": 7, "k": 6, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 1, "algorithm": "medium", "perf": 475.905, "source": "autotuned"}, +{"m": 4, "n": 7, "k": 7, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 1, "algorithm": "medium", "perf": 516.037, "source": "autotuned"}, +{"m": 4, "n": 7, "k": 8, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 11, "algorithm": "medium", "perf": 525.403, "source": "autotuned"}, +{"m": 4, "n": 7, "k": 13, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 25, "algorithm": "medium", "perf": 581.761, "source": "autotuned"}, +{"m": 4, "n": 7, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 9, "algorithm": "medium", "perf": 549.47, "source": "autotuned"}, +{"m": 4, "n": 7, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 19, "algorithm": "medium", "perf": 548.753, "source": "autotuned"}, +{"m": 4, "n": 7, "k": 28, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 555.309, "source": "autotuned"}, +{"m": 4, "n": 7, "k": 32, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 563.644, "source": "autotuned"}, +{"m": 4, "n": 7, "k": 45, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 529.874, "source": "autotuned"}, +{"m": 4, "n": 8, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 7, "minblocks": 1, "algorithm": "medium", "perf": 433.171, "source": "autotuned"}, +{"m": 4, "n": 8, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 16, "algorithm": "medium", "perf": 477.871, "source": "autotuned"}, +{"m": 4, "n": 8, "k": 6, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 23, "algorithm": "medium", "perf": 551.799, "source": "autotuned"}, +{"m": 4, "n": 8, "k": 7, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 569.968, "source": "autotuned"}, +{"m": 4, "n": 8, "k": 8, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 566.549, "source": "autotuned"}, +{"m": 4, "n": 8, "k": 9, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 560.233, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 4, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 5, "algorithm": "medium", "perf": 413.149, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 5, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 492.074, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 6, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 10, "algorithm": "medium", "perf": 541.308, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 7, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 24, "algorithm": "medium", "perf": 581.319, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 8, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 26, "algorithm": "medium", "perf": 561.045, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 9, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 27, "algorithm": "medium", "perf": 583.137, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 26, "algorithm": "medium", "perf": 621.518, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 22, "tile_m": 2, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 15, "algorithm": "medium", "perf": 586.676, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 23, "tile_m": 2, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 14, "algorithm": "medium", "perf": 580.338, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 14, "algorithm": "medium", "perf": 578.701, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 26, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 579.831, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 28, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 589.888, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 32, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 13, "algorithm": "medium", "perf": 597.201, "source": "autotuned"}, +{"m": 4, "n": 9, "k": 45, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 15, "algorithm": "medium", "perf": 571.751, "source": "autotuned"}, +{"m": 4, "n": 10, "k": 4, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 12, "algorithm": "medium", "perf": 461.137, "source": "autotuned"}, +{"m": 4, "n": 10, "k": 10, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 27, "algorithm": "medium", "perf": 642.821, "source": "autotuned"}, +{"m": 4, "n": 10, "k": 15, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 24, "algorithm": "medium", "perf": 623.118, "source": "autotuned"}, +{"m": 4, "n": 13, "k": 4, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 2, "algorithm": "medium", "perf": 580.161, "source": "autotuned"}, +{"m": 4, "n": 13, "k": 5, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 16, "algorithm": "medium", "perf": 622.204, "source": "autotuned"}, +{"m": 4, "n": 13, "k": 7, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 7, "minblocks": 9, "algorithm": "medium", "perf": 681.111, "source": "autotuned"}, +{"m": 4, "n": 13, "k": 9, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 25, "algorithm": "medium", "perf": 671.288, "source": "autotuned"}, +{"m": 4, "n": 13, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 23, "algorithm": "medium", "perf": 658.006, "source": "autotuned"}, +{"m": 4, "n": 13, "k": 22, "tile_m": 2, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 14, "algorithm": "medium", "perf": 645.508, "source": "autotuned"}, +{"m": 4, "n": 13, "k": 23, "tile_m": 2, "tile_n": 1, "threads": 96, "grouping": 2, "minblocks": 14, "algorithm": "medium", "perf": 637.149, "source": "autotuned"}, +{"m": 4, "n": 13, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 629.5, "source": "autotuned"}, +{"m": 4, "n": 13, "k": 26, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 637.134, "source": "autotuned"}, +{"m": 4, "n": 13, "k": 28, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 2, "minblocks": 11, "algorithm": "medium", "perf": 642.176, "source": "autotuned"}, +{"m": 4, "n": 13, "k": 32, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 16, "algorithm": "medium", "perf": 647.889, "source": "autotuned"}, +{"m": 4, "n": 13, "k": 45, "tile_m": 1, "tile_n": 1, "threads": 224, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 628.519, "source": "autotuned"}, +{"m": 4, "n": 15, "k": 4, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 12, "algorithm": "medium", "perf": 643.765, "source": "autotuned"}, +{"m": 4, "n": 15, "k": 10, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 21, "algorithm": "medium", "perf": 674.671, "source": "autotuned"}, +{"m": 4, "n": 15, "k": 15, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 17, "algorithm": "medium", "perf": 664.565, "source": "autotuned"}, +{"m": 4, "n": 22, "k": 4, "tile_m": 1, "tile_n": 3, "threads": 32, "grouping": 7, "minblocks": 9, "algorithm": "medium", "perf": 721.241, "source": "autotuned"}, +{"m": 4, "n": 22, "k": 5, "tile_m": 1, "tile_n": 3, "threads": 32, "grouping": 4, "minblocks": 4, "algorithm": "medium", "perf": 698.948, "source": "autotuned"}, +{"m": 4, "n": 22, "k": 9, "tile_m": 1, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 19, "algorithm": "medium", "perf": 675.48, "source": "autotuned"}, +{"m": 4, "n": 22, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 683.974, "source": "autotuned"}, +{"m": 4, "n": 22, "k": 22, "tile_m": 1, "tile_n": 3, "threads": 96, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 694.29, "source": "autotuned"}, +{"m": 4, "n": 22, "k": 23, "tile_m": 1, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 691.473, "source": "autotuned"}, +{"m": 4, "n": 22, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 693.046, "source": "autotuned"}, +{"m": 4, "n": 23, "k": 4, "tile_m": 1, "tile_n": 3, "threads": 32, "grouping": 7, "minblocks": 12, "algorithm": "medium", "perf": 704.138, "source": "autotuned"}, +{"m": 4, "n": 23, "k": 5, "tile_m": 1, "tile_n": 3, "threads": 32, "grouping": 4, "minblocks": 4, "algorithm": "medium", "perf": 693.297, "source": "autotuned"}, +{"m": 4, "n": 23, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 64, "grouping": 2, "minblocks": 20, "algorithm": "medium", "perf": 688.521, "source": "autotuned"}, +{"m": 4, "n": 23, "k": 22, "tile_m": 2, "tile_n": 1, "threads": 96, "grouping": 2, "minblocks": 13, "algorithm": "medium", "perf": 696.548, "source": "autotuned"}, +{"m": 4, "n": 23, "k": 23, "tile_m": 1, "tile_n": 3, "threads": 96, "grouping": 2, "minblocks": 11, "algorithm": "medium", "perf": 697.693, "source": "autotuned"}, +{"m": 4, "n": 23, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 693.63, "source": "autotuned"}, +{"m": 4, "n": 25, "k": 4, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 16, "algorithm": "medium", "perf": 689.717, "source": "autotuned"}, +{"m": 4, "n": 25, "k": 5, "tile_m": 2, "tile_n": 3, "threads": 32, "grouping": 9, "minblocks": 22, "algorithm": "medium", "perf": 679.56, "source": "autotuned"}, +{"m": 4, "n": 25, "k": 7, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 26, "algorithm": "medium", "perf": 673.897, "source": "autotuned"}, +{"m": 4, "n": 25, "k": 9, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 23, "algorithm": "medium", "perf": 673.553, "source": "autotuned"}, +{"m": 4, "n": 25, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 692.688, "source": "autotuned"}, +{"m": 4, "n": 25, "k": 25, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 2, "minblocks": 10, "algorithm": "medium", "perf": 699.011, "source": "autotuned"}, +{"m": 4, "n": 25, "k": 26, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 702.163, "source": "autotuned"}, +{"m": 4, "n": 25, "k": 28, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 703.031, "source": "autotuned"}, +{"m": 4, "n": 25, "k": 32, "tile_m": 2, "tile_n": 1, "threads": 160, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 714.509, "source": "autotuned"}, +{"m": 4, "n": 25, "k": 45, "tile_m": 2, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 709.255, "source": "autotuned"}, +{"m": 4, "n": 26, "k": 4, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 8, "algorithm": "medium", "perf": 713.203, "source": "autotuned"}, +{"m": 4, "n": 26, "k": 5, "tile_m": 2, "tile_n": 3, "threads": 32, "grouping": 3, "minblocks": 25, "algorithm": "medium", "perf": 682.143, "source": "autotuned"}, +{"m": 4, "n": 26, "k": 7, "tile_m": 2, "tile_n": 2, "threads": 32, "grouping": 4, "minblocks": 26, "algorithm": "medium", "perf": 682.312, "source": "autotuned"}, +{"m": 4, "n": 26, "k": 9, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 22, "algorithm": "medium", "perf": 679.23, "source": "autotuned"}, +{"m": 4, "n": 26, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 697.924, "source": "autotuned"}, +{"m": 4, "n": 26, "k": 22, "tile_m": 2, "tile_n": 1, "threads": 96, "grouping": 2, "minblocks": 10, "algorithm": "medium", "perf": 706.743, "source": "autotuned"}, +{"m": 4, "n": 26, "k": 23, "tile_m": 2, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 697.513, "source": "autotuned"}, +{"m": 4, "n": 26, "k": 25, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 700.508, "source": "autotuned"}, +{"m": 4, "n": 26, "k": 26, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 711.801, "source": "autotuned"}, +{"m": 4, "n": 26, "k": 28, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 713.949, "source": "autotuned"}, +{"m": 4, "n": 26, "k": 32, "tile_m": 2, "tile_n": 1, "threads": 160, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 722.448, "source": "autotuned"}, +{"m": 4, "n": 26, "k": 45, "tile_m": 2, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 714.8, "source": "autotuned"}, +{"m": 4, "n": 28, "k": 4, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 8, "algorithm": "medium", "perf": 713.43, "source": "autotuned"}, +{"m": 4, "n": 28, "k": 5, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 4, "minblocks": 26, "algorithm": "medium", "perf": 691.419, "source": "autotuned"}, +{"m": 4, "n": 28, "k": 7, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 688.417, "source": "autotuned"}, +{"m": 4, "n": 28, "k": 9, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 21, "algorithm": "medium", "perf": 696.35, "source": "autotuned"}, +{"m": 4, "n": 28, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 16, "algorithm": "medium", "perf": 693.509, "source": "autotuned"}, +{"m": 4, "n": 28, "k": 25, "tile_m": 4, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 705.866, "source": "autotuned"}, +{"m": 4, "n": 28, "k": 26, "tile_m": 4, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 718.173, "source": "autotuned"}, +{"m": 4, "n": 28, "k": 28, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 720.648, "source": "autotuned"}, +{"m": 4, "n": 28, "k": 32, "tile_m": 2, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 730.663, "source": "autotuned"}, +{"m": 4, "n": 28, "k": 45, "tile_m": 2, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 721.599, "source": "autotuned"}, +{"m": 4, "n": 32, "k": 4, "tile_m": 2, "tile_n": 2, "threads": 32, "grouping": 8, "minblocks": 17, "algorithm": "medium", "perf": 713.191, "source": "autotuned"}, +{"m": 4, "n": 32, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 4, "minblocks": 26, "algorithm": "medium", "perf": 697.466, "source": "autotuned"}, +{"m": 4, "n": 32, "k": 7, "tile_m": 2, "tile_n": 3, "threads": 32, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 716.759, "source": "autotuned"}, +{"m": 4, "n": 32, "k": 9, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 715.929, "source": "autotuned"}, +{"m": 4, "n": 32, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 718.173, "source": "autotuned"}, +{"m": 4, "n": 32, "k": 25, "tile_m": 4, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 731.936, "source": "autotuned"}, +{"m": 4, "n": 32, "k": 26, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 734.347, "source": "autotuned"}, +{"m": 4, "n": 32, "k": 28, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 733.854, "source": "autotuned"}, +{"m": 4, "n": 32, "k": 32, "tile_m": 1, "tile_n": 2, "threads": 160, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 747.315, "source": "autotuned"}, +{"m": 4, "n": 32, "k": 45, "tile_m": 2, "tile_n": 1, "threads": 192, "grouping": 2, "minblocks": 5, "algorithm": "medium", "perf": 750.234, "source": "autotuned"}, +{"m": 4, "n": 45, "k": 4, "tile_m": 1, "tile_n": 7, "threads": 32, "grouping": 8, "minblocks": 27, "algorithm": "medium", "perf": 661.936, "source": "autotuned"}, +{"m": 4, "n": 45, "k": 5, "tile_m": 1, "tile_n": 6, "threads": 32, "grouping": 4, "minblocks": 25, "algorithm": "medium", "perf": 669.852, "source": "autotuned"}, +{"m": 4, "n": 45, "k": 7, "tile_m": 2, "tile_n": 3, "threads": 32, "grouping": 3, "minblocks": 20, "algorithm": "medium", "perf": 679.091, "source": "autotuned"}, +{"m": 4, "n": 45, "k": 9, "tile_m": 2, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 6, "algorithm": "medium", "perf": 689.661, "source": "autotuned"}, +{"m": 4, "n": 45, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 711.079, "source": "autotuned"}, +{"m": 4, "n": 45, "k": 25, "tile_m": 2, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 736.487, "source": "autotuned"}, +{"m": 4, "n": 45, "k": 26, "tile_m": 2, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 742.618, "source": "autotuned"}, +{"m": 4, "n": 45, "k": 28, "tile_m": 1, "tile_n": 2, "threads": 192, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 745.832, "source": "autotuned"}, +{"m": 4, "n": 45, "k": 32, "tile_m": 2, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 760.801, "source": "autotuned"}, +{"m": 4, "n": 45, "k": 45, "tile_m": 1, "tile_n": 2, "threads": 256, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 759.612, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 15, "algorithm": "medium", "perf": 298.03, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 14, "algorithm": "medium", "perf": 346.1, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 6, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 7, "minblocks": 2, "algorithm": "medium", "perf": 395.26, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 7, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 23, "algorithm": "medium", "perf": 358.983, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 8, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 24, "algorithm": "medium", "perf": 378.392, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 9, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 8, "algorithm": "medium", "perf": 392.962, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 13, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 427.297, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 22, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 5, "minblocks": 14, "algorithm": "medium", "perf": 479.602, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 23, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 14, "algorithm": "medium", "perf": 475.792, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 14, "algorithm": "medium", "perf": 480.624, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 5, "minblocks": 16, "algorithm": "medium", "perf": 468.896, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 28, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 15, "algorithm": "medium", "perf": 478.41, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 32, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 1, "minblocks": 9, "algorithm": "medium", "perf": 475.743, "source": "autotuned"}, +{"m": 5, "n": 4, "k": 45, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 1, "minblocks": 21, "algorithm": "medium", "perf": 457.174, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 12, "algorithm": "medium", "perf": 363.486, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 7, "minblocks": 1, "algorithm": "medium", "perf": 430.294, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 6, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 7, "minblocks": 6, "algorithm": "medium", "perf": 487.699, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 7, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 16, "algorithm": "medium", "perf": 434.941, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 8, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 1, "algorithm": "medium", "perf": 465.891, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 9, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 480.266, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 12, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 537.755, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 13, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 4, "minblocks": 12, "algorithm": "medium", "perf": 540.485, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 16, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 4, "minblocks": 3, "algorithm": "medium", "perf": 585.973, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 22, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 12, "algorithm": "medium", "perf": 552.18, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 23, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 19, "algorithm": "medium", "perf": 551.265, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 24, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 560.713, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 24, "algorithm": "medium", "perf": 543.459, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 22, "algorithm": "medium", "perf": 539.234, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 28, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 24, "algorithm": "medium", "perf": 539.9, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 32, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 12, "algorithm": "medium", "perf": 558.21, "source": "autotuned"}, +{"m": 5, "n": 5, "k": 45, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 525.597, "source": "autotuned"}, +{"m": 5, "n": 6, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 1, "algorithm": "medium", "perf": 438.832, "source": "autotuned"}, +{"m": 5, "n": 6, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 15, "algorithm": "medium", "perf": 516.845, "source": "autotuned"}, +{"m": 5, "n": 6, "k": 6, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 27, "algorithm": "medium", "perf": 481.695, "source": "autotuned"}, +{"m": 5, "n": 6, "k": 7, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 6, "algorithm": "medium", "perf": 496.549, "source": "autotuned"}, +{"m": 5, "n": 6, "k": 8, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 532.953, "source": "autotuned"}, +{"m": 5, "n": 6, "k": 9, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 26, "algorithm": "medium", "perf": 553.846, "source": "autotuned"}, +{"m": 5, "n": 7, "k": 4, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 16, "algorithm": "medium", "perf": 416.574, "source": "autotuned"}, +{"m": 5, "n": 7, "k": 5, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 5, "algorithm": "medium", "perf": 445.927, "source": "autotuned"}, +{"m": 5, "n": 7, "k": 6, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 2, "algorithm": "medium", "perf": 503.652, "source": "autotuned"}, +{"m": 5, "n": 7, "k": 7, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 19, "algorithm": "medium", "perf": 512.586, "source": "autotuned"}, +{"m": 5, "n": 7, "k": 8, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 19, "algorithm": "medium", "perf": 557.338, "source": "autotuned"}, +{"m": 5, "n": 7, "k": 9, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 22, "algorithm": "medium", "perf": 590.81, "source": "autotuned"}, +{"m": 5, "n": 7, "k": 13, "tile_m": 3, "tile_n": 1, "threads": 96, "grouping": 13, "minblocks": 16, "algorithm": "medium", "perf": 608.599, "source": "autotuned"}, +{"m": 5, "n": 7, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 592.774, "source": "autotuned"}, +{"m": 5, "n": 7, "k": 26, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 26, "algorithm": "medium", "perf": 599.357, "source": "autotuned"}, +{"m": 5, "n": 7, "k": 28, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 25, "algorithm": "medium", "perf": 591.972, "source": "autotuned"}, +{"m": 5, "n": 7, "k": 32, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 17, "algorithm": "medium", "perf": 617.18, "source": "autotuned"}, +{"m": 5, "n": 7, "k": 45, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 1, "minblocks": 6, "algorithm": "medium", "perf": 587.119, "source": "autotuned"}, +{"m": 5, "n": 8, "k": 4, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 2, "algorithm": "medium", "perf": 439.326, "source": "autotuned"}, +{"m": 5, "n": 8, "k": 5, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 13, "algorithm": "medium", "perf": 510.7, "source": "autotuned"}, +{"m": 5, "n": 8, "k": 6, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 3, "algorithm": "medium", "perf": 570.354, "source": "autotuned"}, +{"m": 5, "n": 8, "k": 7, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 21, "algorithm": "medium", "perf": 577.392, "source": "autotuned"}, +{"m": 5, "n": 8, "k": 8, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 25, "algorithm": "medium", "perf": 593.793, "source": "autotuned"}, +{"m": 5, "n": 8, "k": 9, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 23, "algorithm": "medium", "perf": 608.298, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 4, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 5, "algorithm": "medium", "perf": 481.098, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 5, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 1, "algorithm": "medium", "perf": 564.498, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 6, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 11, "algorithm": "medium", "perf": 610.45, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 7, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 25, "algorithm": "medium", "perf": 636.859, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 8, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 23, "algorithm": "medium", "perf": 626.219, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 9, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 21, "algorithm": "medium", "perf": 653.5, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 13, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 5, "minblocks": 14, "algorithm": "medium", "perf": 644.103, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 16, "tile_m": 1, "tile_n": 2, "threads": 96, "grouping": 4, "minblocks": 18, "algorithm": "medium", "perf": 674.517, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 22, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 16, "algorithm": "medium", "perf": 648.108, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 23, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 649.547, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 25, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 26, "algorithm": "medium", "perf": 651.245, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 26, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 26, "algorithm": "medium", "perf": 647.326, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 28, "tile_m": 1, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 655.242, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 32, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 12, "algorithm": "medium", "perf": 675.633, "source": "autotuned"}, +{"m": 5, "n": 9, "k": 45, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 645.929, "source": "autotuned"}, +{"m": 5, "n": 12, "k": 5, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 10, "algorithm": "medium", "perf": 691.538, "source": "autotuned"}, +{"m": 5, "n": 12, "k": 12, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 21, "algorithm": "medium", "perf": 751.708, "source": "autotuned"}, +{"m": 5, "n": 12, "k": 13, "tile_m": 1, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 21, "algorithm": "medium", "perf": 717.136, "source": "autotuned"}, +{"m": 5, "n": 12, "k": 26, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 16, "algorithm": "medium", "perf": 709.313, "source": "autotuned"}, +{"m": 5, "n": 12, "k": 32, "tile_m": 1, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 733.427, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 4, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 1, "algorithm": "medium", "perf": 669.163, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 5, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 1, "algorithm": "medium", "perf": 709.666, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 7, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 20, "algorithm": "medium", "perf": 717.806, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 9, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 21, "algorithm": "medium", "perf": 733.531, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 12, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 23, "algorithm": "medium", "perf": 758.992, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 13, "tile_m": 3, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 16, "algorithm": "medium", "perf": 718.762, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 16, "tile_m": 3, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 747.584, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 22, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 725.379, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 23, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 22, "algorithm": "medium", "perf": 720.143, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 24, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 741.677, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 25, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 722.723, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 26, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 23, "algorithm": "medium", "perf": 723.641, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 28, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 4, "algorithm": "medium", "perf": 718.646, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 32, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 17, "algorithm": "medium", "perf": 750.234, "source": "autotuned"}, +{"m": 5, "n": 13, "k": 45, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 2, "algorithm": "medium", "perf": 725.673, "source": "autotuned"}, +{"m": 5, "n": 16, "k": 5, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 17, "algorithm": "medium", "perf": 805.342, "source": "autotuned"}, +{"m": 5, "n": 16, "k": 9, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 24, "algorithm": "medium", "perf": 775.76, "source": "autotuned"}, +{"m": 5, "n": 16, "k": 13, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 27, "algorithm": "medium", "perf": 772.853, "source": "autotuned"}, +{"m": 5, "n": 16, "k": 16, "tile_m": 3, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 14, "algorithm": "medium", "perf": 787.938, "source": "autotuned"}, +{"m": 5, "n": 16, "k": 23, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 770.975, "source": "autotuned"}, +{"m": 5, "n": 16, "k": 32, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 3, "algorithm": "medium", "perf": 792.327, "source": "autotuned"}, +{"m": 5, "n": 22, "k": 4, "tile_m": 3, "tile_n": 2, "threads": 32, "grouping": 9, "minblocks": 2, "algorithm": "medium", "perf": 794.063, "source": "autotuned"}, +{"m": 5, "n": 22, "k": 5, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 4, "minblocks": 26, "algorithm": "medium", "perf": 791.304, "source": "autotuned"}, +{"m": 5, "n": 22, "k": 9, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 19, "algorithm": "medium", "perf": 788.463, "source": "autotuned"}, +{"m": 5, "n": 22, "k": 13, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 784.236, "source": "autotuned"}, +{"m": 5, "n": 22, "k": 22, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 804.773, "source": "autotuned"}, +{"m": 5, "n": 22, "k": 23, "tile_m": 5, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 807.012, "source": "autotuned"}, +{"m": 5, "n": 22, "k": 26, "tile_m": 3, "tile_n": 1, "threads": 160, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 803.622, "source": "autotuned"}, +{"m": 5, "n": 23, "k": 4, "tile_m": 3, "tile_n": 2, "threads": 32, "grouping": 9, "minblocks": 5, "algorithm": "medium", "perf": 765.772, "source": "autotuned"}, +{"m": 5, "n": 23, "k": 5, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 26, "algorithm": "medium", "perf": 772.399, "source": "autotuned"}, +{"m": 5, "n": 23, "k": 9, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 18, "algorithm": "medium", "perf": 790.675, "source": "autotuned"}, +{"m": 5, "n": 23, "k": 13, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 791.671, "source": "autotuned"}, +{"m": 5, "n": 23, "k": 16, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 831.147, "source": "autotuned"}, +{"m": 5, "n": 23, "k": 22, "tile_m": 5, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 811.151, "source": "autotuned"}, +{"m": 5, "n": 23, "k": 23, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 804.019, "source": "autotuned"}, +{"m": 5, "n": 23, "k": 26, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 803.475, "source": "autotuned"}, +{"m": 5, "n": 24, "k": 5, "tile_m": 3, "tile_n": 2, "threads": 32, "grouping": 8, "minblocks": 25, "algorithm": "medium", "perf": 796.427, "source": "autotuned"}, +{"m": 5, "n": 24, "k": 13, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 806.037, "source": "autotuned"}, +{"m": 5, "n": 24, "k": 24, "tile_m": 5, "tile_n": 1, "threads": 128, "grouping": 2, "minblocks": 10, "algorithm": "medium", "perf": 841.384, "source": "autotuned"}, +{"m": 5, "n": 24, "k": 26, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 828.284, "source": "autotuned"}, +{"m": 5, "n": 24, "k": 32, "tile_m": 1, "tile_n": 2, "threads": 192, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 845.334, "source": "autotuned"}, +{"m": 5, "n": 25, "k": 4, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 5, "algorithm": "medium", "perf": 797.443, "source": "autotuned"}, +{"m": 5, "n": 25, "k": 5, "tile_m": 3, "tile_n": 2, "threads": 32, "grouping": 9, "minblocks": 21, "algorithm": "medium", "perf": 784.633, "source": "autotuned"}, +{"m": 5, "n": 25, "k": 7, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 19, "algorithm": "medium", "perf": 770.487, "source": "autotuned"}, +{"m": 5, "n": 25, "k": 9, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 19, "algorithm": "medium", "perf": 800.91, "source": "autotuned"}, +{"m": 5, "n": 25, "k": 13, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 806.303, "source": "autotuned"}, +{"m": 5, "n": 25, "k": 25, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 2, "minblocks": 10, "algorithm": "medium", "perf": 824.361, "source": "autotuned"}, +{"m": 5, "n": 25, "k": 26, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 821.517, "source": "autotuned"}, +{"m": 5, "n": 25, "k": 28, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 828.857, "source": "autotuned"}, +{"m": 5, "n": 25, "k": 32, "tile_m": 3, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 844.859, "source": "autotuned"}, +{"m": 5, "n": 25, "k": 45, "tile_m": 5, "tile_n": 1, "threads": 288, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 835.988, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 4, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 4, "algorithm": "medium", "perf": 807.706, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 5, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 27, "algorithm": "medium", "perf": 791.356, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 7, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 19, "algorithm": "medium", "perf": 786.815, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 9, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 17, "algorithm": "medium", "perf": 796.818, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 12, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 12, "algorithm": "medium", "perf": 816.155, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 13, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 802.23, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 22, "tile_m": 5, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 823.99, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 23, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 827.597, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 24, "tile_m": 5, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 848.091, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 25, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 828.214, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 26, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 830.522, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 28, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 836.658, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 32, "tile_m": 3, "tile_n": 1, "threads": 224, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 855.53, "source": "autotuned"}, +{"m": 5, "n": 26, "k": 45, "tile_m": 3, "tile_n": 1, "threads": 256, "grouping": 2, "minblocks": 5, "algorithm": "medium", "perf": 849.24, "source": "autotuned"}, +{"m": 5, "n": 28, "k": 4, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 6, "algorithm": "medium", "perf": 799.005, "source": "autotuned"}, +{"m": 5, "n": 28, "k": 5, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 27, "algorithm": "medium", "perf": 784.792, "source": "autotuned"}, +{"m": 5, "n": 28, "k": 7, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 19, "algorithm": "medium", "perf": 783.827, "source": "autotuned"}, +{"m": 5, "n": 28, "k": 9, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 22, "algorithm": "medium", "perf": 805.649, "source": "autotuned"}, +{"m": 5, "n": 28, "k": 13, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 810.823, "source": "autotuned"}, +{"m": 5, "n": 28, "k": 25, "tile_m": 5, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 839.457, "source": "autotuned"}, +{"m": 5, "n": 28, "k": 26, "tile_m": 3, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 840.78, "source": "autotuned"}, +{"m": 5, "n": 28, "k": 28, "tile_m": 3, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 848.995, "source": "autotuned"}, +{"m": 5, "n": 28, "k": 32, "tile_m": 3, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 869.837, "source": "autotuned"}, +{"m": 5, "n": 28, "k": 45, "tile_m": 5, "tile_n": 1, "threads": 256, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 859.985, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 4, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 27, "algorithm": "medium", "perf": 805.336, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 5, "tile_m": 3, "tile_n": 2, "threads": 32, "grouping": 4, "minblocks": 25, "algorithm": "medium", "perf": 804.973, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 7, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 19, "algorithm": "medium", "perf": 819.405, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 9, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 15, "algorithm": "medium", "perf": 827.845, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 12, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 841.384, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 13, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 846.251, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 16, "tile_m": 3, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 2, "algorithm": "medium", "perf": 862.803, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 24, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 876.186, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 25, "tile_m": 5, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 866.321, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 26, "tile_m": 3, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 868.868, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 28, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 873.454, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 32, "tile_m": 3, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 890.816, "source": "autotuned"}, +{"m": 5, "n": 32, "k": 45, "tile_m": 3, "tile_n": 1, "threads": 288, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 889.606, "source": "autotuned"}, +{"m": 5, "n": 45, "k": 4, "tile_m": 5, "tile_n": 2, "threads": 32, "grouping": 10, "minblocks": 11, "algorithm": "medium", "perf": 751.005, "source": "autotuned"}, +{"m": 5, "n": 45, "k": 5, "tile_m": 3, "tile_n": 2, "threads": 64, "grouping": 4, "minblocks": 18, "algorithm": "medium", "perf": 769.065, "source": "autotuned"}, +{"m": 5, "n": 45, "k": 7, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 18, "algorithm": "medium", "perf": 795.933, "source": "autotuned"}, +{"m": 5, "n": 45, "k": 9, "tile_m": 3, "tile_n": 2, "threads": 64, "grouping": 3, "minblocks": 16, "algorithm": "medium", "perf": 816.787, "source": "autotuned"}, +{"m": 5, "n": 45, "k": 13, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 842.717, "source": "autotuned"}, +{"m": 5, "n": 45, "k": 25, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 885.081, "source": "autotuned"}, +{"m": 5, "n": 45, "k": 26, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 893.5, "source": "autotuned"}, +{"m": 5, "n": 45, "k": 28, "tile_m": 5, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 897.887, "source": "autotuned"}, +{"m": 5, "n": 45, "k": 32, "tile_m": 3, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 916.161, "source": "autotuned"}, +{"m": 5, "n": 45, "k": 45, "tile_m": 3, "tile_n": 1, "threads": 256, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 919.182, "source": "autotuned"}, +{"m": 6, "n": 4, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 15, "algorithm": "medium", "perf": 369.751, "source": "autotuned"}, +{"m": 6, "n": 4, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 1, "algorithm": "medium", "perf": 437.31, "source": "autotuned"}, +{"m": 6, "n": 4, "k": 6, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 8, "algorithm": "medium", "perf": 408.922, "source": "autotuned"}, +{"m": 6, "n": 4, "k": 7, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 15, "algorithm": "medium", "perf": 459.907, "source": "autotuned"}, +{"m": 6, "n": 4, "k": 8, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 2, "algorithm": "medium", "perf": 477.56, "source": "autotuned"}, +{"m": 6, "n": 4, "k": 9, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 482.086, "source": "autotuned"}, +{"m": 6, "n": 5, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 3, "algorithm": "medium", "perf": 454.274, "source": "autotuned"}, +{"m": 6, "n": 5, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 10, "algorithm": "medium", "perf": 543.411, "source": "autotuned"}, +{"m": 6, "n": 5, "k": 6, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 22, "algorithm": "medium", "perf": 509.28, "source": "autotuned"}, +{"m": 6, "n": 5, "k": 7, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 27, "algorithm": "medium", "perf": 530.719, "source": "autotuned"}, +{"m": 6, "n": 5, "k": 8, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 567.27, "source": "autotuned"}, +{"m": 6, "n": 5, "k": 9, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 586.779, "source": "autotuned"}, +{"m": 6, "n": 6, "k": 4, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 13, "minblocks": 23, "algorithm": "medium", "perf": 436.777, "source": "autotuned"}, +{"m": 6, "n": 6, "k": 5, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 13, "minblocks": 7, "algorithm": "medium", "perf": 514.148, "source": "autotuned"}, +{"m": 6, "n": 6, "k": 6, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 13, "minblocks": 21, "algorithm": "medium", "perf": 519.349, "source": "autotuned"}, +{"m": 6, "n": 6, "k": 7, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 13, "minblocks": 24, "algorithm": "medium", "perf": 551.619, "source": "autotuned"}, +{"m": 6, "n": 6, "k": 8, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 13, "minblocks": 26, "algorithm": "medium", "perf": 599.955, "source": "autotuned"}, +{"m": 6, "n": 7, "k": 4, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 13, "minblocks": 8, "algorithm": "medium", "perf": 510.693, "source": "autotuned"}, +{"m": 6, "n": 7, "k": 5, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 10, "minblocks": 3, "algorithm": "medium", "perf": 546.154, "source": "autotuned"}, +{"m": 6, "n": 7, "k": 6, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 10, "minblocks": 19, "algorithm": "medium", "perf": 578.921, "source": "autotuned"}, +{"m": 6, "n": 7, "k": 7, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 13, "minblocks": 26, "algorithm": "medium", "perf": 620.416, "source": "autotuned"}, +{"m": 6, "n": 7, "k": 8, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 13, "minblocks": 27, "algorithm": "medium", "perf": 679.445, "source": "autotuned"}, +{"m": 6, "n": 7, "k": 9, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 703.892, "source": "autotuned"}, +{"m": 6, "n": 8, "k": 4, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 8, "minblocks": 8, "algorithm": "medium", "perf": 537.011, "source": "autotuned"}, +{"m": 6, "n": 8, "k": 5, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 5, "minblocks": 4, "algorithm": "medium", "perf": 622.638, "source": "autotuned"}, +{"m": 6, "n": 8, "k": 6, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 13, "minblocks": 22, "algorithm": "medium", "perf": 652.165, "source": "autotuned"}, +{"m": 6, "n": 8, "k": 7, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 5, "minblocks": 27, "algorithm": "medium", "perf": 697.9, "source": "autotuned"}, +{"m": 6, "n": 8, "k": 8, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 9, "minblocks": 27, "algorithm": "medium", "perf": 731.135, "source": "autotuned"}, +{"m": 6, "n": 8, "k": 9, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 4, "minblocks": 26, "algorithm": "medium", "perf": 729.606, "source": "autotuned"}, +{"m": 6, "n": 9, "k": 4, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 10, "minblocks": 1, "algorithm": "medium", "perf": 586.525, "source": "autotuned"}, +{"m": 6, "n": 9, "k": 5, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 10, "minblocks": 11, "algorithm": "medium", "perf": 677.531, "source": "autotuned"}, +{"m": 6, "n": 9, "k": 6, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 10, "minblocks": 27, "algorithm": "medium", "perf": 696.243, "source": "autotuned"}, +{"m": 6, "n": 9, "k": 7, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 10, "minblocks": 26, "algorithm": "medium", "perf": 760.106, "source": "autotuned"}, +{"m": 6, "n": 9, "k": 8, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 755.906, "source": "autotuned"}, +{"m": 6, "n": 9, "k": 9, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 4, "minblocks": 25, "algorithm": "medium", "perf": 768.741, "source": "autotuned"}, +{"m": 7, "n": 4, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 16, "algorithm": "medium", "perf": 412.809, "source": "autotuned"}, +{"m": 7, "n": 4, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 25, "algorithm": "medium", "perf": 391.024, "source": "autotuned"}, +{"m": 7, "n": 4, "k": 6, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 7, "algorithm": "medium", "perf": 447.672, "source": "autotuned"}, +{"m": 7, "n": 4, "k": 7, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 10, "algorithm": "medium", "perf": 480.486, "source": "autotuned"}, +{"m": 7, "n": 4, "k": 8, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 14, "algorithm": "medium", "perf": 492.186, "source": "autotuned"}, +{"m": 7, "n": 4, "k": 9, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 27, "algorithm": "medium", "perf": 519.029, "source": "autotuned"}, +{"m": 7, "n": 4, "k": 13, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 25, "algorithm": "medium", "perf": 561.624, "source": "autotuned"}, +{"m": 7, "n": 4, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 14, "algorithm": "medium", "perf": 538.874, "source": "autotuned"}, +{"m": 7, "n": 4, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 544.314, "source": "autotuned"}, +{"m": 7, "n": 4, "k": 28, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 553.272, "source": "autotuned"}, +{"m": 7, "n": 4, "k": 32, "tile_m": 1, "tile_n": 1, "threads": 160, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 549.476, "source": "autotuned"}, +{"m": 7, "n": 4, "k": 45, "tile_m": 1, "tile_n": 1, "threads": 192, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 527.092, "source": "autotuned"}, +{"m": 7, "n": 5, "k": 4, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 13, "algorithm": "medium", "perf": 412.862, "source": "autotuned"}, +{"m": 7, "n": 5, "k": 5, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 13, "algorithm": "medium", "perf": 440.647, "source": "autotuned"}, +{"m": 7, "n": 5, "k": 6, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 17, "algorithm": "medium", "perf": 497.706, "source": "autotuned"}, +{"m": 7, "n": 5, "k": 7, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 19, "algorithm": "medium", "perf": 513.429, "source": "autotuned"}, +{"m": 7, "n": 5, "k": 9, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 20, "algorithm": "medium", "perf": 586.3, "source": "autotuned"}, +{"m": 7, "n": 5, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 96, "grouping": 13, "minblocks": 21, "algorithm": "medium", "perf": 608.599, "source": "autotuned"}, +{"m": 7, "n": 5, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 15, "algorithm": "medium", "perf": 596.467, "source": "autotuned"}, +{"m": 7, "n": 5, "k": 26, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 23, "algorithm": "medium", "perf": 578.722, "source": "autotuned"}, +{"m": 7, "n": 5, "k": 28, "tile_m": 1, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 585.374, "source": "autotuned"}, +{"m": 7, "n": 5, "k": 32, "tile_m": 2, "tile_n": 2, "threads": 32, "grouping": 1, "minblocks": 4, "algorithm": "medium", "perf": 601.751, "source": "autotuned"}, +{"m": 7, "n": 5, "k": 45, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 3, "algorithm": "medium", "perf": 582.16, "source": "autotuned"}, +{"m": 7, "n": 6, "k": 5, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 3, "algorithm": "medium", "perf": 532.017, "source": "autotuned"}, +{"m": 7, "n": 6, "k": 6, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 19, "algorithm": "medium", "perf": 561.04, "source": "autotuned"}, +{"m": 7, "n": 6, "k": 7, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 19, "algorithm": "medium", "perf": 602.177, "source": "autotuned"}, +{"m": 7, "n": 6, "k": 8, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 25, "algorithm": "medium", "perf": 655.348, "source": "autotuned"}, +{"m": 7, "n": 6, "k": 9, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 9, "minblocks": 21, "algorithm": "medium", "perf": 679.328, "source": "autotuned"}, +{"m": 7, "n": 7, "k": 4, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 13, "minblocks": 16, "algorithm": "medium", "perf": 570.156, "source": "autotuned"}, +{"m": 7, "n": 7, "k": 6, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 17, "algorithm": "medium", "perf": 630.712, "source": "autotuned"}, +{"m": 7, "n": 7, "k": 7, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 27, "algorithm": "medium", "perf": 674.449, "source": "autotuned"}, +{"m": 7, "n": 7, "k": 8, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 27, "algorithm": "medium", "perf": 729.115, "source": "autotuned"}, +{"m": 7, "n": 7, "k": 9, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 21, "algorithm": "medium", "perf": 748.054, "source": "autotuned"}, +{"m": 7, "n": 7, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 4, "minblocks": 2, "algorithm": "medium", "perf": 723.398, "source": "autotuned"}, +{"m": 7, "n": 7, "k": 25, "tile_m": 2, "tile_n": 2, "threads": 32, "grouping": 2, "minblocks": 10, "algorithm": "medium", "perf": 714.426, "source": "autotuned"}, +{"m": 7, "n": 7, "k": 26, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 19, "algorithm": "medium", "perf": 715.418, "source": "autotuned"}, +{"m": 7, "n": 7, "k": 28, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 15, "algorithm": "medium", "perf": 722.838, "source": "autotuned"}, +{"m": 7, "n": 7, "k": 32, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 12, "algorithm": "medium", "perf": 745.885, "source": "autotuned"}, +{"m": 7, "n": 7, "k": 45, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 4, "algorithm": "medium", "perf": 713.538, "source": "autotuned"}, +{"m": 7, "n": 8, "k": 4, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 8, "algorithm": "medium", "perf": 608.405, "source": "autotuned"}, +{"m": 7, "n": 8, "k": 5, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 19, "algorithm": "medium", "perf": 651.642, "source": "autotuned"}, +{"m": 7, "n": 8, "k": 6, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 9, "minblocks": 24, "algorithm": "medium", "perf": 717.014, "source": "autotuned"}, +{"m": 7, "n": 8, "k": 7, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 9, "minblocks": 22, "algorithm": "medium", "perf": 772.091, "source": "autotuned"}, +{"m": 7, "n": 8, "k": 8, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 27, "algorithm": "medium", "perf": 787.746, "source": "autotuned"}, +{"m": 7, "n": 8, "k": 9, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 26, "algorithm": "medium", "perf": 768.533, "source": "autotuned"}, +{"m": 7, "n": 9, "k": 4, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 1, "algorithm": "medium", "perf": 627.635, "source": "autotuned"}, +{"m": 7, "n": 9, "k": 5, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 24, "algorithm": "medium", "perf": 690.695, "source": "autotuned"}, +{"m": 7, "n": 9, "k": 6, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 21, "algorithm": "medium", "perf": 751.319, "source": "autotuned"}, +{"m": 7, "n": 9, "k": 7, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 23, "algorithm": "medium", "perf": 797.512, "source": "autotuned"}, +{"m": 7, "n": 9, "k": 8, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 20, "algorithm": "medium", "perf": 782.853, "source": "autotuned"}, +{"m": 7, "n": 9, "k": 9, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 24, "algorithm": "medium", "perf": 778.312, "source": "autotuned"}, +{"m": 7, "n": 9, "k": 13, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 14, "algorithm": "medium", "perf": 783.727, "source": "autotuned"}, +{"m": 7, "n": 9, "k": 25, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 24, "algorithm": "medium", "perf": 771.004, "source": "autotuned"}, +{"m": 7, "n": 9, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 256, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 774.135, "source": "autotuned"}, +{"m": 7, "n": 9, "k": 28, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 15, "algorithm": "medium", "perf": 773.538, "source": "autotuned"}, +{"m": 7, "n": 9, "k": 32, "tile_m": 2, "tile_n": 2, "threads": 32, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 806.222, "source": "autotuned"}, +{"m": 7, "n": 9, "k": 45, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 16, "algorithm": "medium", "perf": 775.68, "source": "autotuned"}, +{"m": 7, "n": 13, "k": 4, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 1, "algorithm": "medium", "perf": 837.383, "source": "autotuned"}, +{"m": 7, "n": 13, "k": 5, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 24, "algorithm": "medium", "perf": 817.077, "source": "autotuned"}, +{"m": 7, "n": 13, "k": 7, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 24, "algorithm": "medium", "perf": 879.853, "source": "autotuned"}, +{"m": 7, "n": 13, "k": 9, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 21, "algorithm": "medium", "perf": 882.819, "source": "autotuned"}, +{"m": 7, "n": 13, "k": 13, "tile_m": 4, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 864.026, "source": "autotuned"}, +{"m": 7, "n": 13, "k": 25, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 873.66, "source": "autotuned"}, +{"m": 7, "n": 13, "k": 26, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 17, "algorithm": "medium", "perf": 882.585, "source": "autotuned"}, +{"m": 7, "n": 13, "k": 28, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 17, "algorithm": "medium", "perf": 884.999, "source": "autotuned"}, +{"m": 7, "n": 13, "k": 32, "tile_m": 2, "tile_n": 2, "threads": 32, "grouping": 2, "minblocks": 17, "algorithm": "medium", "perf": 919.479, "source": "autotuned"}, +{"m": 7, "n": 13, "k": 45, "tile_m": 4, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 893.084, "source": "autotuned"}, +{"m": 7, "n": 25, "k": 4, "tile_m": 7, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 21, "algorithm": "medium", "perf": 951.384, "source": "autotuned"}, +{"m": 7, "n": 25, "k": 5, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 20, "algorithm": "medium", "perf": 943.183, "source": "autotuned"}, +{"m": 7, "n": 25, "k": 7, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 20, "algorithm": "medium", "perf": 950.204, "source": "autotuned"}, +{"m": 7, "n": 25, "k": 9, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 984.683, "source": "autotuned"}, +{"m": 7, "n": 25, "k": 13, "tile_m": 7, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 1007.31, "source": "autotuned"}, +{"m": 7, "n": 25, "k": 25, "tile_m": 2, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1038.69, "source": "autotuned"}, +{"m": 7, "n": 25, "k": 26, "tile_m": 2, "tile_n": 2, "threads": 192, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1048.39, "source": "autotuned"}, +{"m": 7, "n": 25, "k": 28, "tile_m": 4, "tile_n": 1, "threads": 256, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1044.36, "source": "autotuned"}, +{"m": 7, "n": 25, "k": 32, "tile_m": 1, "tile_n": 3, "threads": 256, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1076.16, "source": "autotuned"}, +{"m": 7, "n": 25, "k": 45, "tile_m": 4, "tile_n": 1, "threads": 320, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 1071.86, "source": "autotuned"}, +{"m": 7, "n": 26, "k": 4, "tile_m": 7, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 26, "algorithm": "medium", "perf": 959.977, "source": "autotuned"}, +{"m": 7, "n": 26, "k": 5, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 11, "minblocks": 23, "algorithm": "medium", "perf": 929.153, "source": "autotuned"}, +{"m": 7, "n": 26, "k": 7, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 22, "algorithm": "medium", "perf": 966.104, "source": "autotuned"}, +{"m": 7, "n": 26, "k": 9, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 23, "algorithm": "medium", "perf": 987.395, "source": "autotuned"}, +{"m": 7, "n": 26, "k": 13, "tile_m": 7, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1018.74, "source": "autotuned"}, +{"m": 7, "n": 26, "k": 25, "tile_m": 4, "tile_n": 1, "threads": 192, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 1048.39, "source": "autotuned"}, +{"m": 7, "n": 26, "k": 26, "tile_m": 4, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1054.07, "source": "autotuned"}, +{"m": 7, "n": 26, "k": 28, "tile_m": 1, "tile_n": 3, "threads": 224, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 1066.74, "source": "autotuned"}, +{"m": 7, "n": 26, "k": 32, "tile_m": 4, "tile_n": 2, "threads": 32, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 1096.73, "source": "autotuned"}, +{"m": 7, "n": 26, "k": 45, "tile_m": 4, "tile_n": 1, "threads": 320, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 1090.98, "source": "autotuned"}, +{"m": 7, "n": 28, "k": 4, "tile_m": 7, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 27, "algorithm": "medium", "perf": 982.927, "source": "autotuned"}, +{"m": 7, "n": 28, "k": 5, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 23, "algorithm": "medium", "perf": 928.32, "source": "autotuned"}, +{"m": 7, "n": 28, "k": 7, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 20, "algorithm": "medium", "perf": 972.281, "source": "autotuned"}, +{"m": 7, "n": 28, "k": 9, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 17, "algorithm": "medium", "perf": 1013.11, "source": "autotuned"}, +{"m": 7, "n": 28, "k": 13, "tile_m": 7, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1037.78, "source": "autotuned"}, +{"m": 7, "n": 28, "k": 25, "tile_m": 4, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1076.16, "source": "autotuned"}, +{"m": 7, "n": 28, "k": 26, "tile_m": 4, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1091.09, "source": "autotuned"}, +{"m": 7, "n": 28, "k": 28, "tile_m": 4, "tile_n": 1, "threads": 224, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 1088.08, "source": "autotuned"}, +{"m": 7, "n": 28, "k": 32, "tile_m": 4, "tile_n": 1, "threads": 256, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1113.98, "source": "autotuned"}, +{"m": 7, "n": 28, "k": 45, "tile_m": 2, "tile_n": 2, "threads": 320, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 1110.25, "source": "autotuned"}, +{"m": 7, "n": 32, "k": 4, "tile_m": 7, "tile_n": 1, "threads": 32, "grouping": 4, "minblocks": 25, "algorithm": "medium", "perf": 968.388, "source": "autotuned"}, +{"m": 7, "n": 32, "k": 5, "tile_m": 4, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 24, "algorithm": "medium", "perf": 982.45, "source": "autotuned"}, +{"m": 7, "n": 32, "k": 7, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 20, "algorithm": "medium", "perf": 1027.27, "source": "autotuned"}, +{"m": 7, "n": 32, "k": 9, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 15, "algorithm": "medium", "perf": 1044.53, "source": "autotuned"}, +{"m": 7, "n": 32, "k": 13, "tile_m": 4, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1074.08, "source": "autotuned"}, +{"m": 7, "n": 32, "k": 25, "tile_m": 7, "tile_n": 1, "threads": 224, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1112.64, "source": "autotuned"}, +{"m": 7, "n": 32, "k": 26, "tile_m": 7, "tile_n": 1, "threads": 224, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 1128.45, "source": "autotuned"}, +{"m": 7, "n": 32, "k": 28, "tile_m": 4, "tile_n": 1, "threads": 224, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1133.3, "source": "autotuned"}, +{"m": 7, "n": 32, "k": 32, "tile_m": 2, "tile_n": 1, "threads": 256, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 1145.16, "source": "autotuned"}, +{"m": 7, "n": 32, "k": 45, "tile_m": 4, "tile_n": 1, "threads": 320, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 1159.87, "source": "autotuned"}, +{"m": 7, "n": 45, "k": 4, "tile_m": 7, "tile_n": 2, "threads": 32, "grouping": 4, "minblocks": 17, "algorithm": "medium", "perf": 920.742, "source": "autotuned"}, +{"m": 7, "n": 45, "k": 5, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 15, "algorithm": "medium", "perf": 954.678, "source": "autotuned"}, +{"m": 7, "n": 45, "k": 7, "tile_m": 2, "tile_n": 3, "threads": 64, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1004.78, "source": "autotuned"}, +{"m": 7, "n": 45, "k": 9, "tile_m": 2, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 15, "algorithm": "medium", "perf": 1051.89, "source": "autotuned"}, +{"m": 7, "n": 45, "k": 13, "tile_m": 4, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1087.89, "source": "autotuned"}, +{"m": 7, "n": 45, "k": 25, "tile_m": 2, "tile_n": 2, "threads": 224, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1161.18, "source": "autotuned"}, +{"m": 7, "n": 45, "k": 26, "tile_m": 4, "tile_n": 1, "threads": 256, "grouping": 2, "minblocks": 5, "algorithm": "medium", "perf": 1167.25, "source": "autotuned"}, +{"m": 7, "n": 45, "k": 28, "tile_m": 4, "tile_n": 1, "threads": 224, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1176.58, "source": "autotuned"}, +{"m": 7, "n": 45, "k": 32, "tile_m": 2, "tile_n": 2, "threads": 256, "grouping": 2, "minblocks": 4, "algorithm": "medium", "perf": 1207.27, "source": "autotuned"}, +{"m": 7, "n": 45, "k": 45, "tile_m": 4, "tile_n": 1, "threads": 320, "grouping": 2, "minblocks": 3, "algorithm": "medium", "perf": 1213.99, "source": "autotuned"}, +{"m": 8, "n": 4, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 7, "minblocks": 1, "algorithm": "medium", "perf": 431.726, "source": "autotuned"}, +{"m": 8, "n": 4, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 11, "algorithm": "medium", "perf": 477.505, "source": "autotuned"}, +{"m": 8, "n": 4, "k": 6, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 23, "algorithm": "medium", "perf": 546.389, "source": "autotuned"}, +{"m": 8, "n": 4, "k": 7, "tile_m": 1, "tile_n": 1, "threads": 32, "grouping": 5, "minblocks": 5, "algorithm": "medium", "perf": 565.522, "source": "autotuned"}, +{"m": 8, "n": 4, "k": 8, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 5, "minblocks": 27, "algorithm": "medium", "perf": 566.549, "source": "autotuned"}, +{"m": 8, "n": 5, "k": 5, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 10, "minblocks": 12, "algorithm": "medium", "perf": 520.996, "source": "autotuned"}, +{"m": 8, "n": 5, "k": 6, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 10, "minblocks": 1, "algorithm": "medium", "perf": 584.015, "source": "autotuned"}, +{"m": 8, "n": 5, "k": 7, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 13, "minblocks": 26, "algorithm": "medium", "perf": 596.162, "source": "autotuned"}, +{"m": 8, "n": 6, "k": 4, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 8, "minblocks": 1, "algorithm": "medium", "perf": 535.147, "source": "autotuned"}, +{"m": 8, "n": 6, "k": 5, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 5, "minblocks": 1, "algorithm": "medium", "perf": 623.198, "source": "autotuned"}, +{"m": 8, "n": 6, "k": 7, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 692.73, "source": "autotuned"}, +{"m": 8, "n": 6, "k": 8, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 10, "minblocks": 26, "algorithm": "medium", "perf": 730.266, "source": "autotuned"}, +{"m": 8, "n": 6, "k": 9, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 4, "minblocks": 27, "algorithm": "medium", "perf": 725.755, "source": "autotuned"}, +{"m": 8, "n": 7, "k": 5, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 13, "minblocks": 25, "algorithm": "medium", "perf": 674.466, "source": "autotuned"}, +{"m": 8, "n": 7, "k": 6, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 10, "minblocks": 25, "algorithm": "medium", "perf": 737.422, "source": "autotuned"}, +{"m": 8, "n": 7, "k": 8, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 809.675, "source": "autotuned"}, +{"m": 8, "n": 7, "k": 9, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 4, "minblocks": 25, "algorithm": "medium", "perf": 807.945, "source": "autotuned"}, +{"m": 8, "n": 8, "k": 4, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 10, "minblocks": 4, "algorithm": "medium", "perf": 751.703, "source": "autotuned"}, +{"m": 8, "n": 8, "k": 8, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 948.152, "source": "autotuned"}, +{"m": 8, "n": 8, "k": 9, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 4, "minblocks": 24, "algorithm": "medium", "perf": 873.252, "source": "autotuned"}, +{"m": 8, "n": 9, "k": 4, "tile_m": 1, "tile_n": 3, "threads": 64, "grouping": 13, "minblocks": 22, "algorithm": "medium", "perf": 703.856, "source": "autotuned"}, +{"m": 8, "n": 9, "k": 6, "tile_m": 1, "tile_n": 3, "threads": 64, "grouping": 10, "minblocks": 22, "algorithm": "medium", "perf": 842.134, "source": "autotuned"}, +{"m": 8, "n": 9, "k": 8, "tile_m": 1, "tile_n": 3, "threads": 96, "grouping": 10, "minblocks": 19, "algorithm": "medium", "perf": 855.471, "source": "autotuned"}, +{"m": 8, "n": 9, "k": 9, "tile_m": 1, "tile_n": 3, "threads": 96, "grouping": 14, "minblocks": 14, "algorithm": "medium", "perf": 845.869, "source": "autotuned"}, +{"m": 9, "n": 4, "k": 4, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 8, "minblocks": 17, "algorithm": "medium", "perf": 375.31, "source": "autotuned"}, +{"m": 9, "n": 4, "k": 5, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 8, "minblocks": 26, "algorithm": "medium", "perf": 439.162, "source": "autotuned"}, +{"m": 9, "n": 4, "k": 6, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 483.505, "source": "autotuned"}, +{"m": 9, "n": 4, "k": 7, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 518.811, "source": "autotuned"}, +{"m": 9, "n": 4, "k": 9, "tile_m": 1, "tile_n": 4, "threads": 64, "grouping": 13, "minblocks": 19, "algorithm": "medium", "perf": 531.703, "source": "autotuned"}, +{"m": 9, "n": 4, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 22, "algorithm": "medium", "perf": 578.535, "source": "autotuned"}, +{"m": 9, "n": 4, "k": 22, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 15, "algorithm": "medium", "perf": 577.272, "source": "autotuned"}, +{"m": 9, "n": 4, "k": 23, "tile_m": 1, "tile_n": 1, "threads": 96, "grouping": 2, "minblocks": 18, "algorithm": "medium", "perf": 574.223, "source": "autotuned"}, +{"m": 9, "n": 4, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 567.038, "source": "autotuned"}, +{"m": 9, "n": 4, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 15, "algorithm": "medium", "perf": 575.2, "source": "autotuned"}, +{"m": 9, "n": 4, "k": 28, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 2, "minblocks": 13, "algorithm": "medium", "perf": 579.048, "source": "autotuned"}, +{"m": 9, "n": 4, "k": 32, "tile_m": 1, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 585.549, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 4, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 21, "algorithm": "medium", "perf": 458.869, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 5, "tile_m": 1, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 24, "algorithm": "medium", "perf": 532.058, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 6, "tile_m": 1, "tile_n": 2, "threads": 32, "grouping": 10, "minblocks": 14, "algorithm": "medium", "perf": 561.861, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 8, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 10, "minblocks": 26, "algorithm": "medium", "perf": 607.333, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 9, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 25, "algorithm": "medium", "perf": 631.071, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 13, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 5, "minblocks": 14, "algorithm": "medium", "perf": 650.478, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 16, "tile_m": 1, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 18, "algorithm": "medium", "perf": 681.007, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 22, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 656.849, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 23, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 655.614, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 15, "algorithm": "medium", "perf": 656.774, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 160, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 640.209, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 32, "tile_m": 1, "tile_n": 1, "threads": 192, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 678.178, "source": "autotuned"}, +{"m": 9, "n": 5, "k": 45, "tile_m": 1, "tile_n": 1, "threads": 256, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 645.929, "source": "autotuned"}, +{"m": 9, "n": 6, "k": 4, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 6, "algorithm": "medium", "perf": 542.835, "source": "autotuned"}, +{"m": 9, "n": 6, "k": 5, "tile_m": 2, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 4, "algorithm": "medium", "perf": 619.213, "source": "autotuned"}, +{"m": 9, "n": 6, "k": 6, "tile_m": 1, "tile_n": 2, "threads": 64, "grouping": 13, "minblocks": 25, "algorithm": "medium", "perf": 646.841, "source": "autotuned"}, +{"m": 9, "n": 6, "k": 7, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 25, "algorithm": "medium", "perf": 696.311, "source": "autotuned"}, +{"m": 9, "n": 6, "k": 8, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 25, "algorithm": "medium", "perf": 710.748, "source": "autotuned"}, +{"m": 9, "n": 7, "k": 4, "tile_m": 6, "tile_n": 1, "threads": 32, "grouping": 8, "minblocks": 25, "algorithm": "medium", "perf": 605.959, "source": "autotuned"}, +{"m": 9, "n": 7, "k": 5, "tile_m": 6, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 20, "algorithm": "medium", "perf": 657.579, "source": "autotuned"}, +{"m": 9, "n": 7, "k": 7, "tile_m": 6, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 18, "algorithm": "medium", "perf": 768.916, "source": "autotuned"}, +{"m": 9, "n": 7, "k": 9, "tile_m": 6, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 15, "algorithm": "medium", "perf": 772.147, "source": "autotuned"}, +{"m": 9, "n": 7, "k": 13, "tile_m": 1, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 20, "algorithm": "medium", "perf": 775.811, "source": "autotuned"}, +{"m": 9, "n": 7, "k": 25, "tile_m": 1, "tile_n": 1, "threads": 192, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 769.283, "source": "autotuned"}, +{"m": 9, "n": 7, "k": 28, "tile_m": 1, "tile_n": 1, "threads": 256, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 778.217, "source": "autotuned"}, +{"m": 9, "n": 7, "k": 32, "tile_m": 1, "tile_n": 1, "threads": 256, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 799.404, "source": "autotuned"}, +{"m": 9, "n": 8, "k": 4, "tile_m": 6, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 17, "algorithm": "medium", "perf": 667.45, "source": "autotuned"}, +{"m": 9, "n": 8, "k": 5, "tile_m": 6, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 17, "algorithm": "medium", "perf": 734.394, "source": "autotuned"}, +{"m": 9, "n": 8, "k": 6, "tile_m": 6, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 19, "algorithm": "medium", "perf": 802.799, "source": "autotuned"}, +{"m": 9, "n": 8, "k": 8, "tile_m": 2, "tile_n": 2, "threads": 96, "grouping": 13, "minblocks": 15, "algorithm": "medium", "perf": 818.438, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 4, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 24, "algorithm": "medium", "perf": 721.956, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 5, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 23, "algorithm": "medium", "perf": 798.279, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 6, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 10, "minblocks": 11, "algorithm": "medium", "perf": 851.583, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 7, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 22, "algorithm": "medium", "perf": 882.204, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 8, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 4, "minblocks": 26, "algorithm": "medium", "perf": 897.133, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 9, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 26, "algorithm": "medium", "perf": 894.306, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 10, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 27, "algorithm": "medium", "perf": 891.564, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 12, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 25, "algorithm": "medium", "perf": 904.749, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 13, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 25, "algorithm": "medium", "perf": 885.963, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 16, "tile_m": 1, "tile_n": 3, "threads": 32, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 915.109, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 17, "tile_m": 2, "tile_n": 2, "threads": 32, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 879.336, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 22, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 884.267, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 23, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 15, "algorithm": "medium", "perf": 881.057, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 25, "tile_m": 2, "tile_n": 2, "threads": 32, "grouping": 2, "minblocks": 13, "algorithm": "medium", "perf": 889.476, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 26, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 889.636, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 28, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 899.71, "source": "autotuned"}, +{"m": 9, "n": 9, "k": 45, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 887.859, "source": "autotuned"}, +{"m": 9, "n": 10, "k": 10, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 5, "minblocks": 13, "algorithm": "medium", "perf": 898.622, "source": "autotuned"}, +{"m": 9, "n": 10, "k": 32, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 4, "algorithm": "medium", "perf": 952.679, "source": "autotuned"}, +{"m": 9, "n": 12, "k": 9, "tile_m": 2, "tile_n": 2, "threads": 128, "grouping": 4, "minblocks": 16, "algorithm": "medium", "perf": 959.51, "source": "autotuned"}, +{"m": 9, "n": 12, "k": 10, "tile_m": 2, "tile_n": 2, "threads": 128, "grouping": 6, "minblocks": 12, "algorithm": "medium", "perf": 962.088, "source": "autotuned"}, +{"m": 9, "n": 12, "k": 32, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 11, "algorithm": "medium", "perf": 1028.89, "source": "autotuned"}, +{"m": 9, "n": 13, "k": 4, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 23, "algorithm": "medium", "perf": 932.634, "source": "autotuned"}, +{"m": 9, "n": 13, "k": 5, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 21, "algorithm": "medium", "perf": 931.325, "source": "autotuned"}, +{"m": 9, "n": 13, "k": 9, "tile_m": 5, "tile_n": 1, "threads": 128, "grouping": 6, "minblocks": 11, "algorithm": "medium", "perf": 954.731, "source": "autotuned"}, +{"m": 9, "n": 13, "k": 13, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 14, "algorithm": "medium", "perf": 984.455, "source": "autotuned"}, +{"m": 9, "n": 13, "k": 16, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 22, "algorithm": "medium", "perf": 1020.2, "source": "autotuned"}, +{"m": 9, "n": 13, "k": 22, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 16, "algorithm": "medium", "perf": 992.026, "source": "autotuned"}, +{"m": 9, "n": 13, "k": 23, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 11, "algorithm": "medium", "perf": 1008.27, "source": "autotuned"}, +{"m": 9, "n": 13, "k": 26, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 1007.6, "source": "autotuned"}, +{"m": 9, "n": 16, "k": 5, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 9, "minblocks": 24, "algorithm": "medium", "perf": 1049.8, "source": "autotuned"}, +{"m": 9, "n": 16, "k": 9, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 1049.44, "source": "autotuned"}, +{"m": 9, "n": 16, "k": 13, "tile_m": 5, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 1071.58, "source": "autotuned"}, +{"m": 9, "n": 16, "k": 16, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 17, "algorithm": "medium", "perf": 1100.21, "source": "autotuned"}, +{"m": 9, "n": 16, "k": 17, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 18, "algorithm": "medium", "perf": 1087.28, "source": "autotuned"}, +{"m": 9, "n": 16, "k": 22, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1100.47, "source": "autotuned"}, +{"m": 9, "n": 16, "k": 23, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 20, "algorithm": "medium", "perf": 1092.09, "source": "autotuned"}, +{"m": 9, "n": 17, "k": 9, "tile_m": 1, "tile_n": 2, "threads": 96, "grouping": 10, "minblocks": 17, "algorithm": "medium", "perf": 1007.55, "source": "autotuned"}, +{"m": 9, "n": 17, "k": 16, "tile_m": 1, "tile_n": 2, "threads": 160, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1095.06, "source": "autotuned"}, +{"m": 9, "n": 17, "k": 17, "tile_m": 1, "tile_n": 2, "threads": 160, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1071.59, "source": "autotuned"}, +{"m": 9, "n": 17, "k": 22, "tile_m": 3, "tile_n": 2, "threads": 64, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1078.61, "source": "autotuned"}, +{"m": 9, "n": 17, "k": 23, "tile_m": 3, "tile_n": 2, "threads": 32, "grouping": 2, "minblocks": 20, "algorithm": "medium", "perf": 1082.66, "source": "autotuned"}, +{"m": 9, "n": 22, "k": 4, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 23, "algorithm": "medium", "perf": 1032.38, "source": "autotuned"}, +{"m": 9, "n": 22, "k": 9, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 1102.68, "source": "autotuned"}, +{"m": 9, "n": 22, "k": 13, "tile_m": 5, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1125.11, "source": "autotuned"}, +{"m": 9, "n": 22, "k": 16, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1165.93, "source": "autotuned"}, +{"m": 9, "n": 22, "k": 17, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1143.83, "source": "autotuned"}, +{"m": 9, "n": 22, "k": 22, "tile_m": 9, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 15, "algorithm": "medium", "perf": 1163.2, "source": "autotuned"}, +{"m": 9, "n": 22, "k": 23, "tile_m": 9, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 16, "algorithm": "medium", "perf": 1163.88, "source": "autotuned"}, +{"m": 9, "n": 22, "k": 26, "tile_m": 9, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 15, "algorithm": "medium", "perf": 1186.82, "source": "autotuned"}, +{"m": 9, "n": 23, "k": 4, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 24, "algorithm": "medium", "perf": 1039.39, "source": "autotuned"}, +{"m": 9, "n": 23, "k": 5, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 20, "algorithm": "medium", "perf": 1043.06, "source": "autotuned"}, +{"m": 9, "n": 23, "k": 9, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 1101.62, "source": "autotuned"}, +{"m": 9, "n": 23, "k": 13, "tile_m": 6, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 9, "algorithm": "medium", "perf": 1146.44, "source": "autotuned"}, +{"m": 9, "n": 23, "k": 16, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1186.48, "source": "autotuned"}, +{"m": 9, "n": 23, "k": 17, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1153.25, "source": "autotuned"}, +{"m": 9, "n": 23, "k": 22, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 1190.96, "source": "autotuned"}, +{"m": 9, "n": 23, "k": 23, "tile_m": 9, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 4, "algorithm": "medium", "perf": 1177.76, "source": "autotuned"}, +{"m": 9, "n": 23, "k": 26, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 1194.34, "source": "autotuned"}, +{"m": 9, "n": 26, "k": 4, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 23, "algorithm": "medium", "perf": 1049.02, "source": "autotuned"}, +{"m": 9, "n": 26, "k": 5, "tile_m": 5, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 22, "algorithm": "medium", "perf": 1062.13, "source": "autotuned"}, +{"m": 9, "n": 26, "k": 9, "tile_m": 5, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 16, "algorithm": "medium", "perf": 1135.3, "source": "autotuned"}, +{"m": 9, "n": 26, "k": 13, "tile_m": 9, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1180.7, "source": "autotuned"}, +{"m": 9, "n": 26, "k": 22, "tile_m": 5, "tile_n": 1, "threads": 224, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 1227.07, "source": "autotuned"}, +{"m": 9, "n": 26, "k": 23, "tile_m": 5, "tile_n": 2, "threads": 32, "grouping": 2, "minblocks": 3, "algorithm": "medium", "perf": 1223.56, "source": "autotuned"}, +{"m": 9, "n": 26, "k": 26, "tile_m": 5, "tile_n": 1, "threads": 256, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1246.86, "source": "autotuned"}, +{"m": 10, "n": 10, "k": 10, "tile_m": 5, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 27, "algorithm": "medium", "perf": 977.584, "source": "autotuned"}, +{"m": 10, "n": 10, "k": 32, "tile_m": 1, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1027.72, "source": "autotuned"}, +{"m": 10, "n": 12, "k": 10, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 4, "minblocks": 14, "algorithm": "medium", "perf": 1050.75, "source": "autotuned"}, +{"m": 10, "n": 15, "k": 10, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 4, "minblocks": 16, "algorithm": "medium", "perf": 1071.76, "source": "autotuned"}, +{"m": 10, "n": 15, "k": 15, "tile_m": 1, "tile_n": 2, "threads": 160, "grouping": 5, "minblocks": 9, "algorithm": "medium", "perf": 1088.77, "source": "autotuned"}, +{"m": 11, "n": 11, "k": 11, "tile_m": 6, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 26, "algorithm": "medium", "perf": 1022.85, "source": "autotuned"}, +{"m": 12, "n": 12, "k": 12, "tile_m": 6, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 19, "algorithm": "medium", "perf": 1148.64, "source": "autotuned"}, +{"m": 13, "n": 4, "k": 4, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 8, "minblocks": 26, "algorithm": "medium", "perf": 538.053, "source": "autotuned"}, +{"m": 13, "n": 4, "k": 5, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 9, "minblocks": 1, "algorithm": "medium", "perf": 580.215, "source": "autotuned"}, +{"m": 13, "n": 4, "k": 9, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 25, "algorithm": "medium", "perf": 646.473, "source": "autotuned"}, +{"m": 13, "n": 4, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 21, "algorithm": "medium", "perf": 636.567, "source": "autotuned"}, +{"m": 13, "n": 4, "k": 22, "tile_m": 1, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 15, "algorithm": "medium", "perf": 632.137, "source": "autotuned"}, +{"m": 13, "n": 4, "k": 23, "tile_m": 2, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 625.163, "source": "autotuned"}, +{"m": 13, "n": 4, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 2, "minblocks": 15, "algorithm": "medium", "perf": 632.236, "source": "autotuned"}, +{"m": 13, "n": 5, "k": 4, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 10, "minblocks": 19, "algorithm": "medium", "perf": 601.518, "source": "autotuned"}, +{"m": 13, "n": 5, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 10, "minblocks": 27, "algorithm": "medium", "perf": 657.575, "source": "autotuned"}, +{"m": 13, "n": 5, "k": 9, "tile_m": 2, "tile_n": 2, "threads": 64, "grouping": 4, "minblocks": 22, "algorithm": "medium", "perf": 712.892, "source": "autotuned"}, +{"m": 13, "n": 5, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 96, "grouping": 3, "minblocks": 15, "algorithm": "medium", "perf": 702.088, "source": "autotuned"}, +{"m": 13, "n": 5, "k": 16, "tile_m": 1, "tile_n": 3, "threads": 96, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 738.124, "source": "autotuned"}, +{"m": 13, "n": 5, "k": 22, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 2, "minblocks": 13, "algorithm": "medium", "perf": 719.495, "source": "autotuned"}, +{"m": 13, "n": 5, "k": 23, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 2, "minblocks": 15, "algorithm": "medium", "perf": 713.614, "source": "autotuned"}, +{"m": 13, "n": 5, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 712.358, "source": "autotuned"}, +{"m": 13, "n": 9, "k": 4, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 19, "algorithm": "medium", "perf": 877.374, "source": "autotuned"}, +{"m": 13, "n": 9, "k": 5, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 20, "algorithm": "medium", "perf": 879.219, "source": "autotuned"}, +{"m": 13, "n": 9, "k": 9, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 14, "algorithm": "medium", "perf": 970.648, "source": "autotuned"}, +{"m": 13, "n": 9, "k": 13, "tile_m": 2, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 15, "algorithm": "medium", "perf": 972.888, "source": "autotuned"}, +{"m": 13, "n": 9, "k": 16, "tile_m": 2, "tile_n": 1, "threads": 224, "grouping": 5, "minblocks": 7, "algorithm": "medium", "perf": 1001.38, "source": "autotuned"}, +{"m": 13, "n": 9, "k": 22, "tile_m": 2, "tile_n": 1, "threads": 224, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 978.269, "source": "autotuned"}, +{"m": 13, "n": 9, "k": 23, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 15, "algorithm": "medium", "perf": 981.528, "source": "autotuned"}, +{"m": 13, "n": 9, "k": 26, "tile_m": 1, "tile_n": 1, "threads": 256, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 990.302, "source": "autotuned"}, +{"m": 13, "n": 13, "k": 4, "tile_m": 8, "tile_n": 1, "threads": 64, "grouping": 9, "minblocks": 24, "algorithm": "medium", "perf": 1111.68, "source": "autotuned"}, +{"m": 13, "n": 13, "k": 5, "tile_m": 8, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 22, "algorithm": "medium", "perf": 1109.06, "source": "autotuned"}, +{"m": 13, "n": 13, "k": 9, "tile_m": 8, "tile_n": 1, "threads": 32, "grouping": 9, "minblocks": 22, "algorithm": "medium", "perf": 1135.75, "source": "autotuned"}, +{"m": 13, "n": 13, "k": 13, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 1164.52, "source": "autotuned"}, +{"m": 13, "n": 13, "k": 16, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1230.31, "source": "autotuned"}, +{"m": 13, "n": 13, "k": 17, "tile_m": 4, "tile_n": 2, "threads": 32, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 1199.08, "source": "autotuned"}, +{"m": 13, "n": 13, "k": 22, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1215.28, "source": "autotuned"}, +{"m": 13, "n": 13, "k": 23, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1223.86, "source": "autotuned"}, +{"m": 13, "n": 13, "k": 26, "tile_m": 7, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1220.15, "source": "autotuned"}, +{"m": 13, "n": 16, "k": 5, "tile_m": 7, "tile_n": 1, "threads": 96, "grouping": 13, "minblocks": 14, "algorithm": "medium", "perf": 1169.67, "source": "autotuned"}, +{"m": 13, "n": 16, "k": 9, "tile_m": 7, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 25, "algorithm": "medium", "perf": 1217.94, "source": "autotuned"}, +{"m": 13, "n": 16, "k": 13, "tile_m": 7, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 19, "algorithm": "medium", "perf": 1261.88, "source": "autotuned"}, +{"m": 13, "n": 16, "k": 16, "tile_m": 7, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 19, "algorithm": "medium", "perf": 1316.52, "source": "autotuned"}, +{"m": 13, "n": 16, "k": 23, "tile_m": 7, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 18, "algorithm": "medium", "perf": 1333.01, "source": "autotuned"}, +{"m": 13, "n": 17, "k": 13, "tile_m": 1, "tile_n": 2, "threads": 224, "grouping": 4, "minblocks": 9, "algorithm": "medium", "perf": 1240.09, "source": "autotuned"}, +{"m": 13, "n": 17, "k": 17, "tile_m": 1, "tile_n": 2, "threads": 224, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1269.37, "source": "autotuned"}, +{"m": 13, "n": 17, "k": 23, "tile_m": 4, "tile_n": 3, "threads": 32, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 1303.07, "source": "autotuned"}, +{"m": 13, "n": 22, "k": 4, "tile_m": 8, "tile_n": 1, "threads": 96, "grouping": 13, "minblocks": 14, "algorithm": "medium", "perf": 1203.83, "source": "autotuned"}, +{"m": 13, "n": 22, "k": 5, "tile_m": 7, "tile_n": 1, "threads": 128, "grouping": 6, "minblocks": 12, "algorithm": "medium", "perf": 1187.22, "source": "autotuned"}, +{"m": 13, "n": 22, "k": 9, "tile_m": 2, "tile_n": 2, "threads": 128, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1308.67, "source": "autotuned"}, +{"m": 13, "n": 22, "k": 13, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 1346.22, "source": "autotuned"}, +{"m": 13, "n": 22, "k": 22, "tile_m": 4, "tile_n": 3, "threads": 32, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 1453.46, "source": "autotuned"}, +{"m": 13, "n": 22, "k": 23, "tile_m": 4, "tile_n": 3, "threads": 32, "grouping": 2, "minblocks": 2, "algorithm": "medium", "perf": 1457.16, "source": "autotuned"}, +{"m": 13, "n": 22, "k": 26, "tile_m": 4, "tile_n": 3, "threads": 32, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 1480.46, "source": "autotuned"}, +{"m": 13, "n": 23, "k": 4, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 10, "minblocks": 19, "algorithm": "medium", "perf": 1197.81, "source": "autotuned"}, +{"m": 13, "n": 23, "k": 5, "tile_m": 7, "tile_n": 1, "threads": 128, "grouping": 6, "minblocks": 12, "algorithm": "medium", "perf": 1214.22, "source": "autotuned"}, +{"m": 13, "n": 23, "k": 9, "tile_m": 7, "tile_n": 1, "threads": 128, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 1310.86, "source": "autotuned"}, +{"m": 13, "n": 23, "k": 13, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 1362.06, "source": "autotuned"}, +{"m": 13, "n": 23, "k": 16, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 1447.81, "source": "autotuned"}, +{"m": 13, "n": 23, "k": 17, "tile_m": 4, "tile_n": 3, "threads": 32, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1430.26, "source": "autotuned"}, +{"m": 13, "n": 23, "k": 22, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1470.51, "source": "autotuned"}, +{"m": 13, "n": 23, "k": 23, "tile_m": 4, "tile_n": 3, "threads": 32, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 1478.54, "source": "autotuned"}, +{"m": 13, "n": 23, "k": 26, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 9, "algorithm": "medium", "perf": 1500.66, "source": "autotuned"}, +{"m": 13, "n": 26, "k": 4, "tile_m": 8, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 17, "algorithm": "medium", "perf": 1215.84, "source": "autotuned"}, +{"m": 13, "n": 26, "k": 5, "tile_m": 7, "tile_n": 1, "threads": 96, "grouping": 6, "minblocks": 11, "algorithm": "medium", "perf": 1225.27, "source": "autotuned"}, +{"m": 13, "n": 26, "k": 9, "tile_m": 2, "tile_n": 2, "threads": 128, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1382.06, "source": "autotuned"}, +{"m": 13, "n": 26, "k": 13, "tile_m": 7, "tile_n": 1, "threads": 192, "grouping": 4, "minblocks": 6, "algorithm": "medium", "perf": 1417.56, "source": "autotuned"}, +{"m": 13, "n": 26, "k": 22, "tile_m": 7, "tile_n": 2, "threads": 32, "grouping": 2, "minblocks": 2, "algorithm": "medium", "perf": 1536.42, "source": "autotuned"}, +{"m": 13, "n": 26, "k": 23, "tile_m": 7, "tile_n": 2, "threads": 32, "grouping": 2, "minblocks": 11, "algorithm": "medium", "perf": 1536.74, "source": "autotuned"}, +{"m": 13, "n": 26, "k": 26, "tile_m": 7, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 1558.37, "source": "autotuned"}, +{"m": 14, "n": 14, "k": 14, "tile_m": 7, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 16, "algorithm": "medium", "perf": 1285.95, "source": "autotuned"}, +{"m": 15, "n": 15, "k": 15, "tile_m": 2, "tile_n": 4, "threads": 32, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 1348.44, "source": "autotuned"}, +{"m": 16, "n": 5, "k": 5, "tile_m": 1, "tile_n": 3, "threads": 32, "grouping": 8, "minblocks": 26, "algorithm": "medium", "perf": 796.959, "source": "autotuned"}, +{"m": 16, "n": 5, "k": 9, "tile_m": 1, "tile_n": 3, "threads": 64, "grouping": 4, "minblocks": 22, "algorithm": "medium", "perf": 780.707, "source": "autotuned"}, +{"m": 16, "n": 5, "k": 13, "tile_m": 1, "tile_n": 1, "threads": 96, "grouping": 2, "minblocks": 19, "algorithm": "medium", "perf": 767.06, "source": "autotuned"}, +{"m": 16, "n": 5, "k": 16, "tile_m": 1, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 789.126, "source": "autotuned"}, +{"m": 16, "n": 5, "k": 23, "tile_m": 1, "tile_n": 1, "threads": 128, "grouping": 2, "minblocks": 13, "algorithm": "medium", "perf": 781.229, "source": "autotuned"}, +{"m": 16, "n": 9, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 64, "grouping": 9, "minblocks": 22, "algorithm": "medium", "perf": 1052.33, "source": "autotuned"}, +{"m": 16, "n": 9, "k": 9, "tile_m": 1, "tile_n": 3, "threads": 96, "grouping": 5, "minblocks": 14, "algorithm": "medium", "perf": 1060.21, "source": "autotuned"}, +{"m": 16, "n": 9, "k": 13, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 1071.99, "source": "autotuned"}, +{"m": 16, "n": 9, "k": 16, "tile_m": 1, "tile_n": 2, "threads": 160, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1104.43, "source": "autotuned"}, +{"m": 16, "n": 9, "k": 17, "tile_m": 1, "tile_n": 2, "threads": 160, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1089.7, "source": "autotuned"}, +{"m": 16, "n": 9, "k": 22, "tile_m": 3, "tile_n": 2, "threads": 32, "grouping": 3, "minblocks": 18, "algorithm": "medium", "perf": 1097.3, "source": "autotuned"}, +{"m": 16, "n": 9, "k": 23, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 1089.58, "source": "autotuned"}, +{"m": 16, "n": 13, "k": 5, "tile_m": 1, "tile_n": 4, "threads": 96, "grouping": 12, "minblocks": 19, "algorithm": "medium", "perf": 1184.66, "source": "autotuned"}, +{"m": 16, "n": 13, "k": 9, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 4, "minblocks": 14, "algorithm": "medium", "perf": 1239.17, "source": "autotuned"}, +{"m": 16, "n": 13, "k": 13, "tile_m": 1, "tile_n": 2, "threads": 224, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1283.89, "source": "autotuned"}, +{"m": 16, "n": 13, "k": 16, "tile_m": 2, "tile_n": 4, "threads": 32, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1315.31, "source": "autotuned"}, +{"m": 16, "n": 13, "k": 23, "tile_m": 1, "tile_n": 7, "threads": 32, "grouping": 2, "minblocks": 17, "algorithm": "medium", "perf": 1319.04, "source": "autotuned"}, +{"m": 16, "n": 16, "k": 5, "tile_m": 1, "tile_n": 8, "threads": 32, "grouping": 8, "minblocks": 27, "algorithm": "medium", "perf": 1305.79, "source": "autotuned"}, +{"m": 16, "n": 16, "k": 9, "tile_m": 1, "tile_n": 8, "threads": 32, "grouping": 3, "minblocks": 26, "algorithm": "medium", "perf": 1397.65, "source": "autotuned"}, +{"m": 16, "n": 16, "k": 13, "tile_m": 2, "tile_n": 4, "threads": 32, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1449.51, "source": "autotuned"}, +{"m": 16, "n": 16, "k": 16, "tile_m": 1, "tile_n": 4, "threads": 128, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1523.53, "source": "autotuned"}, +{"m": 16, "n": 16, "k": 17, "tile_m": 2, "tile_n": 2, "threads": 64, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1482.75, "source": "autotuned"}, +{"m": 16, "n": 16, "k": 22, "tile_m": 1, "tile_n": 4, "threads": 128, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1529.11, "source": "autotuned"}, +{"m": 16, "n": 16, "k": 23, "tile_m": 2, "tile_n": 2, "threads": 64, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 1515.27, "source": "autotuned"}, +{"m": 16, "n": 17, "k": 9, "tile_m": 1, "tile_n": 3, "threads": 160, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1368.03, "source": "autotuned"}, +{"m": 16, "n": 17, "k": 16, "tile_m": 1, "tile_n": 9, "threads": 32, "grouping": 2, "minblocks": 19, "algorithm": "medium", "perf": 1493.26, "source": "autotuned"}, +{"m": 16, "n": 17, "k": 17, "tile_m": 1, "tile_n": 9, "threads": 32, "grouping": 2, "minblocks": 13, "algorithm": "medium", "perf": 1443.65, "source": "autotuned"}, +{"m": 16, "n": 17, "k": 22, "tile_m": 2, "tile_n": 5, "threads": 32, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 1498.59, "source": "autotuned"}, +{"m": 16, "n": 17, "k": 23, "tile_m": 2, "tile_n": 5, "threads": 32, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 1490.94, "source": "autotuned"}, +{"m": 16, "n": 22, "k": 9, "tile_m": 1, "tile_n": 3, "threads": 224, "grouping": 5, "minblocks": 8, "algorithm": "medium", "perf": 1476.24, "source": "autotuned"}, +{"m": 16, "n": 22, "k": 16, "tile_m": 1, "tile_n": 6, "threads": 64, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1642.34, "source": "autotuned"}, +{"m": 16, "n": 22, "k": 17, "tile_m": 2, "tile_n": 3, "threads": 96, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1601.36, "source": "autotuned"}, +{"m": 16, "n": 22, "k": 22, "tile_m": 2, "tile_n": 3, "threads": 64, "grouping": 2, "minblocks": 11, "algorithm": "medium", "perf": 1678.29, "source": "autotuned"}, +{"m": 16, "n": 22, "k": 23, "tile_m": 2, "tile_n": 6, "threads": 32, "grouping": 2, "minblocks": 4, "algorithm": "medium", "perf": 1667.16, "source": "autotuned"}, +{"m": 16, "n": 23, "k": 5, "tile_m": 1, "tile_n": 3, "threads": 128, "grouping": 5, "minblocks": 16, "algorithm": "medium", "perf": 1387.02, "source": "autotuned"}, +{"m": 16, "n": 23, "k": 9, "tile_m": 1, "tile_n": 3, "threads": 160, "grouping": 5, "minblocks": 9, "algorithm": "medium", "perf": 1493.7, "source": "autotuned"}, +{"m": 16, "n": 23, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 224, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1582.33, "source": "autotuned"}, +{"m": 16, "n": 23, "k": 16, "tile_m": 1, "tile_n": 6, "threads": 64, "grouping": 2, "minblocks": 16, "algorithm": "medium", "perf": 1664.11, "source": "autotuned"}, +{"m": 16, "n": 23, "k": 17, "tile_m": 1, "tile_n": 2, "threads": 288, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1618.44, "source": "autotuned"}, +{"m": 16, "n": 23, "k": 22, "tile_m": 2, "tile_n": 6, "threads": 32, "grouping": 2, "minblocks": 9, "algorithm": "medium", "perf": 1695.08, "source": "autotuned"}, +{"m": 16, "n": 23, "k": 23, "tile_m": 4, "tile_n": 3, "threads": 32, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 1687.93, "source": "autotuned"}, +{"m": 17, "n": 9, "k": 9, "tile_m": 3, "tile_n": 1, "threads": 96, "grouping": 4, "minblocks": 15, "algorithm": "medium", "perf": 1017.05, "source": "autotuned"}, +{"m": 17, "n": 9, "k": 16, "tile_m": 3, "tile_n": 1, "threads": 160, "grouping": 6, "minblocks": 9, "algorithm": "medium", "perf": 1093.49, "source": "autotuned"}, +{"m": 17, "n": 9, "k": 17, "tile_m": 3, "tile_n": 1, "threads": 160, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1070.7, "source": "autotuned"}, +{"m": 17, "n": 9, "k": 22, "tile_m": 3, "tile_n": 2, "threads": 32, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1087.55, "source": "autotuned"}, +{"m": 17, "n": 9, "k": 23, "tile_m": 3, "tile_n": 2, "threads": 32, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 1093.2, "source": "autotuned"}, +{"m": 17, "n": 13, "k": 13, "tile_m": 3, "tile_n": 1, "threads": 224, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1246.95, "source": "autotuned"}, +{"m": 17, "n": 13, "k": 17, "tile_m": 9, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 20, "algorithm": "medium", "perf": 1287.34, "source": "autotuned"}, +{"m": 17, "n": 13, "k": 23, "tile_m": 9, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 15, "algorithm": "medium", "perf": 1325.03, "source": "autotuned"}, +{"m": 17, "n": 16, "k": 9, "tile_m": 9, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 26, "algorithm": "medium", "perf": 1370.02, "source": "autotuned"}, +{"m": 17, "n": 16, "k": 16, "tile_m": 9, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 19, "algorithm": "medium", "perf": 1466.93, "source": "autotuned"}, +{"m": 17, "n": 16, "k": 17, "tile_m": 9, "tile_n": 1, "threads": 32, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 1452.87, "source": "autotuned"}, +{"m": 17, "n": 16, "k": 22, "tile_m": 9, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 14, "algorithm": "medium", "perf": 1490.38, "source": "autotuned"}, +{"m": 17, "n": 16, "k": 23, "tile_m": 9, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 15, "algorithm": "medium", "perf": 1493.66, "source": "autotuned"}, +{"m": 17, "n": 17, "k": 9, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 13, "minblocks": 4, "algorithm": "medium", "perf": 1368.12, "source": "autotuned"}, +{"m": 17, "n": 17, "k": 13, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1446.94, "source": "autotuned"}, +{"m": 17, "n": 17, "k": 16, "tile_m": 3, "tile_n": 2, "threads": 64, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1524.54, "source": "autotuned"}, +{"m": 17, "n": 17, "k": 17, "tile_m": 3, "tile_n": 2, "threads": 64, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1499.4, "source": "autotuned"}, +{"m": 17, "n": 17, "k": 22, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1536.35, "source": "autotuned"}, +{"m": 17, "n": 17, "k": 23, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1539.49, "source": "autotuned"}, +{"m": 17, "n": 22, "k": 9, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1429.34, "source": "autotuned"}, +{"m": 17, "n": 22, "k": 16, "tile_m": 5, "tile_n": 3, "threads": 32, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 1638.17, "source": "autotuned"}, +{"m": 17, "n": 22, "k": 17, "tile_m": 9, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 11, "algorithm": "medium", "perf": 1606.06, "source": "autotuned"}, +{"m": 17, "n": 22, "k": 22, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 2, "minblocks": 9, "algorithm": "medium", "perf": 1673.45, "source": "autotuned"}, +{"m": 17, "n": 22, "k": 23, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 2, "minblocks": 5, "algorithm": "medium", "perf": 1680.38, "source": "autotuned"}, +{"m": 17, "n": 23, "k": 9, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1457.2, "source": "autotuned"}, +{"m": 17, "n": 23, "k": 13, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1557.3, "source": "autotuned"}, +{"m": 17, "n": 23, "k": 16, "tile_m": 10, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1659.79, "source": "autotuned"}, +{"m": 17, "n": 23, "k": 17, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 1632.08, "source": "autotuned"}, +{"m": 17, "n": 23, "k": 22, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 2, "minblocks": 4, "algorithm": "medium", "perf": 1699.3, "source": "autotuned"}, +{"m": 17, "n": 23, "k": 23, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 2, "minblocks": 12, "algorithm": "medium", "perf": 1707.42, "source": "autotuned"}, +{"m": 18, "n": 18, "k": 18, "tile_m": 3, "tile_n": 2, "threads": 64, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 1597.87, "source": "autotuned"}, +{"m": 19, "n": 19, "k": 19, "tile_m": 2, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1648.65, "source": "autotuned"}, +{"m": 20, "n": 11, "k": 11, "tile_m": 1, "tile_n": 3, "threads": 128, "grouping": 4, "minblocks": 12, "algorithm": "medium", "perf": 1224.09, "source": "autotuned"}, +{"m": 20, "n": 11, "k": 12, "tile_m": 1, "tile_n": 2, "threads": 160, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1236.34, "source": "autotuned"}, +{"m": 20, "n": 11, "k": 20, "tile_m": 1, "tile_n": 3, "threads": 224, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1297.58, "source": "autotuned"}, +{"m": 20, "n": 11, "k": 25, "tile_m": 1, "tile_n": 2, "threads": 288, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1299.21, "source": "autotuned"}, +{"m": 20, "n": 11, "k": 32, "tile_m": 2, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 5, "algorithm": "medium", "perf": 1369.72, "source": "autotuned"}, +{"m": 20, "n": 12, "k": 11, "tile_m": 1, "tile_n": 4, "threads": 224, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1264.76, "source": "autotuned"}, +{"m": 20, "n": 12, "k": 12, "tile_m": 1, "tile_n": 3, "threads": 160, "grouping": 4, "minblocks": 9, "algorithm": "medium", "perf": 1309.5, "source": "autotuned"}, +{"m": 20, "n": 12, "k": 20, "tile_m": 1, "tile_n": 2, "threads": 256, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1376.58, "source": "autotuned"}, +{"m": 20, "n": 12, "k": 25, "tile_m": 10, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 15, "algorithm": "medium", "perf": 1369.04, "source": "autotuned"}, +{"m": 20, "n": 12, "k": 32, "tile_m": 2, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 1446.23, "source": "autotuned"}, +{"m": 20, "n": 20, "k": 11, "tile_m": 1, "tile_n": 8, "threads": 64, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 1630.94, "source": "autotuned"}, +{"m": 20, "n": 20, "k": 20, "tile_m": 1, "tile_n": 7, "threads": 64, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1792.94, "source": "autotuned"}, +{"m": 20, "n": 20, "k": 25, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 1820.96, "source": "autotuned"}, +{"m": 20, "n": 20, "k": 32, "tile_m": 1, "tile_n": 7, "threads": 64, "grouping": 2, "minblocks": 2, "algorithm": "medium", "perf": 1926.77, "source": "autotuned"}, +{"m": 20, "n": 25, "k": 11, "tile_m": 10, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 14, "algorithm": "medium", "perf": 1702.26, "source": "autotuned"}, +{"m": 20, "n": 25, "k": 12, "tile_m": 1, "tile_n": 9, "threads": 64, "grouping": 3, "minblocks": 14, "algorithm": "medium", "perf": 1747.44, "source": "autotuned"}, +{"m": 20, "n": 25, "k": 20, "tile_m": 1, "tile_n": 9, "threads": 64, "grouping": 2, "minblocks": 9, "algorithm": "medium", "perf": 1894.53, "source": "autotuned"}, +{"m": 20, "n": 25, "k": 25, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1928.03, "source": "autotuned"}, +{"m": 20, "n": 25, "k": 32, "tile_m": 1, "tile_n": 5, "threads": 160, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 2044.24, "source": "autotuned"}, +{"m": 20, "n": 32, "k": 12, "tile_m": 1, "tile_n": 4, "threads": 256, "grouping": 5, "minblocks": 5, "algorithm": "medium", "perf": 1962.47, "source": "autotuned"}, +{"m": 20, "n": 32, "k": 20, "tile_m": 10, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 2132.1, "source": "autotuned"}, +{"m": 20, "n": 32, "k": 25, "tile_m": 10, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2171.45, "source": "autotuned"}, +{"m": 20, "n": 32, "k": 32, "tile_m": 10, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 2308.41, "source": "autotuned"}, +{"m": 21, "n": 21, "k": 21, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 1831.9, "source": "autotuned"}, +{"m": 22, "n": 4, "k": 4, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 8, "minblocks": 15, "algorithm": "medium", "perf": 711.621, "source": "autotuned"}, +{"m": 22, "n": 4, "k": 5, "tile_m": 3, "tile_n": 1, "threads": 32, "grouping": 4, "minblocks": 6, "algorithm": "medium", "perf": 688.022, "source": "autotuned"}, +{"m": 22, "n": 4, "k": 9, "tile_m": 1, "tile_n": 4, "threads": 64, "grouping": 4, "minblocks": 20, "algorithm": "medium", "perf": 675.403, "source": "autotuned"}, +{"m": 22, "n": 4, "k": 13, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 19, "algorithm": "medium", "perf": 683.239, "source": "autotuned"}, +{"m": 22, "n": 4, "k": 22, "tile_m": 3, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 693.792, "source": "autotuned"}, +{"m": 22, "n": 4, "k": 23, "tile_m": 1, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 681.694, "source": "autotuned"}, +{"m": 22, "n": 4, "k": 26, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 2, "minblocks": 10, "algorithm": "medium", "perf": 689.746, "source": "autotuned"}, +{"m": 22, "n": 5, "k": 4, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 9, "minblocks": 27, "algorithm": "medium", "perf": 784.759, "source": "autotuned"}, +{"m": 22, "n": 5, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 4, "minblocks": 27, "algorithm": "medium", "perf": 787.494, "source": "autotuned"}, +{"m": 22, "n": 5, "k": 9, "tile_m": 1, "tile_n": 5, "threads": 64, "grouping": 3, "minblocks": 20, "algorithm": "medium", "perf": 784.145, "source": "autotuned"}, +{"m": 22, "n": 5, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 96, "grouping": 3, "minblocks": 15, "algorithm": "medium", "perf": 781.451, "source": "autotuned"}, +{"m": 22, "n": 5, "k": 22, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 807.134, "source": "autotuned"}, +{"m": 22, "n": 5, "k": 23, "tile_m": 1, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 803.426, "source": "autotuned"}, +{"m": 22, "n": 5, "k": 26, "tile_m": 1, "tile_n": 3, "threads": 160, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 801.203, "source": "autotuned"}, +{"m": 22, "n": 9, "k": 4, "tile_m": 1, "tile_n": 9, "threads": 64, "grouping": 13, "minblocks": 17, "algorithm": "medium", "perf": 1024.46, "source": "autotuned"}, +{"m": 22, "n": 9, "k": 5, "tile_m": 1, "tile_n": 9, "threads": 64, "grouping": 15, "minblocks": 18, "algorithm": "medium", "perf": 1018.92, "source": "autotuned"}, +{"m": 22, "n": 9, "k": 9, "tile_m": 1, "tile_n": 3, "threads": 96, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 1090.92, "source": "autotuned"}, +{"m": 22, "n": 9, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 128, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 1141.06, "source": "autotuned"}, +{"m": 22, "n": 9, "k": 16, "tile_m": 1, "tile_n": 3, "threads": 160, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1169.51, "source": "autotuned"}, +{"m": 22, "n": 9, "k": 17, "tile_m": 1, "tile_n": 3, "threads": 160, "grouping": 4, "minblocks": 9, "algorithm": "medium", "perf": 1142.02, "source": "autotuned"}, +{"m": 22, "n": 9, "k": 22, "tile_m": 1, "tile_n": 3, "threads": 256, "grouping": 4, "minblocks": 6, "algorithm": "medium", "perf": 1178.83, "source": "autotuned"}, +{"m": 22, "n": 9, "k": 23, "tile_m": 1, "tile_n": 2, "threads": 224, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 1170.96, "source": "autotuned"}, +{"m": 22, "n": 9, "k": 26, "tile_m": 1, "tile_n": 2, "threads": 256, "grouping": 4, "minblocks": 6, "algorithm": "medium", "perf": 1190.75, "source": "autotuned"}, +{"m": 22, "n": 9, "k": 32, "tile_m": 3, "tile_n": 3, "threads": 32, "grouping": 2, "minblocks": 9, "algorithm": "medium", "perf": 1243.07, "source": "autotuned"}, +{"m": 22, "n": 13, "k": 4, "tile_m": 11, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 15, "algorithm": "medium", "perf": 1203.83, "source": "autotuned"}, +{"m": 22, "n": 13, "k": 5, "tile_m": 1, "tile_n": 4, "threads": 128, "grouping": 6, "minblocks": 11, "algorithm": "medium", "perf": 1199.03, "source": "autotuned"}, +{"m": 22, "n": 13, "k": 9, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1316.36, "source": "autotuned"}, +{"m": 22, "n": 13, "k": 13, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1361.0, "source": "autotuned"}, +{"m": 22, "n": 13, "k": 22, "tile_m": 1, "tile_n": 2, "threads": 288, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1459.53, "source": "autotuned"}, +{"m": 22, "n": 13, "k": 23, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 3, "algorithm": "medium", "perf": 1465.16, "source": "autotuned"}, +{"m": 22, "n": 13, "k": 26, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 12, "algorithm": "medium", "perf": 1486.77, "source": "autotuned"}, +{"m": 22, "n": 16, "k": 9, "tile_m": 1, "tile_n": 4, "threads": 224, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1448.79, "source": "autotuned"}, +{"m": 22, "n": 16, "k": 16, "tile_m": 11, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 13, "algorithm": "medium", "perf": 1651.97, "source": "autotuned"}, +{"m": 22, "n": 16, "k": 17, "tile_m": 11, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 3, "algorithm": "medium", "perf": 1592.98, "source": "autotuned"}, +{"m": 22, "n": 16, "k": 22, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 11, "algorithm": "medium", "perf": 1671.89, "source": "autotuned"}, +{"m": 22, "n": 16, "k": 23, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 1664.08, "source": "autotuned"}, +{"m": 22, "n": 17, "k": 9, "tile_m": 1, "tile_n": 4, "threads": 160, "grouping": 5, "minblocks": 9, "algorithm": "medium", "perf": 1456.55, "source": "autotuned"}, +{"m": 22, "n": 17, "k": 16, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1635.33, "source": "autotuned"}, +{"m": 22, "n": 17, "k": 17, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1595.45, "source": "autotuned"}, +{"m": 22, "n": 17, "k": 22, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 2, "minblocks": 9, "algorithm": "medium", "perf": 1675.6, "source": "autotuned"}, +{"m": 22, "n": 17, "k": 23, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 1681.7, "source": "autotuned"}, +{"m": 22, "n": 22, "k": 4, "tile_m": 11, "tile_n": 1, "threads": 96, "grouping": 6, "minblocks": 12, "algorithm": "medium", "perf": 1421.19, "source": "autotuned"}, +{"m": 22, "n": 22, "k": 5, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 5, "minblocks": 12, "algorithm": "medium", "perf": 1463.93, "source": "autotuned"}, +{"m": 22, "n": 22, "k": 9, "tile_m": 11, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 1663.75, "source": "autotuned"}, +{"m": 22, "n": 22, "k": 13, "tile_m": 1, "tile_n": 6, "threads": 96, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1800.33, "source": "autotuned"}, +{"m": 22, "n": 22, "k": 16, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1916.32, "source": "autotuned"}, +{"m": 22, "n": 22, "k": 17, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 1870.44, "source": "autotuned"}, +{"m": 22, "n": 22, "k": 22, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 2, "minblocks": 2, "algorithm": "medium", "perf": 1960.01, "source": "autotuned"}, +{"m": 22, "n": 22, "k": 23, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 1955.08, "source": "autotuned"}, +{"m": 22, "n": 22, "k": 26, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 1990.71, "source": "autotuned"}, +{"m": 22, "n": 22, "k": 32, "tile_m": 3, "tile_n": 3, "threads": 96, "grouping": 2, "minblocks": 5, "algorithm": "medium", "perf": 2080.88, "source": "autotuned"}, +{"m": 22, "n": 23, "k": 4, "tile_m": 1, "tile_n": 7, "threads": 96, "grouping": 5, "minblocks": 13, "algorithm": "medium", "perf": 1421.46, "source": "autotuned"}, +{"m": 22, "n": 23, "k": 5, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 6, "minblocks": 10, "algorithm": "medium", "perf": 1484.33, "source": "autotuned"}, +{"m": 22, "n": 23, "k": 9, "tile_m": 1, "tile_n": 3, "threads": 224, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1662.16, "source": "autotuned"}, +{"m": 22, "n": 23, "k": 13, "tile_m": 1, "tile_n": 6, "threads": 96, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1770.26, "source": "autotuned"}, +{"m": 22, "n": 23, "k": 16, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1877.7, "source": "autotuned"}, +{"m": 22, "n": 23, "k": 17, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1852.86, "source": "autotuned"}, +{"m": 22, "n": 23, "k": 22, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1926.55, "source": "autotuned"}, +{"m": 22, "n": 23, "k": 23, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 2, "minblocks": 9, "algorithm": "medium", "perf": 1934.26, "source": "autotuned"}, +{"m": 22, "n": 23, "k": 26, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 1969.68, "source": "autotuned"}, +{"m": 22, "n": 26, "k": 4, "tile_m": 1, "tile_n": 7, "threads": 96, "grouping": 5, "minblocks": 13, "algorithm": "medium", "perf": 1462.44, "source": "autotuned"}, +{"m": 22, "n": 26, "k": 5, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 5, "minblocks": 10, "algorithm": "medium", "perf": 1534.81, "source": "autotuned"}, +{"m": 22, "n": 26, "k": 9, "tile_m": 1, "tile_n": 4, "threads": 256, "grouping": 6, "minblocks": 6, "algorithm": "medium", "perf": 1747.8, "source": "autotuned"}, +{"m": 22, "n": 26, "k": 13, "tile_m": 11, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1869.4, "source": "autotuned"}, +{"m": 22, "n": 26, "k": 22, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2021.4, "source": "autotuned"}, +{"m": 22, "n": 26, "k": 23, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2024.94, "source": "autotuned"}, +{"m": 22, "n": 26, "k": 26, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2073.98, "source": "autotuned"}, +{"m": 22, "n": 32, "k": 9, "tile_m": 1, "tile_n": 5, "threads": 224, "grouping": 4, "minblocks": 6, "algorithm": "medium", "perf": 1904.44, "source": "autotuned"}, +{"m": 22, "n": 32, "k": 22, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 2244.91, "source": "autotuned"}, +{"m": 22, "n": 32, "k": 32, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 2417.23, "source": "autotuned"}, +{"m": 23, "n": 4, "k": 4, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 8, "minblocks": 8, "algorithm": "medium", "perf": 697.748, "source": "autotuned"}, +{"m": 23, "n": 4, "k": 5, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 4, "minblocks": 27, "algorithm": "medium", "perf": 692.1, "source": "autotuned"}, +{"m": 23, "n": 4, "k": 9, "tile_m": 1, "tile_n": 4, "threads": 64, "grouping": 4, "minblocks": 17, "algorithm": "medium", "perf": 675.284, "source": "autotuned"}, +{"m": 23, "n": 4, "k": 13, "tile_m": 3, "tile_n": 1, "threads": 64, "grouping": 2, "minblocks": 20, "algorithm": "medium", "perf": 685.798, "source": "autotuned"}, +{"m": 23, "n": 4, "k": 22, "tile_m": 3, "tile_n": 1, "threads": 96, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 689.119, "source": "autotuned"}, +{"m": 23, "n": 4, "k": 23, "tile_m": 1, "tile_n": 4, "threads": 96, "grouping": 2, "minblocks": 12, "algorithm": "medium", "perf": 685.403, "source": "autotuned"}, +{"m": 23, "n": 4, "k": 26, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 692.026, "source": "autotuned"}, +{"m": 23, "n": 5, "k": 4, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 10, "minblocks": 9, "algorithm": "medium", "perf": 765.819, "source": "autotuned"}, +{"m": 23, "n": 5, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 5, "minblocks": 26, "algorithm": "medium", "perf": 770.232, "source": "autotuned"}, +{"m": 23, "n": 5, "k": 9, "tile_m": 1, "tile_n": 5, "threads": 64, "grouping": 3, "minblocks": 19, "algorithm": "medium", "perf": 794.315, "source": "autotuned"}, +{"m": 23, "n": 5, "k": 13, "tile_m": 1, "tile_n": 5, "threads": 96, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 779.905, "source": "autotuned"}, +{"m": 23, "n": 5, "k": 16, "tile_m": 1, "tile_n": 5, "threads": 96, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 818.943, "source": "autotuned"}, +{"m": 23, "n": 5, "k": 22, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 2, "minblocks": 12, "algorithm": "medium", "perf": 803.171, "source": "autotuned"}, +{"m": 23, "n": 5, "k": 23, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 799.948, "source": "autotuned"}, +{"m": 23, "n": 5, "k": 26, "tile_m": 1, "tile_n": 5, "threads": 160, "grouping": 4, "minblocks": 6, "algorithm": "medium", "perf": 800.518, "source": "autotuned"}, +{"m": 23, "n": 9, "k": 4, "tile_m": 1, "tile_n": 9, "threads": 64, "grouping": 12, "minblocks": 17, "algorithm": "medium", "perf": 1023.81, "source": "autotuned"}, +{"m": 23, "n": 9, "k": 5, "tile_m": 12, "tile_n": 1, "threads": 64, "grouping": 13, "minblocks": 15, "algorithm": "medium", "perf": 1023.47, "source": "autotuned"}, +{"m": 23, "n": 9, "k": 9, "tile_m": 1, "tile_n": 9, "threads": 96, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1100.22, "source": "autotuned"}, +{"m": 23, "n": 9, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1131.5, "source": "autotuned"}, +{"m": 23, "n": 9, "k": 16, "tile_m": 3, "tile_n": 1, "threads": 192, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1178.32, "source": "autotuned"}, +{"m": 23, "n": 9, "k": 17, "tile_m": 2, "tile_n": 2, "threads": 160, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1150.36, "source": "autotuned"}, +{"m": 23, "n": 9, "k": 22, "tile_m": 3, "tile_n": 3, "threads": 32, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 1179.45, "source": "autotuned"}, +{"m": 23, "n": 9, "k": 23, "tile_m": 3, "tile_n": 3, "threads": 32, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 1176.02, "source": "autotuned"}, +{"m": 23, "n": 9, "k": 26, "tile_m": 3, "tile_n": 3, "threads": 32, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 1195.58, "source": "autotuned"}, +{"m": 23, "n": 13, "k": 4, "tile_m": 12, "tile_n": 1, "threads": 64, "grouping": 14, "minblocks": 15, "algorithm": "medium", "perf": 1177.19, "source": "autotuned"}, +{"m": 23, "n": 13, "k": 5, "tile_m": 2, "tile_n": 3, "threads": 128, "grouping": 6, "minblocks": 12, "algorithm": "medium", "perf": 1175.8, "source": "autotuned"}, +{"m": 23, "n": 13, "k": 9, "tile_m": 2, "tile_n": 2, "threads": 128, "grouping": 4, "minblocks": 12, "algorithm": "medium", "perf": 1317.59, "source": "autotuned"}, +{"m": 23, "n": 13, "k": 13, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 3, "minblocks": 16, "algorithm": "medium", "perf": 1370.54, "source": "autotuned"}, +{"m": 23, "n": 13, "k": 16, "tile_m": 3, "tile_n": 2, "threads": 64, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1458.25, "source": "autotuned"}, +{"m": 23, "n": 13, "k": 17, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1421.47, "source": "autotuned"}, +{"m": 23, "n": 13, "k": 22, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 1476.23, "source": "autotuned"}, +{"m": 23, "n": 13, "k": 23, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 5, "algorithm": "medium", "perf": 1468.44, "source": "autotuned"}, +{"m": 23, "n": 13, "k": 26, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 4, "algorithm": "medium", "perf": 1500.18, "source": "autotuned"}, +{"m": 23, "n": 16, "k": 5, "tile_m": 3, "tile_n": 1, "threads": 128, "grouping": 5, "minblocks": 13, "algorithm": "medium", "perf": 1345.94, "source": "autotuned"}, +{"m": 23, "n": 16, "k": 9, "tile_m": 2, "tile_n": 2, "threads": 224, "grouping": 4, "minblocks": 9, "algorithm": "medium", "perf": 1472.9, "source": "autotuned"}, +{"m": 23, "n": 16, "k": 13, "tile_m": 3, "tile_n": 1, "threads": 224, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1560.32, "source": "autotuned"}, +{"m": 23, "n": 16, "k": 16, "tile_m": 12, "tile_n": 1, "threads": 32, "grouping": 2, "minblocks": 15, "algorithm": "medium", "perf": 1649.82, "source": "autotuned"}, +{"m": 23, "n": 16, "k": 17, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 1621.65, "source": "autotuned"}, +{"m": 23, "n": 16, "k": 22, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 1694.73, "source": "autotuned"}, +{"m": 23, "n": 16, "k": 23, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 1691.64, "source": "autotuned"}, +{"m": 23, "n": 17, "k": 9, "tile_m": 2, "tile_n": 2, "threads": 224, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1469.57, "source": "autotuned"}, +{"m": 23, "n": 17, "k": 13, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 1559.69, "source": "autotuned"}, +{"m": 23, "n": 17, "k": 16, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 1653.36, "source": "autotuned"}, +{"m": 23, "n": 17, "k": 17, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1624.37, "source": "autotuned"}, +{"m": 23, "n": 17, "k": 22, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 1698.96, "source": "autotuned"}, +{"m": 23, "n": 17, "k": 23, "tile_m": 3, "tile_n": 5, "threads": 32, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 1701.82, "source": "autotuned"}, +{"m": 23, "n": 22, "k": 4, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 6, "minblocks": 13, "algorithm": "medium", "perf": 1410.89, "source": "autotuned"}, +{"m": 23, "n": 22, "k": 5, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 6, "minblocks": 9, "algorithm": "medium", "perf": 1455.1, "source": "autotuned"}, +{"m": 23, "n": 22, "k": 9, "tile_m": 2, "tile_n": 2, "threads": 224, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1654.71, "source": "autotuned"}, +{"m": 23, "n": 22, "k": 13, "tile_m": 12, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1752.81, "source": "autotuned"}, +{"m": 23, "n": 22, "k": 16, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1880.75, "source": "autotuned"}, +{"m": 23, "n": 22, "k": 17, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 1844.78, "source": "autotuned"}, +{"m": 23, "n": 22, "k": 22, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1925.66, "source": "autotuned"}, +{"m": 23, "n": 22, "k": 23, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 1921.8, "source": "autotuned"}, +{"m": 23, "n": 22, "k": 26, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 1986.83, "source": "autotuned"}, +{"m": 23, "n": 23, "k": 4, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 6, "minblocks": 4, "algorithm": "medium", "perf": 1404.74, "source": "autotuned"}, +{"m": 23, "n": 23, "k": 5, "tile_m": 2, "tile_n": 5, "threads": 64, "grouping": 5, "minblocks": 16, "algorithm": "medium", "perf": 1489.35, "source": "autotuned"}, +{"m": 23, "n": 23, "k": 9, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 1725.75, "source": "autotuned"}, +{"m": 23, "n": 23, "k": 13, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1867.64, "source": "autotuned"}, +{"m": 23, "n": 23, "k": 16, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 1969.45, "source": "autotuned"}, +{"m": 23, "n": 23, "k": 17, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1941.68, "source": "autotuned"}, +{"m": 23, "n": 23, "k": 22, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 2026.63, "source": "autotuned"}, +{"m": 23, "n": 23, "k": 23, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 2028.24, "source": "autotuned"}, +{"m": 23, "n": 23, "k": 26, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 2065.19, "source": "autotuned"}, +{"m": 23, "n": 26, "k": 4, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 6, "minblocks": 10, "algorithm": "medium", "perf": 1471.74, "source": "autotuned"}, +{"m": 23, "n": 26, "k": 5, "tile_m": 12, "tile_n": 1, "threads": 128, "grouping": 6, "minblocks": 8, "algorithm": "medium", "perf": 1522.69, "source": "autotuned"}, +{"m": 23, "n": 26, "k": 9, "tile_m": 2, "tile_n": 2, "threads": 256, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1743.61, "source": "autotuned"}, +{"m": 23, "n": 26, "k": 13, "tile_m": 12, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1884.43, "source": "autotuned"}, +{"m": 23, "n": 26, "k": 22, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 2067.9, "source": "autotuned"}, +{"m": 23, "n": 26, "k": 23, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2073.49, "source": "autotuned"}, +{"m": 23, "n": 26, "k": 26, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 2115.65, "source": "autotuned"}, +{"m": 24, "n": 5, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 4, "minblocks": 26, "algorithm": "medium", "perf": 796.427, "source": "autotuned"}, +{"m": 24, "n": 5, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 96, "grouping": 2, "minblocks": 14, "algorithm": "medium", "perf": 807.706, "source": "autotuned"}, +{"m": 24, "n": 5, "k": 24, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 835.528, "source": "autotuned"}, +{"m": 24, "n": 5, "k": 26, "tile_m": 1, "tile_n": 2, "threads": 160, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 823.042, "source": "autotuned"}, +{"m": 24, "n": 5, "k": 32, "tile_m": 1, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 841.384, "source": "autotuned"}, +{"m": 24, "n": 13, "k": 5, "tile_m": 1, "tile_n": 3, "threads": 128, "grouping": 5, "minblocks": 14, "algorithm": "medium", "perf": 1250.39, "source": "autotuned"}, +{"m": 24, "n": 13, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 192, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1424.6, "source": "autotuned"}, +{"m": 24, "n": 13, "k": 24, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 12, "algorithm": "medium", "perf": 1553.58, "source": "autotuned"}, +{"m": 24, "n": 13, "k": 26, "tile_m": 3, "tile_n": 4, "threads": 32, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 1549.36, "source": "autotuned"}, +{"m": 24, "n": 13, "k": 32, "tile_m": 3, "tile_n": 2, "threads": 64, "grouping": 2, "minblocks": 2, "algorithm": "medium", "perf": 1611.52, "source": "autotuned"}, +{"m": 24, "n": 24, "k": 5, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 4, "minblocks": 12, "algorithm": "medium", "perf": 1593.42, "source": "autotuned"}, +{"m": 24, "n": 24, "k": 13, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 1983.67, "source": "autotuned"}, +{"m": 24, "n": 24, "k": 24, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 2213.95, "source": "autotuned"}, +{"m": 24, "n": 24, "k": 26, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 2211.71, "source": "autotuned"}, +{"m": 24, "n": 24, "k": 32, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 2, "minblocks": 5, "algorithm": "medium", "perf": 2310.88, "source": "autotuned"}, +{"m": 24, "n": 26, "k": 5, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 5, "minblocks": 10, "algorithm": "medium", "perf": 1612.07, "source": "autotuned"}, +{"m": 24, "n": 26, "k": 13, "tile_m": 1, "tile_n": 7, "threads": 96, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1965.73, "source": "autotuned"}, +{"m": 24, "n": 26, "k": 24, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2194.44, "source": "autotuned"}, +{"m": 24, "n": 26, "k": 26, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 2182.89, "source": "autotuned"}, +{"m": 24, "n": 26, "k": 32, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 3, "algorithm": "medium", "perf": 2311.83, "source": "autotuned"}, +{"m": 24, "n": 32, "k": 5, "tile_m": 1, "tile_n": 4, "threads": 192, "grouping": 5, "minblocks": 7, "algorithm": "medium", "perf": 1756.65, "source": "autotuned"}, +{"m": 24, "n": 32, "k": 13, "tile_m": 12, "tile_n": 1, "threads": 64, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 2142.55, "source": "autotuned"}, +{"m": 24, "n": 32, "k": 24, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 2451.83, "source": "autotuned"}, +{"m": 24, "n": 32, "k": 26, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 2431.93, "source": "autotuned"}, +{"m": 24, "n": 32, "k": 32, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 3, "algorithm": "medium", "perf": 2560.8, "source": "autotuned"}, +{"m": 25, "n": 4, "k": 4, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 9, "minblocks": 20, "algorithm": "medium", "perf": 688.492, "source": "autotuned"}, +{"m": 25, "n": 4, "k": 5, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 4, "minblocks": 26, "algorithm": "medium", "perf": 677.205, "source": "autotuned"}, +{"m": 25, "n": 4, "k": 7, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 3, "minblocks": 26, "algorithm": "medium", "perf": 692.721, "source": "autotuned"}, +{"m": 25, "n": 4, "k": 9, "tile_m": 1, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 22, "algorithm": "medium", "perf": 684.905, "source": "autotuned"}, +{"m": 25, "n": 4, "k": 13, "tile_m": 1, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 19, "algorithm": "medium", "perf": 692.688, "source": "autotuned"}, +{"m": 25, "n": 4, "k": 25, "tile_m": 1, "tile_n": 4, "threads": 128, "grouping": 2, "minblocks": 10, "algorithm": "medium", "perf": 691.588, "source": "autotuned"}, +{"m": 25, "n": 4, "k": 26, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 694.661, "source": "autotuned"}, +{"m": 25, "n": 4, "k": 28, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 696.043, "source": "autotuned"}, +{"m": 25, "n": 4, "k": 32, "tile_m": 1, "tile_n": 2, "threads": 192, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 711.123, "source": "autotuned"}, +{"m": 25, "n": 4, "k": 45, "tile_m": 1, "tile_n": 2, "threads": 192, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 704.519, "source": "autotuned"}, +{"m": 25, "n": 5, "k": 4, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 9, "minblocks": 23, "algorithm": "medium", "perf": 784.633, "source": "autotuned"}, +{"m": 25, "n": 5, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 9, "minblocks": 24, "algorithm": "medium", "perf": 770.704, "source": "autotuned"}, +{"m": 25, "n": 5, "k": 7, "tile_m": 1, "tile_n": 5, "threads": 64, "grouping": 3, "minblocks": 23, "algorithm": "medium", "perf": 779.902, "source": "autotuned"}, +{"m": 25, "n": 5, "k": 9, "tile_m": 1, "tile_n": 5, "threads": 64, "grouping": 3, "minblocks": 17, "algorithm": "medium", "perf": 794.332, "source": "autotuned"}, +{"m": 25, "n": 5, "k": 13, "tile_m": 1, "tile_n": 5, "threads": 96, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 797.264, "source": "autotuned"}, +{"m": 25, "n": 5, "k": 25, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 2, "minblocks": 9, "algorithm": "medium", "perf": 822.625, "source": "autotuned"}, +{"m": 25, "n": 5, "k": 26, "tile_m": 1, "tile_n": 5, "threads": 160, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 808.442, "source": "autotuned"}, +{"m": 25, "n": 5, "k": 28, "tile_m": 1, "tile_n": 5, "threads": 160, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 814.46, "source": "autotuned"}, +{"m": 25, "n": 5, "k": 32, "tile_m": 1, "tile_n": 3, "threads": 192, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 844.859, "source": "autotuned"}, +{"m": 25, "n": 5, "k": 45, "tile_m": 1, "tile_n": 5, "threads": 256, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 832.032, "source": "autotuned"}, +{"m": 25, "n": 7, "k": 4, "tile_m": 1, "tile_n": 7, "threads": 32, "grouping": 9, "minblocks": 21, "algorithm": "medium", "perf": 951.384, "source": "autotuned"}, +{"m": 25, "n": 7, "k": 5, "tile_m": 1, "tile_n": 7, "threads": 64, "grouping": 11, "minblocks": 19, "algorithm": "medium", "perf": 920.51, "source": "autotuned"}, +{"m": 25, "n": 7, "k": 7, "tile_m": 1, "tile_n": 7, "threads": 64, "grouping": 10, "minblocks": 19, "algorithm": "medium", "perf": 947.852, "source": "autotuned"}, +{"m": 25, "n": 7, "k": 9, "tile_m": 1, "tile_n": 7, "threads": 64, "grouping": 3, "minblocks": 17, "algorithm": "medium", "perf": 970.814, "source": "autotuned"}, +{"m": 25, "n": 7, "k": 13, "tile_m": 1, "tile_n": 7, "threads": 96, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 996.022, "source": "autotuned"}, +{"m": 25, "n": 7, "k": 25, "tile_m": 3, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1030.87, "source": "autotuned"}, +{"m": 25, "n": 7, "k": 26, "tile_m": 3, "tile_n": 1, "threads": 192, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1040.72, "source": "autotuned"}, +{"m": 25, "n": 7, "k": 28, "tile_m": 3, "tile_n": 1, "threads": 256, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1041.99, "source": "autotuned"}, +{"m": 25, "n": 7, "k": 32, "tile_m": 1, "tile_n": 7, "threads": 32, "grouping": 2, "minblocks": 9, "algorithm": "medium", "perf": 1078.37, "source": "autotuned"}, +{"m": 25, "n": 7, "k": 45, "tile_m": 3, "tile_n": 1, "threads": 320, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 1067.21, "source": "autotuned"}, +{"m": 25, "n": 9, "k": 4, "tile_m": 1, "tile_n": 9, "threads": 64, "grouping": 13, "minblocks": 16, "algorithm": "medium", "perf": 1018.77, "source": "autotuned"}, +{"m": 25, "n": 9, "k": 5, "tile_m": 1, "tile_n": 9, "threads": 64, "grouping": 10, "minblocks": 19, "algorithm": "medium", "perf": 1031.57, "source": "autotuned"}, +{"m": 25, "n": 9, "k": 7, "tile_m": 1, "tile_n": 9, "threads": 64, "grouping": 4, "minblocks": 17, "algorithm": "medium", "perf": 1090.63, "source": "autotuned"}, +{"m": 25, "n": 9, "k": 9, "tile_m": 1, "tile_n": 9, "threads": 96, "grouping": 4, "minblocks": 12, "algorithm": "medium", "perf": 1110.54, "source": "autotuned"}, +{"m": 25, "n": 9, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 128, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1161.08, "source": "autotuned"}, +{"m": 25, "n": 9, "k": 25, "tile_m": 1, "tile_n": 3, "threads": 256, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1218.26, "source": "autotuned"}, +{"m": 25, "n": 9, "k": 26, "tile_m": 1, "tile_n": 3, "threads": 256, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1219.13, "source": "autotuned"}, +{"m": 25, "n": 9, "k": 28, "tile_m": 1, "tile_n": 3, "threads": 256, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 1243.81, "source": "autotuned"}, +{"m": 25, "n": 9, "k": 32, "tile_m": 3, "tile_n": 3, "threads": 32, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 1281.24, "source": "autotuned"}, +{"m": 25, "n": 9, "k": 45, "tile_m": 1, "tile_n": 3, "threads": 416, "grouping": 4, "minblocks": 3, "algorithm": "medium", "perf": 1264.33, "source": "autotuned"}, +{"m": 25, "n": 11, "k": 11, "tile_m": 1, "tile_n": 4, "threads": 128, "grouping": 4, "minblocks": 9, "algorithm": "medium", "perf": 1260.81, "source": "autotuned"}, +{"m": 25, "n": 11, "k": 12, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1273.55, "source": "autotuned"}, +{"m": 25, "n": 11, "k": 20, "tile_m": 5, "tile_n": 2, "threads": 32, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 1350.23, "source": "autotuned"}, +{"m": 25, "n": 11, "k": 25, "tile_m": 5, "tile_n": 2, "threads": 32, "grouping": 2, "minblocks": 12, "algorithm": "medium", "perf": 1377.63, "source": "autotuned"}, +{"m": 25, "n": 11, "k": 32, "tile_m": 5, "tile_n": 2, "threads": 64, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 1452.92, "source": "autotuned"}, +{"m": 25, "n": 12, "k": 11, "tile_m": 5, "tile_n": 1, "threads": 160, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1311.32, "source": "autotuned"}, +{"m": 25, "n": 12, "k": 12, "tile_m": 1, "tile_n": 3, "threads": 160, "grouping": 4, "minblocks": 9, "algorithm": "medium", "perf": 1355.85, "source": "autotuned"}, +{"m": 25, "n": 12, "k": 20, "tile_m": 1, "tile_n": 4, "threads": 256, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 1439.07, "source": "autotuned"}, +{"m": 25, "n": 13, "k": 4, "tile_m": 1, "tile_n": 7, "threads": 64, "grouping": 13, "minblocks": 17, "algorithm": "medium", "perf": 1172.24, "source": "autotuned"}, +{"m": 25, "n": 13, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 6, "minblocks": 12, "algorithm": "medium", "perf": 1201.28, "source": "autotuned"}, +{"m": 25, "n": 13, "k": 7, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 4, "minblocks": 12, "algorithm": "medium", "perf": 1288.33, "source": "autotuned"}, +{"m": 25, "n": 13, "k": 9, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1349.59, "source": "autotuned"}, +{"m": 25, "n": 13, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 192, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1380.55, "source": "autotuned"}, +{"m": 25, "n": 13, "k": 14, "tile_m": 1, "tile_n": 3, "threads": 192, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1408.24, "source": "autotuned"}, +{"m": 25, "n": 13, "k": 25, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1523.91, "source": "autotuned"}, +{"m": 25, "n": 13, "k": 26, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 2, "minblocks": 8, "algorithm": "medium", "perf": 1512.28, "source": "autotuned"}, +{"m": 25, "n": 13, "k": 28, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1543.2, "source": "autotuned"}, +{"m": 25, "n": 13, "k": 32, "tile_m": 7, "tile_n": 2, "threads": 32, "grouping": 2, "minblocks": 10, "algorithm": "medium", "perf": 1604.12, "source": "autotuned"}, +{"m": 25, "n": 13, "k": 45, "tile_m": 3, "tile_n": 2, "threads": 64, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 1600.38, "source": "autotuned"}, +{"m": 25, "n": 14, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 192, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1451.35, "source": "autotuned"}, +{"m": 25, "n": 14, "k": 14, "tile_m": 1, "tile_n": 3, "threads": 224, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1482.32, "source": "autotuned"}, +{"m": 25, "n": 14, "k": 26, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1595.12, "source": "autotuned"}, +{"m": 25, "n": 14, "k": 32, "tile_m": 3, "tile_n": 2, "threads": 64, "grouping": 2, "minblocks": 2, "algorithm": "medium", "perf": 1680.53, "source": "autotuned"}, +{"m": 25, "n": 20, "k": 11, "tile_m": 1, "tile_n": 4, "threads": 288, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1691.1, "source": "autotuned"}, +{"m": 25, "n": 20, "k": 12, "tile_m": 1, "tile_n": 4, "threads": 320, "grouping": 5, "minblocks": 6, "algorithm": "medium", "perf": 1720.72, "source": "autotuned"}, +{"m": 25, "n": 20, "k": 20, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 1886.91, "source": "autotuned"}, +{"m": 25, "n": 20, "k": 25, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1909.19, "source": "autotuned"}, +{"m": 25, "n": 20, "k": 32, "tile_m": 3, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 2038.68, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 4, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 6, "minblocks": 12, "algorithm": "medium", "perf": 1485.73, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 5, "minblocks": 12, "algorithm": "medium", "perf": 1575.59, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 7, "tile_m": 1, "tile_n": 9, "threads": 96, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1702.42, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 9, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 4, "minblocks": 2, "algorithm": "medium", "perf": 1812.74, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 11, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 1890.71, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 12, "tile_m": 5, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 1953.74, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 13, "tile_m": 3, "tile_n": 5, "threads": 64, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1968.88, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 14, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 2006.28, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 20, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 2131.35, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 25, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 2191.41, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 26, "tile_m": 3, "tile_n": 4, "threads": 96, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 2199.01, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 28, "tile_m": 3, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 2205.83, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 32, "tile_m": 3, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 2298.51, "source": "autotuned"}, +{"m": 25, "n": 25, "k": 45, "tile_m": 3, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 2299.51, "source": "autotuned"}, +{"m": 25, "n": 26, "k": 4, "tile_m": 1, "tile_n": 9, "threads": 128, "grouping": 6, "minblocks": 9, "algorithm": "medium", "perf": 1505.1, "source": "autotuned"}, +{"m": 25, "n": 26, "k": 5, "tile_m": 1, "tile_n": 9, "threads": 160, "grouping": 6, "minblocks": 8, "algorithm": "medium", "perf": 1562.99, "source": "autotuned"}, +{"m": 25, "n": 26, "k": 7, "tile_m": 3, "tile_n": 2, "threads": 192, "grouping": 5, "minblocks": 8, "algorithm": "medium", "perf": 1727.51, "source": "autotuned"}, +{"m": 25, "n": 26, "k": 9, "tile_m": 3, "tile_n": 2, "threads": 256, "grouping": 5, "minblocks": 6, "algorithm": "medium", "perf": 1769.71, "source": "autotuned"}, +{"m": 25, "n": 26, "k": 13, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 1923.39, "source": "autotuned"}, +{"m": 25, "n": 26, "k": 14, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 1957.32, "source": "autotuned"}, +{"m": 25, "n": 26, "k": 25, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 2161.58, "source": "autotuned"}, +{"m": 25, "n": 26, "k": 26, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 2159.22, "source": "autotuned"}, +{"m": 25, "n": 26, "k": 28, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 2202.3, "source": "autotuned"}, +{"m": 25, "n": 26, "k": 32, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 2305.68, "source": "autotuned"}, +{"m": 25, "n": 26, "k": 45, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 5, "algorithm": "medium", "perf": 2334.51, "source": "autotuned"}, +{"m": 25, "n": 28, "k": 4, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 6, "minblocks": 12, "algorithm": "medium", "perf": 1549.16, "source": "autotuned"}, +{"m": 25, "n": 28, "k": 5, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 5, "minblocks": 11, "algorithm": "medium", "perf": 1616.88, "source": "autotuned"}, +{"m": 25, "n": 28, "k": 7, "tile_m": 3, "tile_n": 2, "threads": 256, "grouping": 5, "minblocks": 6, "algorithm": "medium", "perf": 1760.61, "source": "autotuned"}, +{"m": 25, "n": 28, "k": 9, "tile_m": 1, "tile_n": 6, "threads": 256, "grouping": 6, "minblocks": 5, "algorithm": "medium", "perf": 1846.28, "source": "autotuned"}, +{"m": 25, "n": 28, "k": 13, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 2007.98, "source": "autotuned"}, +{"m": 25, "n": 28, "k": 25, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2238.93, "source": "autotuned"}, +{"m": 25, "n": 28, "k": 28, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 3, "algorithm": "medium", "perf": 2303.35, "source": "autotuned"}, +{"m": 25, "n": 28, "k": 32, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 2, "algorithm": "medium", "perf": 2395.28, "source": "autotuned"}, +{"m": 25, "n": 28, "k": 45, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 1, "minblocks": 3, "algorithm": "medium", "perf": 2401.66, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 4, "tile_m": 1, "tile_n": 7, "threads": 160, "grouping": 6, "minblocks": 8, "algorithm": "medium", "perf": 1604.78, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 5, "tile_m": 1, "tile_n": 11, "threads": 128, "grouping": 5, "minblocks": 9, "algorithm": "medium", "perf": 1712.86, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 7, "tile_m": 3, "tile_n": 2, "threads": 256, "grouping": 6, "minblocks": 6, "algorithm": "medium", "perf": 1882.31, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 9, "tile_m": 7, "tile_n": 2, "threads": 64, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 1923.68, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 11, "tile_m": 7, "tile_n": 2, "threads": 64, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 2058.0, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 12, "tile_m": 7, "tile_n": 2, "threads": 64, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 2113.34, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 13, "tile_m": 5, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 2129.49, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 14, "tile_m": 5, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 2179.1, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 20, "tile_m": 3, "tile_n": 5, "threads": 64, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2319.12, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 25, "tile_m": 7, "tile_n": 2, "threads": 64, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 2380.19, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 26, "tile_m": 7, "tile_n": 2, "threads": 64, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 2405.19, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 28, "tile_m": 7, "tile_n": 2, "threads": 64, "grouping": 2, "minblocks": 2, "algorithm": "medium", "perf": 2445.47, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 32, "tile_m": 3, "tile_n": 5, "threads": 64, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 2543.17, "source": "autotuned"}, +{"m": 25, "n": 32, "k": 45, "tile_m": 7, "tile_n": 2, "threads": 64, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 2534.58, "source": "autotuned"}, +{"m": 25, "n": 45, "k": 4, "tile_m": 1, "tile_n": 9, "threads": 192, "grouping": 7, "minblocks": 6, "algorithm": "medium", "perf": 1681.3, "source": "autotuned"}, +{"m": 25, "n": 45, "k": 5, "tile_m": 1, "tile_n": 9, "threads": 128, "grouping": 5, "minblocks": 8, "algorithm": "medium", "perf": 1844.44, "source": "autotuned"}, +{"m": 25, "n": 45, "k": 7, "tile_m": 1, "tile_n": 9, "threads": 192, "grouping": 5, "minblocks": 6, "algorithm": "medium", "perf": 2028.88, "source": "autotuned"}, +{"m": 25, "n": 45, "k": 9, "tile_m": 1, "tile_n": 9, "threads": 256, "grouping": 5, "minblocks": 4, "algorithm": "medium", "perf": 2114.73, "source": "autotuned"}, +{"m": 25, "n": 45, "k": 13, "tile_m": 1, "tile_n": 9, "threads": 128, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 2282.07, "source": "autotuned"}, +{"m": 25, "n": 45, "k": 25, "tile_m": 3, "tile_n": 4, "threads": 128, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 2621.81, "source": "autotuned"}, +{"m": 25, "n": 45, "k": 26, "tile_m": 5, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 2612.42, "source": "autotuned"}, +{"m": 25, "n": 45, "k": 28, "tile_m": 3, "tile_n": 5, "threads": 128, "grouping": 1, "minblocks": 7, "algorithm": "medium", "perf": 2678.19, "source": "autotuned"}, +{"m": 25, "n": 45, "k": 32, "tile_m": 3, "tile_n": 5, "threads": 160, "grouping": 1, "minblocks": 5, "algorithm": "medium", "perf": 2804.03, "source": "autotuned"}, +{"m": 25, "n": 45, "k": 45, "tile_m": 7, "tile_n": 2, "threads": 96, "grouping": 2, "minblocks": 3, "algorithm": "medium", "perf": 2769.88, "source": "autotuned"}, +{"m": 26, "n": 4, "k": 4, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 9, "minblocks": 6, "algorithm": "medium", "perf": 699.77, "source": "autotuned"}, +{"m": 26, "n": 4, "k": 5, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 3, "minblocks": 26, "algorithm": "medium", "perf": 688.995, "source": "autotuned"}, +{"m": 26, "n": 4, "k": 9, "tile_m": 1, "tile_n": 4, "threads": 64, "grouping": 4, "minblocks": 17, "algorithm": "medium", "perf": 677.491, "source": "autotuned"}, +{"m": 26, "n": 4, "k": 13, "tile_m": 1, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 17, "algorithm": "medium", "perf": 693.116, "source": "autotuned"}, +{"m": 26, "n": 4, "k": 22, "tile_m": 1, "tile_n": 4, "threads": 96, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 703.614, "source": "autotuned"}, +{"m": 26, "n": 4, "k": 23, "tile_m": 1, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 694.92, "source": "autotuned"}, +{"m": 26, "n": 4, "k": 26, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 708.26, "source": "autotuned"}, +{"m": 26, "n": 5, "k": 4, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 9, "minblocks": 22, "algorithm": "medium", "perf": 804.323, "source": "autotuned"}, +{"m": 26, "n": 5, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 5, "minblocks": 26, "algorithm": "medium", "perf": 784.372, "source": "autotuned"}, +{"m": 26, "n": 5, "k": 9, "tile_m": 1, "tile_n": 3, "threads": 64, "grouping": 3, "minblocks": 23, "algorithm": "medium", "perf": 802.5, "source": "autotuned"}, +{"m": 26, "n": 5, "k": 13, "tile_m": 1, "tile_n": 5, "threads": 96, "grouping": 3, "minblocks": 13, "algorithm": "medium", "perf": 801.035, "source": "autotuned"}, +{"m": 26, "n": 5, "k": 22, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 819.975, "source": "autotuned"}, +{"m": 26, "n": 5, "k": 23, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 821.742, "source": "autotuned"}, +{"m": 26, "n": 5, "k": 26, "tile_m": 1, "tile_n": 2, "threads": 160, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 826.193, "source": "autotuned"}, +{"m": 26, "n": 9, "k": 4, "tile_m": 1, "tile_n": 9, "threads": 64, "grouping": 13, "minblocks": 15, "algorithm": "medium", "perf": 1043.71, "source": "autotuned"}, +{"m": 26, "n": 9, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 64, "grouping": 4, "minblocks": 24, "algorithm": "medium", "perf": 1052.58, "source": "autotuned"}, +{"m": 26, "n": 9, "k": 9, "tile_m": 1, "tile_n": 9, "threads": 96, "grouping": 4, "minblocks": 12, "algorithm": "medium", "perf": 1138.73, "source": "autotuned"}, +{"m": 26, "n": 9, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1184.14, "source": "autotuned"}, +{"m": 26, "n": 9, "k": 22, "tile_m": 1, "tile_n": 3, "threads": 256, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 1229.9, "source": "autotuned"}, +{"m": 26, "n": 9, "k": 23, "tile_m": 1, "tile_n": 3, "threads": 256, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1228.59, "source": "autotuned"}, +{"m": 26, "n": 9, "k": 26, "tile_m": 1, "tile_n": 3, "threads": 256, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1249.07, "source": "autotuned"}, +{"m": 26, "n": 13, "k": 4, "tile_m": 1, "tile_n": 7, "threads": 64, "grouping": 13, "minblocks": 17, "algorithm": "medium", "perf": 1216.94, "source": "autotuned"}, +{"m": 26, "n": 13, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 96, "grouping": 5, "minblocks": 14, "algorithm": "medium", "perf": 1229.6, "source": "autotuned"}, +{"m": 26, "n": 13, "k": 9, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 4, "minblocks": 10, "algorithm": "medium", "perf": 1378.77, "source": "autotuned"}, +{"m": 26, "n": 13, "k": 13, "tile_m": 1, "tile_n": 4, "threads": 192, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1417.71, "source": "autotuned"}, +{"m": 26, "n": 13, "k": 22, "tile_m": 1, "tile_n": 3, "threads": 288, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1533.15, "source": "autotuned"}, +{"m": 26, "n": 13, "k": 23, "tile_m": 5, "tile_n": 3, "threads": 32, "grouping": 2, "minblocks": 4, "algorithm": "medium", "perf": 1533.11, "source": "autotuned"}, +{"m": 26, "n": 13, "k": 26, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1554.51, "source": "autotuned"}, +{"m": 26, "n": 22, "k": 4, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 6, "minblocks": 11, "algorithm": "medium", "perf": 1499.81, "source": "autotuned"}, +{"m": 26, "n": 22, "k": 5, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 5, "minblocks": 10, "algorithm": "medium", "perf": 1535.64, "source": "autotuned"}, +{"m": 26, "n": 22, "k": 9, "tile_m": 1, "tile_n": 4, "threads": 256, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1734.07, "source": "autotuned"}, +{"m": 26, "n": 22, "k": 13, "tile_m": 1, "tile_n": 11, "threads": 64, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1854.64, "source": "autotuned"}, +{"m": 26, "n": 22, "k": 22, "tile_m": 1, "tile_n": 8, "threads": 128, "grouping": 2, "minblocks": 7, "algorithm": "medium", "perf": 2013.32, "source": "autotuned"}, +{"m": 26, "n": 22, "k": 23, "tile_m": 1, "tile_n": 8, "threads": 128, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2014.1, "source": "autotuned"}, +{"m": 26, "n": 22, "k": 26, "tile_m": 1, "tile_n": 8, "threads": 96, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 2055.64, "source": "autotuned"}, +{"m": 26, "n": 23, "k": 4, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 6, "minblocks": 12, "algorithm": "medium", "perf": 1491.55, "source": "autotuned"}, +{"m": 26, "n": 23, "k": 5, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 4, "minblocks": 12, "algorithm": "medium", "perf": 1542.12, "source": "autotuned"}, +{"m": 26, "n": 23, "k": 9, "tile_m": 1, "tile_n": 3, "threads": 256, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1784.05, "source": "autotuned"}, +{"m": 26, "n": 23, "k": 13, "tile_m": 1, "tile_n": 4, "threads": 352, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 1883.67, "source": "autotuned"}, +{"m": 26, "n": 23, "k": 22, "tile_m": 1, "tile_n": 8, "threads": 128, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2063.9, "source": "autotuned"}, +{"m": 26, "n": 23, "k": 23, "tile_m": 1, "tile_n": 6, "threads": 160, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2052.66, "source": "autotuned"}, +{"m": 26, "n": 23, "k": 26, "tile_m": 1, "tile_n": 8, "threads": 96, "grouping": 2, "minblocks": 6, "algorithm": "medium", "perf": 2108.1, "source": "autotuned"}, +{"m": 26, "n": 26, "k": 4, "tile_m": 1, "tile_n": 9, "threads": 128, "grouping": 6, "minblocks": 9, "algorithm": "medium", "perf": 1540.78, "source": "autotuned"}, +{"m": 26, "n": 26, "k": 5, "tile_m": 1, "tile_n": 9, "threads": 96, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1601.64, "source": "autotuned"}, +{"m": 26, "n": 26, "k": 9, "tile_m": 1, "tile_n": 9, "threads": 128, "grouping": 4, "minblocks": 9, "algorithm": "medium", "perf": 1892.58, "source": "autotuned"}, +{"m": 26, "n": 26, "k": 13, "tile_m": 1, "tile_n": 9, "threads": 96, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 2050.56, "source": "autotuned"}, +{"m": 26, "n": 26, "k": 22, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 4, "algorithm": "medium", "perf": 2248.36, "source": "autotuned"}, +{"m": 26, "n": 26, "k": 23, "tile_m": 3, "tile_n": 4, "threads": 64, "grouping": 2, "minblocks": 1, "algorithm": "medium", "perf": 2245.79, "source": "autotuned"}, +{"m": 26, "n": 26, "k": 26, "tile_m": 3, "tile_n": 4, "threads": 96, "grouping": 2, "minblocks": 5, "algorithm": "medium", "perf": 2288.66, "source": "autotuned"}, +{"m": 27, "n": 27, "k": 27, "tile_m": 3, "tile_n": 4, "threads": 96, "grouping": 2, "minblocks": 5, "algorithm": "medium", "perf": 2376.96, "source": "autotuned"}, +{"m": 28, "n": 4, "k": 5, "tile_m": 1, "tile_n": 4, "threads": 32, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 673.287, "source": "autotuned"}, +{"m": 28, "n": 4, "k": 13, "tile_m": 1, "tile_n": 4, "threads": 64, "grouping": 3, "minblocks": 15, "algorithm": "medium", "perf": 692.231, "source": "autotuned"}, +{"m": 28, "n": 4, "k": 28, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 713.815, "source": "autotuned"}, +{"m": 28, "n": 5, "k": 25, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 830.956, "source": "autotuned"}, +{"m": 28, "n": 28, "k": 28, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 2506.43, "source": "autotuned"}, +{"m": 29, "n": 14, "k": 14, "tile_m": 5, "tile_n": 3, "threads": 32, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 1545.05, "source": "autotuned"}, +{"m": 29, "n": 14, "k": 16, "tile_m": 5, "tile_n": 3, "threads": 32, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 1615.89, "source": "autotuned"}, +{"m": 29, "n": 14, "k": 29, "tile_m": 5, "tile_n": 3, "w": 10, "v": 8, "threads": 32, "grouping": 16, "minblocks": 4, "algorithm": "largeDB2", "perf": 1693.49, "source": "autotuned"}, +{"m": 29, "n": 14, "k": 32, "tile_m": 3, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1770.9, "source": "autotuned"}, +{"m": 29, "n": 16, "k": 14, "tile_m": 3, "tile_n": 1, "threads": 256, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1706.42, "source": "autotuned"}, +{"m": 29, "n": 16, "k": 16, "tile_m": 3, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 10, "algorithm": "medium", "perf": 1749.29, "source": "autotuned"}, +{"m": 29, "n": 29, "k": 14, "tile_m": 3, "tile_n": 2, "threads": 160, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 2271.93, "source": "autotuned"}, +{"m": 29, "n": 29, "k": 16, "tile_m": 3, "tile_n": 5, "threads": 96, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 2399.04, "source": "autotuned"}, +{"m": 29, "n": 29, "k": 29, "tile_m": 3, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 2547.24, "source": "autotuned"}, +{"m": 29, "n": 29, "k": 32, "tile_m": 3, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 2645.48, "source": "autotuned"}, +{"m": 29, "n": 32, "k": 14, "tile_m": 3, "tile_n": 2, "threads": 160, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 2320.73, "source": "autotuned"}, +{"m": 29, "n": 32, "k": 29, "tile_m": 3, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 2639.95, "source": "autotuned"}, +{"m": 29, "n": 32, "k": 32, "tile_m": 3, "tile_n": 2, "threads": 256, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 2714.3, "source": "autotuned"}, +{"m": 30, "n": 30, "k": 30, "tile_m": 3, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 2650.57, "source": "autotuned"}, +{"m": 31, "n": 31, "k": 31, "tile_m": 2, "tile_n": 4, "threads": 128, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 2700.28, "source": "autotuned"}, +{"m": 32, "n": 4, "k": 13, "tile_m": 1, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 14, "algorithm": "medium", "perf": 712.551, "source": "autotuned"}, +{"m": 32, "n": 4, "k": 25, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 728.383, "source": "autotuned"}, +{"m": 32, "n": 4, "k": 26, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 734.347, "source": "autotuned"}, +{"m": 32, "n": 4, "k": 28, "tile_m": 1, "tile_n": 2, "threads": 128, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 738.692, "source": "autotuned"}, +{"m": 32, "n": 4, "k": 32, "tile_m": 1, "tile_n": 2, "threads": 160, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 748.772, "source": "autotuned"}, +{"m": 32, "n": 5, "k": 4, "tile_m": 2, "tile_n": 3, "threads": 32, "grouping": 5, "minblocks": 2, "algorithm": "medium", "perf": 788.628, "source": "autotuned"}, +{"m": 32, "n": 5, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 32, "grouping": 4, "minblocks": 25, "algorithm": "medium", "perf": 806.704, "source": "autotuned"}, +{"m": 32, "n": 5, "k": 7, "tile_m": 1, "tile_n": 3, "threads": 64, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 809.076, "source": "autotuned"}, +{"m": 32, "n": 5, "k": 16, "tile_m": 1, "tile_n": 2, "threads": 96, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 860.484, "source": "autotuned"}, +{"m": 32, "n": 5, "k": 26, "tile_m": 1, "tile_n": 2, "threads": 160, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 863.101, "source": "autotuned"}, +{"m": 32, "n": 5, "k": 45, "tile_m": 1, "tile_n": 2, "threads": 256, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 884.942, "source": "autotuned"}, +{"m": 32, "n": 7, "k": 4, "tile_m": 1, "tile_n": 7, "threads": 32, "grouping": 5, "minblocks": 25, "algorithm": "medium", "perf": 950.689, "source": "autotuned"}, +{"m": 32, "n": 7, "k": 7, "tile_m": 1, "tile_n": 7, "threads": 64, "grouping": 5, "minblocks": 15, "algorithm": "medium", "perf": 1006.18, "source": "autotuned"}, +{"m": 32, "n": 7, "k": 9, "tile_m": 1, "tile_n": 7, "threads": 64, "grouping": 4, "minblocks": 14, "algorithm": "medium", "perf": 1044.53, "source": "autotuned"}, +{"m": 32, "n": 7, "k": 13, "tile_m": 1, "tile_n": 4, "threads": 96, "grouping": 3, "minblocks": 12, "algorithm": "medium", "perf": 1070.92, "source": "autotuned"}, +{"m": 32, "n": 7, "k": 26, "tile_m": 1, "tile_n": 4, "threads": 224, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1130.79, "source": "autotuned"}, +{"m": 32, "n": 7, "k": 32, "tile_m": 1, "tile_n": 2, "threads": 256, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1158.98, "source": "autotuned"}, +{"m": 32, "n": 7, "k": 45, "tile_m": 1, "tile_n": 2, "threads": 320, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 1155.62, "source": "autotuned"}, +{"m": 32, "n": 9, "k": 7, "tile_m": 1, "tile_n": 5, "threads": 64, "grouping": 4, "minblocks": 13, "algorithm": "medium", "perf": 1189.05, "source": "autotuned"}, +{"m": 32, "n": 9, "k": 12, "tile_m": 1, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1266.02, "source": "autotuned"}, +{"m": 32, "n": 9, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1272.14, "source": "autotuned"}, +{"m": 32, "n": 9, "k": 25, "tile_m": 1, "tile_n": 3, "threads": 256, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1334.41, "source": "autotuned"}, +{"m": 32, "n": 9, "k": 26, "tile_m": 1, "tile_n": 3, "threads": 256, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1345.25, "source": "autotuned"}, +{"m": 32, "n": 9, "k": 32, "tile_m": 1, "tile_n": 3, "threads": 320, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 1380.62, "source": "autotuned"}, +{"m": 32, "n": 9, "k": 45, "tile_m": 1, "tile_n": 3, "threads": 416, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 1398.6, "source": "autotuned"}, +{"m": 32, "n": 10, "k": 9, "tile_m": 1, "tile_n": 4, "threads": 96, "grouping": 3, "minblocks": 16, "algorithm": "medium", "perf": 1295.37, "source": "autotuned"}, +{"m": 32, "n": 10, "k": 10, "tile_m": 1, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1316.2, "source": "autotuned"}, +{"m": 32, "n": 10, "k": 32, "tile_m": 1, "tile_n": 2, "threads": 352, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 1486.53, "source": "autotuned"}, +{"m": 32, "n": 11, "k": 11, "tile_m": 1, "tile_n": 3, "threads": 128, "grouping": 3, "minblocks": 11, "algorithm": "medium", "perf": 1401.98, "source": "autotuned"}, +{"m": 32, "n": 11, "k": 12, "tile_m": 1, "tile_n": 3, "threads": 192, "grouping": 3, "minblocks": 8, "algorithm": "medium", "perf": 1409.69, "source": "autotuned"}, +{"m": 32, "n": 11, "k": 20, "tile_m": 1, "tile_n": 4, "threads": 224, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1507.32, "source": "autotuned"}, +{"m": 32, "n": 11, "k": 32, "tile_m": 1, "tile_n": 3, "threads": 384, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 1595.66, "source": "autotuned"}, +{"m": 32, "n": 12, "k": 11, "tile_m": 1, "tile_n": 3, "threads": 160, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 1472.58, "source": "autotuned"}, +{"m": 32, "n": 12, "k": 20, "tile_m": 1, "tile_n": 3, "threads": 256, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 1614.85, "source": "autotuned"}, +{"m": 32, "n": 12, "k": 26, "tile_m": 1, "tile_n": 3, "threads": 320, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 1631.17, "source": "autotuned"}, +{"m": 32, "n": 12, "k": 32, "tile_m": 1, "tile_n": 3, "threads": 512, "grouping": 4, "minblocks": 3, "algorithm": "medium", "perf": 1687.2, "source": "autotuned"}, +{"m": 32, "n": 13, "k": 7, "tile_m": 1, "tile_n": 7, "threads": 96, "grouping": 4, "minblocks": 11, "algorithm": "medium", "perf": 1444.9, "source": "autotuned"}, +{"m": 32, "n": 13, "k": 9, "tile_m": 1, "tile_n": 3, "threads": 160, "grouping": 4, "minblocks": 9, "algorithm": "medium", "perf": 1494.08, "source": "autotuned"}, +{"m": 32, "n": 13, "k": 13, "tile_m": 1, "tile_n": 2, "threads": 256, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1560.49, "source": "autotuned"}, +{"m": 32, "n": 13, "k": 16, "tile_m": 1, "tile_n": 5, "threads": 224, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1621.29, "source": "autotuned"}, +{"m": 32, "n": 13, "k": 28, "tile_m": 1, "tile_n": 2, "threads": 384, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 1714.82, "source": "autotuned"}, +{"m": 32, "n": 14, "k": 13, "tile_m": 1, "tile_n": 3, "threads": 224, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 1640.15, "source": "autotuned"}, +{"m": 32, "n": 16, "k": 5, "tile_m": 1, "tile_n": 4, "threads": 192, "grouping": 5, "minblocks": 9, "algorithm": "medium", "perf": 1495.79, "source": "autotuned"}, +{"m": 32, "n": 16, "k": 12, "tile_m": 1, "tile_n": 4, "threads": 224, "grouping": 4, "minblocks": 6, "algorithm": "medium", "perf": 1794.95, "source": "autotuned"}, +{"m": 32, "n": 16, "k": 13, "tile_m": 1, "tile_n": 4, "threads": 256, "grouping": 4, "minblocks": 6, "algorithm": "medium", "perf": 1798.83, "source": "autotuned"}, +{"m": 32, "n": 20, "k": 11, "tile_m": 1, "tile_n": 4, "threads": 224, "grouping": 4, "minblocks": 6, "algorithm": "medium", "perf": 1930.43, "source": "autotuned"}, +{"m": 32, "n": 20, "k": 20, "tile_m": 1, "tile_n": 5, "threads": 128, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 2135.9, "source": "autotuned"}, +{"m": 32, "n": 22, "k": 9, "tile_m": 1, "tile_n": 4, "threads": 320, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 1904.44, "source": "autotuned"}, +{"m": 32, "n": 24, "k": 5, "tile_m": 1, "tile_n": 4, "threads": 224, "grouping": 5, "minblocks": 8, "algorithm": "medium", "perf": 1756.65, "source": "autotuned"}, +{"m": 32, "n": 24, "k": 13, "tile_m": 1, "tile_n": 12, "threads": 64, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 2172.37, "source": "autotuned"}, +{"m": 32, "n": 24, "k": 24, "tile_m": 1, "tile_n": 8, "threads": 96, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2431.14, "source": "autotuned"}, +{"m": 32, "n": 24, "k": 26, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 2435.09, "source": "autotuned"}, +{"m": 32, "n": 24, "k": 32, "tile_m": 1, "tile_n": 6, "threads": 128, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 2543.84, "source": "autotuned"}, +{"m": 32, "n": 25, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 224, "grouping": 5, "minblocks": 7, "algorithm": "medium", "perf": 1752.88, "source": "autotuned"}, +{"m": 32, "n": 25, "k": 7, "tile_m": 1, "tile_n": 5, "threads": 192, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1916.66, "source": "autotuned"}, +{"m": 32, "n": 25, "k": 11, "tile_m": 1, "tile_n": 4, "threads": 384, "grouping": 5, "minblocks": 5, "algorithm": "medium", "perf": 2094.56, "source": "autotuned"}, +{"m": 32, "n": 25, "k": 13, "tile_m": 1, "tile_n": 9, "threads": 96, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 2181.89, "source": "autotuned"}, +{"m": 32, "n": 25, "k": 26, "tile_m": 1, "tile_n": 5, "threads": 192, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 2429.15, "source": "autotuned"}, +{"m": 32, "n": 25, "k": 32, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 2519.15, "source": "autotuned"}, +{"m": 32, "n": 25, "k": 45, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 2569.3, "source": "autotuned"}, +{"m": 32, "n": 26, "k": 5, "tile_m": 1, "tile_n": 5, "threads": 192, "grouping": 5, "minblocks": 8, "algorithm": "medium", "perf": 1789.55, "source": "autotuned"}, +{"m": 32, "n": 26, "k": 7, "tile_m": 1, "tile_n": 4, "threads": 256, "grouping": 4, "minblocks": 7, "algorithm": "medium", "perf": 1986.08, "source": "autotuned"}, +{"m": 32, "n": 26, "k": 9, "tile_m": 1, "tile_n": 4, "threads": 320, "grouping": 4, "minblocks": 5, "algorithm": "medium", "perf": 2071.44, "source": "autotuned"}, +{"m": 32, "n": 26, "k": 12, "tile_m": 1, "tile_n": 10, "threads": 96, "grouping": 3, "minblocks": 9, "algorithm": "medium", "perf": 2213.46, "source": "autotuned"}, +{"m": 32, "n": 26, "k": 24, "tile_m": 1, "tile_n": 9, "threads": 96, "grouping": 3, "minblocks": 6, "algorithm": "medium", "perf": 2496.78, "source": "autotuned"}, +{"m": 32, "n": 26, "k": 28, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 2514.02, "source": "autotuned"}, +{"m": 32, "n": 26, "k": 32, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 2625.43, "source": "autotuned"}, +{"m": 32, "n": 26, "k": 45, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 2612.42, "source": "autotuned"}, +{"m": 32, "n": 28, "k": 4, "tile_m": 1, "tile_n": 4, "threads": 224, "grouping": 5, "minblocks": 7, "algorithm": "medium", "perf": 1706.12, "source": "autotuned"}, +{"m": 32, "n": 28, "k": 13, "tile_m": 1, "tile_n": 4, "threads": 480, "grouping": 4, "minblocks": 4, "algorithm": "medium", "perf": 2294.83, "source": "autotuned"}, +{"m": 32, "n": 28, "k": 25, "tile_m": 2, "tile_n": 7, "threads": 64, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 2558.66, "source": "autotuned"}, +{"m": 32, "n": 28, "k": 26, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 2600.81, "source": "autotuned"}, +{"m": 32, "n": 28, "k": 28, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 2649.48, "source": "autotuned"}, +{"m": 32, "n": 28, "k": 32, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 2746.52, "source": "autotuned"}, +{"m": 32, "n": 28, "k": 45, "tile_m": 1, "tile_n": 7, "threads": 128, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 2759.99, "source": "autotuned"}, +{"m": 32, "n": 29, "k": 29, "tile_m": 1, "tile_n": 8, "threads": 128, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 2631.69, "source": "autotuned"}, +{"m": 32, "n": 32, "k": 4, "tile_m": 2, "tile_n": 8, "threads": 64, "grouping": 32, "minblocks": 5, "algorithm": "medium", "perf": 1811.89, "source": "autotuned"}, +{"m": 32, "n": 32, "k": 7, "tile_m": 1, "tile_n": 8, "threads": 128, "grouping": 4, "minblocks": 8, "algorithm": "medium", "perf": 2196.76, "source": "autotuned"}, +{"m": 32, "n": 32, "k": 10, "tile_m": 2, "tile_n": 8, "threads": 64, "grouping": 4, "minblocks": 2, "algorithm": "medium", "perf": 2425.0, "source": "autotuned"}, +{"m": 32, "n": 32, "k": 11, "tile_m": 1, "tile_n": 9, "threads": 128, "grouping": 3, "minblocks": 7, "algorithm": "medium", "perf": 2468.06, "source": "autotuned"}, +{"m": 32, "n": 32, "k": 16, "tile_m": 1, "tile_n": 8, "threads": 128, "grouping": 3, "minblocks": 5, "algorithm": "medium", "perf": 2733.95, "source": "autotuned"}, +{"m": 32, "n": 32, "k": 22, "tile_m": 1, "tile_n": 8, "threads": 128, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 2801.94, "source": "autotuned"}, +{"m": 32, "n": 32, "k": 24, "tile_m": 1, "tile_n": 8, "threads": 128, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 2848.85, "source": "autotuned"}, +{"m": 32, "n": 32, "k": 25, "tile_m": 1, "tile_n": 8, "threads": 160, "grouping": 3, "minblocks": 3, "algorithm": "medium", "perf": 2821.09, "source": "autotuned"}, +{"m": 32, "n": 32, "k": 26, "tile_m": 3, "tile_n": 4, "threads": 128, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 2853.46, "source": "autotuned"}, +{"m": 32, "n": 32, "k": 29, "tile_m": 1, "tile_n": 8, "threads": 128, "grouping": 3, "minblocks": 2, "algorithm": "medium", "perf": 2876.93, "source": "autotuned"}, +{"m": 32, "n": 32, "k": 32, "tile_m": 1, "tile_n": 8, "threads": 128, "grouping": 3, "minblocks": 1, "algorithm": "medium", "perf": 2970.84, "source": "autotuned"}, +{"m": 32, "n": 32, "k": 45, "tile_m": 2, "tile_n": 4, "threads": 256, "grouping": 4, "minblocks": 1, "algorithm": "medium", "perf": 2972.04, "source": "autotuned"}, +{"m": 32, "n": 45, "k": 5, "tile_m": 1, "tile_n": 8, "threads": 256, "grouping": 5, "minblocks": 5, "algorithm": "medium", "perf": 2090.44, "source": "autotuned"}, +{"m": 32, "n": 45, "k": 7, "tile_m": 1, "tile_n": 6, "threads": 256, "grouping": 4, "minblocks": 6, "algorithm": "medium", "perf": 2357.34, "source": "autotuned"}, +{"m": 32, "n": 45, "k": 9, "tile_m": 1, "tile_n": 6, "threads": 320, "grouping": 5, "minblocks": 4, "algorithm": "medium", "perf": 2485.44, "source": "autotuned"}, +{"m": 32, "n": 45, "k": 28, "tile_m": 1, "tile_n": 12, "threads": 128, "grouping": 3, "minblocks": 4, "algorithm": "medium", "perf": 3112.08, "source": "autotuned"}, +{"m": 45, "n": 45, "k": 45, "tile_m": 3, "tile_n": 8, "w": 16, "v": 32, "threads": 96, "grouping": 16, "minblocks": 1, "algorithm": "largeDB2", "perf": 3610.99, "source": "autotuned"}, +{"m": 55, "n": 55, "k": 32, "tile_m": 2, "tile_n": 7, "threads": 256, "grouping": 5, "minblocks": 2, "algorithm": "medium", "perf": 3960.66, "source": "autotuned"}, +{"m": 56, "n": 56, "k": 56, "tile_m": 7, "tile_n": 4, "w": 20, "v": 44, "threads": 128, "grouping": 16, "minblocks": 2, "algorithm": "largeDB2", "perf": 4524.45, "source": "autotuned"}, +{"m": 64, "n": 64, "k": 64, "tile_m": 2, "tile_n": 8, "w": 24, "v": 56, "threads": 256, "grouping": 16, "minblocks": 2, "algorithm": "largeDB2", "perf": 5272.92, "source": "autotuned"}, +{"m": 72, "n": 72, "k": 72, "tile_m": 5, "tile_n": 9, "w": 12, "v": 36, "threads": 128, "grouping": 16, "minblocks": 1, "algorithm": "largeDB2", "perf": 5054.05, "source": "autotuned"} +] \ No newline at end of file From 658e7dab6b99e711b0aca73b7eb942b1d4c0be35 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Mon, 11 Mar 2019 12:48:21 +0100 Subject: [PATCH 22/88] libcusmm/tune: add option to tune all triplets from another GPU - Complete and update tune.md accordingly - Fix numbering issue in libcusmm/README --- src/acc/libsmm_acc/libcusmm/README.md | 4 +-- src/acc/libsmm_acc/libcusmm/tune.md | 40 ++++++++++++++++++----- src/acc/libsmm_acc/libcusmm/tune_setup.py | 40 +++++++++++++++++------ 3 files changed, 63 insertions(+), 21 deletions(-) diff --git a/src/acc/libsmm_acc/libcusmm/README.md b/src/acc/libsmm_acc/libcusmm/README.md index c610a4e4049..c3f2f8dcacc 100644 --- a/src/acc/libsmm_acc/libcusmm/README.md +++ b/src/acc/libsmm_acc/libcusmm/README.md @@ -78,9 +78,9 @@ Follow the [predictive modelling procedure](predict.md) 2. Add the GPU to the `arch_number` data structure in [`kernels/cusmm_predict.py`](kernels/cusmm_predict.py) -4. Add the necessary code for setting `ARCH_NUMBER` correctly in the [`Makefile`](../../../../Makefile) and in the [`CMakeListst`](CMakeLists.txt) +3. Add the necessary code for setting `ARCH_NUMBER` correctly in the [`Makefile`](../../../../Makefile) and in the [`CMakeListst`](CMakeLists.txt) -5. Add a minimal JSON file `parameters_GPU.json`, containing: +4. Add a minimal JSON file `parameters_GPU.json`, containing: ```json { diff --git a/src/acc/libsmm_acc/libcusmm/tune.md b/src/acc/libsmm_acc/libcusmm/tune.md index 706c39690a8..4b6f148e468 100644 --- a/src/acc/libsmm_acc/libcusmm/tune.md +++ b/src/acc/libsmm_acc/libcusmm/tune.md @@ -59,18 +59,40 @@ The `tune_setup.py` script generates job files. You have to adapt the script to #### 3. Run the script `tune_setup.py` -Specify which GPU you are autotuning for by passing the appropriate `parameters_GPU.json` file as an argument with `-p`. In addition, the script takes as arguments the block sizes you want to add to `libcusmm`. For example, if the system you want to autotune for contains blocks of size 5 and 8, run: +Specify which GPU you are autotuning for by passing the appropriate `parameters_GPU.json` file as an argument with `-p`. +In addition, the script takes as arguments the block sizes you want to add to `libcusmm`. You can specify these as a list of integers or provide a the parameter file of a different GPU from which to read the block sizes to autotune. + +For example, if the system you want to autotune for contains blocks of size 5 and 8, run: ```bash $ ./tune_setup.py 5 8 -p parameters_P100.json -Found 23 parameter sets for 5x5x5 -Found 31 parameter sets for 5x5x8 -Found 107 parameter sets for 5x8x5 -Found 171 parameter sets for 5x8x8 -Found 75 parameter sets for 8x5x5 -Found 107 parameter sets for 8x5x8 -Found 248 parameter sets for 8x8x5 -Found 424 parameter sets for 8x8x8 +Reading parameters from parameters_P100.json +Libcusmm: Found 74096 existing parameter sets, of which 1641 are autotuned and 72455 are predicted. +Requested to autotune 8 triplets +Found 41824 parameter sets for 5x5x5 +Found 83648 parameter sets for 5x5x8 +Found 103072 parameter sets for 5x8x5 +Found 103072 parameter sets for 5x8x8 +Found 103072 parameter sets for 8x5x5 +Found 103072 parameter sets for 8x5x8 +Found 125344 parameter sets for 8x8x5 +Found 125344 parameter sets for 8x8x8 +``` + +Or, if you want to obtain, for the NVIDIA P100, the parameters of the same block sizes as recorded for the NVIDIA K40, run: + +```bash +$ ./tune_setup.py -p parameters_P100.json parameters_K40.json +Reading parameters from parameters_P100.json +Libcusmm: Found 74093 existing parameter sets, of which 1638 are autotuned and 72455 are predicted. +Reading parameters to autotune from parameters_K40.json +Requested to autotune 19 triplets +Found 41824 parameter sets for 5x5x5 +Found 95648 parameter sets for 6x6x6 +Found 110496 parameter sets for 7x7x7 +Found 125344 parameter sets for 8x8x8 +Found 173764 parameter sets for 9x9x9 +... ``` The script will create a directory for each combination of the block sizes: diff --git a/src/acc/libsmm_acc/libcusmm/tune_setup.py b/src/acc/libsmm_acc/libcusmm/tune_setup.py index d3449544491..e5da179e6a5 100755 --- a/src/acc/libsmm_acc/libcusmm/tune_setup.py +++ b/src/acc/libsmm_acc/libcusmm/tune_setup.py @@ -23,7 +23,7 @@ # =============================================================================== -def main(param_fn, cpus_per_node, max_num_nodes, blocksizes): +def main(param_fn, cpus_per_node, max_num_nodes, blocksizes, blocks_from_param_file): # Read existing parameters assert param_fn in arch_number.keys(), ( @@ -45,11 +45,19 @@ def main(param_fn, cpus_per_node, max_num_nodes, blocksizes): ) # Get blocksizes to be autotuned - assert len(set(blocksizes)) == len(blocksizes) - blocksizes.sort() - - # Get (m, n, k) triplets to be autotuned - triples = combinations(*blocksizes) + if blocks_from_param_file: # open and read file + with open(blocksizes) as f: + all_kernels_ref = [ + params_dict_to_kernel(**params) for params in json.load(f) + ] + print("Reading parameters to autotune from %s" % blocksizes) + triples = [(k.m, k.n, k.k) for k in all_kernels_ref if k.autotuned] + else: + assert len(set(blocksizes)) == len(blocksizes) + blocksizes.sort() + # Get (m, n, k) triplets to be autotuned + triples = combinations(*blocksizes) + print("Requested to autotune %d triplets" % len(triples)) for (m, n, k) in triples: existing = [kern for kern in autotuned_kernels if kern.can_handle(m, n, k)] @@ -383,9 +391,21 @@ def combinations(*sizes): "blocksizes", metavar="BLOCKSIZE", nargs="+", - type=int, - help="Blocksize(s) to autotune", - ) + type=str, + help='Blocksize(s) to autotune. They can be provided as a list of integers (eg. "23",' + + ' "4 5 13", "32 45") or provide a parameter file from which to read the blocksizes ' + + 'to autotune, of the format parameters_GPU.json.') args = parser.parse_args() - main(args.params, args.cpus_per_node, args.nodes, args.blocksizes) + + # ========== + # Blocksizes from parameter file or as list of integers + blocksizes_from_param_file = False + if args.blocksizes[0].isdigit(): # blocksizes is a sequence of strings + args.blocksizes = [int(b) for b in args.blocksizes] + else: # blocksizes is a file name + blocksizes_from_param_file = True + args.blocksizes = args.blocksizes[0] + + # ========== + main(args.params, args.cpus_per_node, args.nodes, args.blocksizes, blocksizes_from_param_file) From 0138e89bcc761734b43cc0d94ebfdd7860b290c2 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Mon, 18 Mar 2019 13:28:06 +0100 Subject: [PATCH 23/88] libcusmm/tune: add option to provide custom path to tune folders --- src/acc/libsmm_acc/libcusmm/tune_setup.py | 34 +++++++++++++++++------ 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/acc/libsmm_acc/libcusmm/tune_setup.py b/src/acc/libsmm_acc/libcusmm/tune_setup.py index e5da179e6a5..c03df2bd008 100755 --- a/src/acc/libsmm_acc/libcusmm/tune_setup.py +++ b/src/acc/libsmm_acc/libcusmm/tune_setup.py @@ -23,7 +23,9 @@ # =============================================================================== -def main(param_fn, cpus_per_node, max_num_nodes, blocksizes, blocks_from_param_file): +def main( + param_fn, cpus_per_node, max_num_nodes, blocksizes, blocks_from_param_file, tune_dir +): # Read existing parameters assert param_fn in arch_number.keys(), ( @@ -45,7 +47,7 @@ def main(param_fn, cpus_per_node, max_num_nodes, blocksizes, blocks_from_param_f ) # Get blocksizes to be autotuned - if blocks_from_param_file: # open and read file + if blocks_from_param_file: # open and read file with open(blocksizes) as f: all_kernels_ref = [ params_dict_to_kernel(**params) for params in json.load(f) @@ -68,7 +70,7 @@ def main(param_fn, cpus_per_node, max_num_nodes, blocksizes, blocks_from_param_f ) continue - outdir = "tune_%dx%dx%d/" % (m, n, k) + outdir = os.path.join(tune_dir, "tune_%dx%dx%d/" % (m, n, k)) if os.path.exists(outdir): print("Directory %s exists already, skipping." % outdir) continue @@ -363,12 +365,20 @@ def combinations(*sizes): """, formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) + parser.add_argument( + "-d", + "--dir", + metavar="tune_directory", + default=".", + type=str, + help="Path from which to read already-existing tune-folders and write new tune-folders", + ) parser.add_argument( "-p", "--params", metavar="parameters_GPU.json", default="parameters_P100.json", - help="Parameter file to extend by this autotuning (pick the right GPU)", + help="Parameter file that this autotuning should extend (pick the right GPU)", ) parser.add_argument( "-c", @@ -394,18 +404,26 @@ def combinations(*sizes): type=str, help='Blocksize(s) to autotune. They can be provided as a list of integers (eg. "23",' + ' "4 5 13", "32 45") or provide a parameter file from which to read the blocksizes ' - + 'to autotune, of the format parameters_GPU.json.') + + "to autotune, of the format parameters_GPU.json.", + ) args = parser.parse_args() # ========== # Blocksizes from parameter file or as list of integers blocksizes_from_param_file = False - if args.blocksizes[0].isdigit(): # blocksizes is a sequence of strings + if args.blocksizes[0].isdigit(): # blocksizes is a sequence of strings args.blocksizes = [int(b) for b in args.blocksizes] - else: # blocksizes is a file name + else: # blocksizes is a file name blocksizes_from_param_file = True args.blocksizes = args.blocksizes[0] # ========== - main(args.params, args.cpus_per_node, args.nodes, args.blocksizes, blocksizes_from_param_file) + main( + args.params, + args.cpus_per_node, + args.nodes, + args.blocksizes, + blocksizes_from_param_file, + args.dir, + ) From 2bd4e1f1ebd0ab26ba055595c1ba472afcdd20e5 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Wed, 20 Mar 2019 16:22:09 +0100 Subject: [PATCH 24/88] libcusmm/tune: add option "max number of jobs to submit" --- src/acc/libsmm_acc/libcusmm/tune.md | 2 +- src/acc/libsmm_acc/libcusmm/tune_submit.py | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/acc/libsmm_acc/libcusmm/tune.md b/src/acc/libsmm_acc/libcusmm/tune.md index 4b6f148e468..9d4954ebea6 100644 --- a/src/acc/libsmm_acc/libcusmm/tune.md +++ b/src/acc/libsmm_acc/libcusmm/tune.md @@ -126,7 +126,7 @@ The script `tune_submit.py` was written for the slurm batch system as used e.g. #### 5. Submit Jobs -Each tune-directory contains a job file. Since there might be many tune-directories, the convenience script `tune_submit.py` can be used to submit jobs. It will go through all the `tune_*`-directories and check if its job has already been submitted or run. For this, the script calls `squeue` in the background and it searches for `slurm-*.out`files. +Each tune-directory contains a job file. Since there might be many tune-directories, the convenience script `tune_submit.py` can be used to submit jobs. It will go through all the `tune_*`-directories and check if its job has already been submitted or run. For this, the script calls `squeue` in the background and it searches for `slurm-*.out`files. In order to limit the number of jobs submitted at a time, a maximum number of jobs to submit can be specified with `-j`. When `tune_submit.py` is called without arguments, it will just list the jobs that could be submitted: diff --git a/src/acc/libsmm_acc/libcusmm/tune_submit.py b/src/acc/libsmm_acc/libcusmm/tune_submit.py index bb5f5b4abbb..f5f0c0d64b2 100755 --- a/src/acc/libsmm_acc/libcusmm/tune_submit.py +++ b/src/acc/libsmm_acc/libcusmm/tune_submit.py @@ -18,7 +18,7 @@ # =============================================================================== -def main(submit_jobs): +def main(submit_jobs, num_jobs): cmd = ["squeue", "--user", os.environ["USER"], "--format=%j", "--nohead"] p = Popen(cmd, stdout=PIPE) @@ -56,6 +56,10 @@ def main(submit_jobs): % d ) + if num_jobs > 0: + if n_submits >= num_jobs: + break + print("Number of jobs submitted: %d" % n_submits) @@ -74,7 +78,15 @@ def main(submit_jobs): formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument("doit", metavar="doit!", nargs="?", type=str) + parser.add_argument( + "-j", + "--num_jobs", + metavar="INT", + default=0, + type=int, + help="Maximum number of jobs to submit. 0: submit all", + ) args = parser.parse_args() submit_jobs = True if args.doit == "doit!" else False - main(submit_jobs) + main(submit_jobs, args.num_jobs) From e97d373f212c9044c93d66b967a345f479ff4a15 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Wed, 3 Apr 2019 15:18:35 +0200 Subject: [PATCH 25/88] libcusmm/tune: fix minor regex issue The performances measured in the tuning procedure may have no decimal part. Fix the capturing regex to reflect this. And remove an obsolete '_' --- src/acc/libsmm_acc/libcusmm/kernels/cusmm_predict.py | 4 +++- src/acc/libsmm_acc/libcusmm/tune_collect.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/acc/libsmm_acc/libcusmm/kernels/cusmm_predict.py b/src/acc/libsmm_acc/libcusmm/kernels/cusmm_predict.py index 6c0be2136e6..f51a5a2f7e3 100644 --- a/src/acc/libsmm_acc/libcusmm/kernels/cusmm_predict.py +++ b/src/acc/libsmm_acc/libcusmm/kernels/cusmm_predict.py @@ -90,7 +90,9 @@ def descr_to_kernel(kernel_descr, source="autotuned"): from ast import literal_eval - re_kernel_descr = re.compile(r"Kernel_dnt_(\w+)(\(.*\)) , # (\d+\.\d+) GFlop/s") + re_kernel_descr = re.compile( + r"Kernel_dnt_(\w+)(\(.*\)) , # (\d+(?:\.\d+)?) GFlop/s" + ) match = re_kernel_descr.search(kernel_descr).groups() algo = match[0] m = match[1].replace("=", "':") diff --git a/src/acc/libsmm_acc/libcusmm/tune_collect.py b/src/acc/libsmm_acc/libcusmm/tune_collect.py index b0302b7a828..1e8d3bc59c1 100755 --- a/src/acc/libsmm_acc/libcusmm/tune_collect.py +++ b/src/acc/libsmm_acc/libcusmm/tune_collect.py @@ -16,7 +16,7 @@ import argparse from kernels.cusmm_predict import descr_to_kernel -re_mnk = re.compile(r"tune_(\d+)x(\d+)x(\d+)_") +re_mnk = re.compile(r"tune_(\d+)x(\d+)x(\d+)") re_winner = re.compile(r"\nWINNER: \d+ (.+)\n") re_gflops = re.compile(r"# ([0-9.]+) GFlop/s") re_errors = re.compile(r"Number of errors: (\d+)\n") From 37308bc6778645aa5576c94f0c34adb91898cfc4 Mon Sep 17 00:00:00 2001 From: alazzaro Date: Mon, 29 Apr 2019 11:40:42 +0200 Subject: [PATCH 26/88] Clean of DBCSR init --- src/{core => acc/cuda}/dbcsr_cuda_nvtx_cu.cu | 0 src/{core => acc}/dbcsr_cuda_profiling.F | 0 src/core/dbcsr_lib.F | 224 +++++++++---------- src/core/dbcsr_timings.F | 33 +-- src/dbcsr_api.F | 28 --- 5 files changed, 107 insertions(+), 178 deletions(-) rename src/{core => acc/cuda}/dbcsr_cuda_nvtx_cu.cu (100%) rename src/{core => acc}/dbcsr_cuda_profiling.F (100%) diff --git a/src/core/dbcsr_cuda_nvtx_cu.cu b/src/acc/cuda/dbcsr_cuda_nvtx_cu.cu similarity index 100% rename from src/core/dbcsr_cuda_nvtx_cu.cu rename to src/acc/cuda/dbcsr_cuda_nvtx_cu.cu diff --git a/src/core/dbcsr_cuda_profiling.F b/src/acc/dbcsr_cuda_profiling.F similarity index 100% rename from src/core/dbcsr_cuda_profiling.F rename to src/acc/dbcsr_cuda_profiling.F diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 51ccfcea7ba..708fc6dd611 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -21,7 +21,6 @@ MODULE dbcsr_lib int_2_size, & int_4_size, & int_8_size, dp - USE dbcsr_machine, ONLY: default_output_unit USE dbcsr_mpiwrap, ONLY: add_mp_perf_env, & describe_mp_perf_env, & @@ -32,34 +31,26 @@ MODULE dbcsr_lib USE dbcsr_multiply_api, ONLY: dbcsr_multiply_clear_mempools, & dbcsr_multiply_lib_finalize, & dbcsr_multiply_lib_init - - USE dbcsr_timings, ONLY: add_timer_env, & rm_timer_env, & timings_register_hooks USE dbcsr_timings_report, ONLY: cost_type_time, & timings_report_callgraph, & timings_report_print - USE dbcsr_log_handling, ONLY: dbcsr_add_default_logger, & dbcsr_logger_create, & dbcsr_logger_release, & dbcsr_logger_type, & dbcsr_rm_default_logger - USE dbcsr_base_hooks, ONLY: timeset_hook, & timestop_hook,& dbcsr_abort_hook,& dbcsr_warn_hook,& dbcsr_abort_interface, dbcsr_warn_interface, & timeset_interface, timestop_interface - use dbcsr_types, only: dbcsr_mp_obj - use dbcsr_mp_methods, only: dbcsr_mp_new, dbcsr_mp_release, & dbcsr_mp_make_env - - use dbcsr_error_handling, only: dbcsr_error_handling_setup #include "base/dbcsr_base_uses.f90" @@ -73,128 +64,105 @@ MODULE dbcsr_lib PUBLIC :: dbcsr_init_lib, dbcsr_finalize_lib, dbcsr_clear_mempools - LOGICAL, PRIVATE, SAVE :: is_initialized = .FALSE. - - TYPE(dbcsr_logger_type), POINTER :: logger - TYPE(dbcsr_mp_obj) , save :: mp_env - integer, save :: default_group, ext_io_unit - - interface dbcsr_init_lib - module procedure dbcsr_init_lib_def - module procedure dbcsr_init_lib_ext - end interface -CONTAINS + TYPE(dbcsr_logger_type), POINTER :: logger => Null() + TYPE(dbcsr_mp_obj), SAVE :: mp_env + INTEGER, SAVE :: default_group, ext_io_unit + INTERFACE dbcsr_init_lib + MODULE PROCEDURE dbcsr_init_lib_def + MODULE PROCEDURE dbcsr_init_lib_hooks + END INTERFACE - subroutine base_init(mp_comm, io_unit) - INTEGER, INTENT(IN) :: mp_comm - integer, intent(in), optional :: io_unit - integer :: numnodes, mynode - - IF (PRESENT(io_unit)) THEN - ext_io_unit = io_unit - ELSE - ext_io_unit = 0 - CALL mp_environ(numnodes, mynode, mp_comm) - IF (mynode .EQ. 0) ext_io_unit = default_output_unit - ENDIF +CONTAINS - call dbcsr_mp_make_env(mp_env, default_group, mp_comm) - end subroutine ! ************************************************************************************************** -!> \brief Initialize timers -!> -!> Prepares the DBCSR library for use. +!> \brief Initialize the DBCSR library using internal loggers and timer callbacks +!> \param mp_comm ... +!> \param io_unit ... ! ************************************************************************************************** - subroutine dbcsr_init_timer() - NULLIFY (logger) + SUBROUTINE dbcsr_init_lib_def(mp_comm, io_unit) + INTEGER, INTENT(IN) :: mp_comm + INTEGER, INTENT(IN), OPTIONAL :: io_unit + + CALL dbcsr_init_lib_pre(mp_comm, io_unit) + ! Declare loggers and timers CALL dbcsr_logger_create(logger, mp_env=mp_env, & - default_global_unit_nr=ext_io_unit, & - close_global_unit_on_dealloc=.FALSE.) + default_global_unit_nr=ext_io_unit, & + close_global_unit_on_dealloc=.FALSE.) CALL dbcsr_add_default_logger(logger) CALL dbcsr_logger_release(logger) CALL dbcsr_error_handling_setup() - call timings_register_hooks() + CALL timings_register_hooks() CALL add_mp_perf_env() CALL add_timer_env() - end subroutine - + ! + CALL dbcsr_init_lib_low() + END SUBROUTINE dbcsr_init_lib_def ! ************************************************************************************************** -!> \brief Kill timers -!> -!> Prepares the DBCSR library for use. +!> \brief Initialize the DBCSR library using external loggers and timer callbacks +!> \param mp_comm ... +!> \param in_timeset_hook ... +!> \param in_timestop_hook ... +!> \param in_abort_hook ... +!> \param in_warn_hook ... +!> \param io_unit ... ! ************************************************************************************************** - subroutine dbcsr_finalize_timer() - CALL dbcsr_rm_default_logger() - CALL dbcsr_mp_release(mp_env) - call mp_comm_free(default_group) - CALL rm_mp_perf_env() - CALL rm_timer_env() - end subroutine + SUBROUTINE dbcsr_init_lib_hooks(mp_comm, & + in_timeset_hook, in_timestop_hook, & + in_abort_hook, in_warn_hook, io_unit) + INTEGER, INTENT(IN) :: mp_comm + PROCEDURE(timeset_interface), INTENT(IN), POINTER :: in_timeset_hook + PROCEDURE(timestop_interface), INTENT(IN), POINTER :: in_timestop_hook + PROCEDURE(dbcsr_abort_interface), INTENT(IN), POINTER :: in_abort_hook + PROCEDURE(dbcsr_warn_interface), INTENT(IN), POINTER :: in_warn_hook + INTEGER, INTENT(IN), OPTIONAL :: io_unit + + CALL dbcsr_init_lib_pre(mp_comm, io_unit) + ! + timeset_hook => in_timeset_hook + timestop_hook => in_timestop_hook + dbcsr_abort_hook => in_abort_hook + dbcsr_warn_hook => in_warn_hook + ! + CALL dbcsr_init_lib_low() + END SUBROUTINE dbcsr_init_lib_hooks - ! ************************************************************************************************** -!> \brief Print timers +!> \brief Initialize the DBCSR library !> !> Prepares the DBCSR library for use. +!> \param mp_comm ... +!> \param io_unit ... ! ************************************************************************************************** - subroutine dbcsr_print_timer() - CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) - end subroutine - - - -! ************************************************************************************************** -!> \brief Initialize the DBCSR library using internal timer callbacks -! ************************************************************************************************** - SUBROUTINE dbcsr_init_lib_def(mp_comm, io_unit) + SUBROUTINE dbcsr_init_lib_pre(mp_comm, io_unit) INTEGER, INTENT(IN) :: mp_comm - integer, intent(in), optional :: io_unit - call base_init(mp_comm, io_unit) - call dbcsr_init_timer() - call dbcsr_init_lib_only() - end subroutine + INTEGER, INTENT(IN), OPTIONAL :: io_unit - -! ************************************************************************************************** -!> \brief Initialize the DBCSR library using external timer callbacks -! ************************************************************************************************** - SUBROUTINE dbcsr_init_lib_ext(mp_comm, io_unit, & - in_timeset_hook, in_timestop_hook, & - in_abort_hook, in_warn_hook) - INTEGER, INTENT(IN) :: mp_comm - integer, intent(in), optional :: io_unit + INTEGER :: numnodes, mynode - PROCEDURE(timeset_interface), INTENT(IN), POINTER :: in_timeset_hook - PROCEDURE(timestop_interface), INTENT(IN), POINTER :: in_timestop_hook - PROCEDURE(dbcsr_abort_interface), INTENT(IN), POINTER :: in_abort_hook - PROCEDURE(dbcsr_warn_interface), INTENT(IN), POINTER :: in_warn_hook - - call base_init(mp_comm, io_unit) - call timings_register_hooks(in_timeset_hook, in_timestop_hook,& - in_abort_hook, in_warn_hook) - - call dbcsr_init_lib_only() - end subroutine + IF (PRESENT(io_unit)) THEN + ext_io_unit = io_unit + ELSE + ext_io_unit = 0 + CALL mp_environ(numnodes, mynode, mp_comm) + IF (mynode .EQ. 0) ext_io_unit = default_output_unit + ENDIF + CALL dbcsr_mp_make_env(mp_env, default_group, mp_comm) + END SUBROUTINE dbcsr_init_lib_pre - ! ************************************************************************************************** !> \brief Initialize the DBCSR library !> !> Prepares the DBCSR library for use. ! ************************************************************************************************** - SUBROUTINE dbcsr_init_lib_only() + SUBROUTINE dbcsr_init_lib_low() - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_init_lib', routineP = moduleN//':'//routineN + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_init_low', routineP = moduleN//':'//routineN INTEGER :: error_handle, ithread, nthreads - - !integer, dimension(2) :: npdims - !integer, dimension(2) :: myploc - !INTEGER, DIMENSION(:, :), POINTER :: pgrid IF (is_initialized) RETURN CALL timeset(routineN, error_handle) @@ -229,39 +197,32 @@ SUBROUTINE dbcsr_init_lib_only() is_initialized = .TRUE. CALL timestop(error_handle) - END SUBROUTINE + END SUBROUTINE dbcsr_init_lib_low ! ************************************************************************************************** !> \brief Finalize the DBCSR library !> !> Cleans up after the DBCSR library. Used to deallocate persistent objects. -!> \param group ... -!> \param output_unit ... ! ************************************************************************************************** SUBROUTINE dbcsr_finalize_lib() - !INTEGER, INTENT(IN) :: group - !INTEGER, INTENT(IN), OPTIONAL :: output_unit - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_finalize_lib', & routineP = moduleN//':'//routineN - INTEGER :: error_handle, io_unit, ithread !, mynode + INTEGER :: error_handle, ithread IF (.NOT. is_initialized) RETURN CALL timeset(routineN, error_handle) - io_unit = ext_io_unit - - IF (io_unit > 0) THEN - WRITE (UNIT=io_unit, FMT="(/,T2,A)") REPEAT("-", 79) - WRITE (UNIT=io_unit, FMT="(T2,A,T80,A)") "-", "-" - WRITE (UNIT=io_unit, FMT="(T2,A,T35,A,T80,A)") "-", "DBCSR STATISTICS", "-" - WRITE (UNIT=io_unit, FMT="(T2,A,T80,A)") "-", "-" - WRITE (UNIT=io_unit, FMT="(T2,A)") REPEAT("-", 79) + IF (ext_io_unit > 0) THEN + WRITE (UNIT=ext_io_unit, FMT="(/,T2,A)") REPEAT("-", 79) + WRITE (UNIT=ext_io_unit, FMT="(T2,A,T80,A)") "-", "-" + WRITE (UNIT=ext_io_unit, FMT="(T2,A,T35,A,T80,A)") "-", "DBCSR STATISTICS", "-" + WRITE (UNIT=ext_io_unit, FMT="(T2,A,T80,A)") "-", "-" + WRITE (UNIT=ext_io_unit, FMT="(T2,A)") REPEAT("-", 79) END IF -!$OMP PARALLEL DEFAULT(NONE) PRIVATE(ithread) SHARED(io_unit, default_group, cublas_handles) - CALL dbcsr_multiply_lib_finalize(default_group, io_unit) +!$OMP PARALLEL DEFAULT(NONE) PRIVATE(ithread) SHARED(ext_io_unit, default_group, cublas_handles) + CALL dbcsr_multiply_lib_finalize(default_group, ext_io_unit) ithread = 0 !$ ithread = omp_get_thread_num() CALL cublas_handle_destroy(cublas_handles(ithread + 1)) @@ -270,18 +231,41 @@ SUBROUTINE dbcsr_finalize_lib() DEALLOCATE (cublas_handles) !$OMP END MASTER !$OMP END PARALLEL - IF (io_unit > 0) WRITE (UNIT=io_unit, FMT="(T2,A)") REPEAT("-", 79) - - CALL describe_mp_perf_env(io_unit) + IF (ext_io_unit > 0) WRITE (UNIT=ext_io_unit, FMT="(T2,A)") REPEAT("-", 79) is_initialized = .FALSE. + CALL dbcsr_print_timers() + CALL timestop(error_handle) - call dbcsr_print_timer() - call dbcsr_finalize_timer() + IF (ASSOCIATED(logger)) THEN + CALL dbcsr_rm_default_logger() + CALL rm_mp_perf_env() + CALL rm_timer_env() + NULLIFY(logger) + ENDIF + CALL dbcsr_mp_release(mp_env) + CALL mp_comm_free(default_group) + NULLIFY(timeset_hook) + NULLIFY(timestop_hook) + NULLIFY(dbcsr_abort_hook) + NULLIFY(dbcsr_warn_hook) + END SUBROUTINE dbcsr_finalize_lib +! ************************************************************************************************** +!> \brief Print timers +!> +!> Prepares the DBCSR library for use. +! ************************************************************************************************** + SUBROUTINE dbcsr_print_timers() + CALL describe_mp_perf_env(ext_io_unit) + CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) + ! Dump callgraph + ! CALL timings_report_callgraph("test.callgraph") + END SUBROUTINE dbcsr_print_timers + ! ************************************************************************************************** !> \brief Deallocate memory contained in mempools ! ************************************************************************************************** diff --git a/src/core/dbcsr_timings.F b/src/core/dbcsr_timings.F index bca7265e551..ad8653d0320 100644 --- a/src/core/dbcsr_timings.F +++ b/src/core/dbcsr_timings.F @@ -18,11 +18,7 @@ ! ************************************************************************************************** MODULE dbcsr_timings USE dbcsr_base_hooks, ONLY: timeset_hook, & - timestop_hook,& - dbcsr_abort_hook,& - dbcsr_warn_hook, & - dbcsr_abort_interface, dbcsr_warn_interface, & - timeset_interface, timestop_interface + timestop_hook USE dbcsr_cuda_profiling, ONLY: cuda_mem_info, & cuda_nvtx_range_pop, & cuda_nvtx_range_push @@ -69,37 +65,16 @@ MODULE dbcsr_timings INTEGER, PUBLIC, PARAMETER :: default_timings_level = 1 INTEGER, PUBLIC, SAVE :: global_timings_level = default_timings_level - interface timings_register_hooks - MODULE PROCEDURE timings_register_hooks_def - MODULE PROCEDURE timings_register_hooks_ext - end interface CONTAINS ! ************************************************************************************************** !> \brief Registers handlers with base_hooks.F !> \author Ole Schuett ! ************************************************************************************************** - SUBROUTINE timings_register_hooks_def() + SUBROUTINE timings_register_hooks() timeset_hook => timeset_handler timestop_hook => timestop_handler - dbcsr_abort_hook => NULL() - dbcsr_warn_hook => NULL() - END SUBROUTINE timings_register_hooks_def - - - SUBROUTINE timings_register_hooks_ext(in_timeset_hook, in_timestop_hook, & - in_abort_hook, in_warn_hook) - PROCEDURE(timeset_interface), INTENT(IN), POINTER :: in_timeset_hook - PROCEDURE(timestop_interface), INTENT(IN), POINTER :: in_timestop_hook - PROCEDURE(dbcsr_abort_interface), INTENT(IN), POINTER :: in_abort_hook - PROCEDURE(dbcsr_warn_interface), INTENT(IN), POINTER :: in_warn_hook - - timeset_hook => in_timeset_hook - timestop_hook => in_timestop_hook - dbcsr_abort_hook => in_abort_hook - dbcsr_warn_hook => in_warn_hook - - END SUBROUTINE timings_register_hooks_ext + END SUBROUTINE timings_register_hooks ! ************************************************************************************************** !> \brief adds the given timer_env to the top of the stack @@ -161,8 +136,6 @@ SUBROUTINE rm_timer_env() timer_env => list_pop(timers_stack) CALL timer_env_release(timer_env) IF (list_size(timers_stack) == 0) CALL list_destroy(timers_stack) - timeset_hook => NULL() - timestop_hook => NULL() END SUBROUTINE rm_timer_env ! ************************************************************************************************** diff --git a/src/dbcsr_api.F b/src/dbcsr_api.F index 35ab8791a2d..94cec6ccb64 100644 --- a/src/dbcsr_api.F +++ b/src/dbcsr_api.F @@ -132,10 +132,6 @@ MODULE dbcsr_api int_8, & real_4, & real_8 - USE dbcsr_base_hooks, ONLY: timeset_hook, timestop_hook, & - timeset_interface, timestop_interface, & - dbcsr_abort_hook, dbcsr_warn_hook, & - dbcsr_abort_interface, dbcsr_warn_interface USE dbcsr_mpiwrap, ONLY: mp_cart_rank, & mp_environ @@ -301,9 +297,6 @@ MODULE dbcsr_api PUBLIC :: dbcsr_binary_read PUBLIC :: dbcsr_test_binary_io - ! hooks - PUBLIC :: dbcsr_set_hooks - ! ----------------------------------------------------------------------------------------------- TYPE dbcsr_p_type TYPE(dbcsr_type), POINTER :: matrix => Null() @@ -1672,27 +1665,6 @@ SUBROUTINE dbcsr_add_block_node(matrix, block_row, block_col, block) IF (.NOT. is_there) block(:, :) = 0.0_dp END SUBROUTINE dbcsr_add_block_node -! ************************************************************************************************** -!> \brief Set time and error handlers -!> \param[in] in_timeset_hook -!> \param[in] in_timestop_hook -!> \param[in] in_abort_hook -!> \param[in] in_warn_hook -! ************************************************************************************************** - SUBROUTINE dbcsr_set_hooks(in_timeset_hook, in_timestop_hook, & - in_abort_hook, in_warn_hook) - PROCEDURE(timeset_interface), INTENT(IN), POINTER :: in_timeset_hook - PROCEDURE(timestop_interface), INTENT(IN), POINTER :: in_timestop_hook - PROCEDURE(dbcsr_abort_interface), INTENT(IN), POINTER :: in_abort_hook - PROCEDURE(dbcsr_warn_interface), INTENT(IN), POINTER :: in_warn_hook - - timeset_hook => in_timeset_hook - timestop_hook => in_timestop_hook - dbcsr_abort_hook => in_abort_hook - dbcsr_warn_hook => in_warn_hook - - END SUBROUTINE dbcsr_set_hooks - #:include 'data/dbcsr.fypp' #:for n, nametype1, base1, prec1, kind1, type1, dkind1 in inst_params_float ! ************************************************************************************************** From 95b2b1a4613cd731719617c9103e544c63b3fb31 Mon Sep 17 00:00:00 2001 From: alazzaro Date: Mon, 29 Apr 2019 11:51:37 +0200 Subject: [PATCH 27/88] Fix Cmake CUDA error --- src/CMakeLists.txt | 3 ++- src/acc/{ => cuda}/dbcsr_cuda_profiling.F | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename src/acc/{ => cuda}/dbcsr_cuda_profiling.F (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e92a9aed59d..60db7319165 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,6 +12,7 @@ add_fypp_sources(DBCSR_SRCS acc/dbcsr_acc_hostmem.F acc/dbcsr_acc_init.F acc/dbcsr_acc_stream.F + acc/cuda/dbcsr_cuda_profiling.F base/dbcsr_base_hooks.F base/dbcsr_kinds.F base/dbcsr_machine.F @@ -25,7 +26,6 @@ add_fypp_sources(DBCSR_SRCS core/dbcsr_lib.F core/dbcsr_methods.F core/dbcsr_types.F - core/dbcsr_cuda_profiling.F core/dbcsr_dict.F core/dbcsr_error_handling.F core/dbcsr_iter_types.F @@ -109,6 +109,7 @@ set(DBCSR_CUDA_SRCS acc/cuda/acc_cuda_mem.cu acc/cuda/acc_cuda_stream.cu acc/cuda/acc_cuda_init.cu + acc/cuda/dbcsr_cuda_nvtx_cu.cu acc/cublaswrap/cublas.cu ) diff --git a/src/acc/dbcsr_cuda_profiling.F b/src/acc/cuda/dbcsr_cuda_profiling.F similarity index 100% rename from src/acc/dbcsr_cuda_profiling.F rename to src/acc/cuda/dbcsr_cuda_profiling.F From 6a8569d5cddea6ee6f37b96a090c70078a2ab3ac Mon Sep 17 00:00:00 2001 From: alazzaro Date: Mon, 29 Apr 2019 12:05:34 +0200 Subject: [PATCH 28/88] Close #32 --- src/acc/cuda/PACKAGE | 2 +- src/core/PACKAGE | 2 +- src/mpi/dbcsr_mp_methods.F | 29 +++++++++++++++++------------ src/ops/dbcsr_tests.F | 1 - 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/acc/cuda/PACKAGE b/src/acc/cuda/PACKAGE index 91f4f349a7b..b8deaad8a96 100644 --- a/src/acc/cuda/PACKAGE +++ b/src/acc/cuda/PACKAGE @@ -1,5 +1,5 @@ { "description": "Cuda backend for accelerator api", "archive":"libdbcsr", -"requires": ["../include"] +"requires": ["../../base", "../include"] } diff --git a/src/core/PACKAGE b/src/core/PACKAGE index c7dad2a31db..7b48fbfbbac 100644 --- a/src/core/PACKAGE +++ b/src/core/PACKAGE @@ -1,6 +1,6 @@ { "description": "Core DBCSR matrix datastructure", "archive": "libdbcsr", -"requires": ["../acc", "../acc/cublaswrap", "../mpi", "../base", +"requires": ["../acc", "../acc/cuda", "../acc/cublaswrap", "../mpi", "../base", "../data", "../mm", "../utils"], } diff --git a/src/mpi/dbcsr_mp_methods.F b/src/mpi/dbcsr_mp_methods.F index 82aa117bb09..c10915eb93c 100644 --- a/src/mpi/dbcsr_mp_methods.F +++ b/src/mpi/dbcsr_mp_methods.F @@ -20,7 +20,8 @@ MODULE dbcsr_mp_methods mp_comm_free,& mp_environ,& mp_cart_rank,& - mp_dims_create + mp_dims_create,& + mp_comm_null USE dbcsr_types, ONLY: dbcsr_mp_obj !$ USE OMP_LIB, ONLY: omp_get_max_threads, omp_get_thread_num, omp_get_num_threads @@ -182,6 +183,7 @@ SUBROUTINE dbcsr_mp_make_env(mp_env, cart_group, mp_group, & prow INTEGER, ALLOCATABLE, DIMENSION(:, :) :: pgrid INTEGER, DIMENSION(2) :: coord, myploc, npdims + LOGICAL :: alive ! --------------------------------------------------------------------------- @@ -200,21 +202,24 @@ SUBROUTINE dbcsr_mp_make_env(mp_env, cart_group, mp_group, & CALL mp_dims_create(numnodes, npdims) ENDIF CALL mp_cart_create(mp_group, 2, npdims, myploc, cart_group) - CALL mp_environ(numnodes, mynode, cart_group) - ALLOCATE (pgrid(0:npdims(1) - 1, 0:npdims(2) - 1)) - DO prow = 0, npdims(1) - 1 - DO pcol = 0, npdims(2) - 1 - coord = (/prow, pcol/) - CALL mp_cart_rank(cart_group, coord, pgrid(prow, pcol)) + alive = cart_group .NE. mp_comm_null + IF (alive) THEN + CALL mp_environ(numnodes, mynode, cart_group) + ALLOCATE (pgrid(0:npdims(1) - 1, 0:npdims(2) - 1)) + DO prow = 0, npdims(1) - 1 + DO pcol = 0, npdims(2) - 1 + coord = (/prow, pcol/) + CALL mp_cart_rank(cart_group, coord, pgrid(prow, pcol)) + ENDDO ENDDO - ENDDO - CALL dbcsr_mp_new(mp_env, pgrid, cart_group, mynode, numnodes, & - myprow=myploc(1), mypcol=myploc(2)) + CALL dbcsr_mp_new(mp_env, pgrid, cart_group, mynode, numnodes, & + myprow=myploc(1), mypcol=myploc(2)) + ELSE + CALL dbcsr_mp_init(mp_env) + ENDIF CALL timestop(error_handle) END SUBROUTINE dbcsr_mp_make_env - - ! ************************************************************************************************** !> \brief Marks another use of the mp_env !> \param[in,out] mp_env multiprocessor environment diff --git a/src/ops/dbcsr_tests.F b/src/ops/dbcsr_tests.F index ada9288066d..6908f5b751e 100644 --- a/src/ops/dbcsr_tests.F +++ b/src/ops/dbcsr_tests.F @@ -500,5 +500,4 @@ SUBROUTINE test_multiplies_multiproc(group_sizes, & CALL timestop(error_handle) END SUBROUTINE test_multiplies_multiproc - END MODULE dbcsr_tests From a93769615f96e198ff02e99016c486956ce095b3 Mon Sep 17 00:00:00 2001 From: alazzaro Date: Mon, 29 Apr 2019 12:22:13 +0200 Subject: [PATCH 29/88] Remove clusters, close #34 --- src/core/dbcsr_methods.F | 2 -- src/core/dbcsr_types.F | 1 - src/dbcsr_api.F | 38 ++--------------------------------- src/dist/dbcsr_dist_methods.F | 19 +----------------- 4 files changed, 3 insertions(+), 57 deletions(-) diff --git a/src/core/dbcsr_methods.F b/src/core/dbcsr_methods.F index 8336e0afafc..35cd7fc61f2 100644 --- a/src/core/dbcsr_methods.F +++ b/src/core/dbcsr_methods.F @@ -172,8 +172,6 @@ SUBROUTINE dbcsr_distribution_release(dist) CALL array_release(dist%d%col_dist_block) CALL array_release(dist%d%local_rows) CALL array_release(dist%d%local_cols) - CALL array_release(dist%d%row_dist_cluster) - CALL array_release(dist%d%col_dist_cluster) CALL dbcsr_mp_release(dist%d%mp_env) IF (dist%d%has_thread_dist) & CALL array_release(dist%d%thread_dist) diff --git a/src/core/dbcsr_types.F b/src/core/dbcsr_types.F index c916d6ec3d7..144218cf7c5 100644 --- a/src/core/dbcsr_types.F +++ b/src/core/dbcsr_types.F @@ -181,7 +181,6 @@ MODULE dbcsr_types TYPE dbcsr_distribution_type TYPE(array_i1d_obj) :: row_dist_block, col_dist_block TYPE(array_i1d_obj) :: local_rows, local_cols - TYPE(array_i1d_obj) :: row_dist_cluster, col_dist_cluster INTEGER :: max_row_dist, max_col_dist TYPE(array_i1d_obj), DIMENSION(:), POINTER :: other_l_rows => Null() TYPE(array_i1d_obj), DIMENSION(:), POINTER :: other_l_cols => Null() diff --git a/src/dbcsr_api.F b/src/dbcsr_api.F index 94cec6ccb64..ce5c4f6775e 100644 --- a/src/dbcsr_api.F +++ b/src/dbcsr_api.F @@ -839,10 +839,6 @@ END SUBROUTINE dbcsr_get_info !> \param dist ... !> \param row_dist ... !> \param col_dist ... -!> \param row_cluster ... -!> \param col_cluster ... -!> \param has_col_clusters ... -!> \param has_row_clusters ... !> \param nrows ... !> \param ncols ... !> \param has_threads ... @@ -859,15 +855,11 @@ END SUBROUTINE dbcsr_get_info !> \param pcol_group ... ! ************************************************************************************************** SUBROUTINE dbcsr_distribution_get(dist, row_dist, col_dist, & - row_cluster, col_cluster, & - has_col_clusters, has_row_clusters, & nrows, ncols, has_threads, & group, mynode, numnodes, nprows, npcols, myprow, mypcol, pgrid, & subgroups_defined, prow_group, pcol_group) TYPE(dbcsr_distribution_type), INTENT(IN) :: dist - INTEGER, DIMENSION(:), OPTIONAL, POINTER :: row_dist, col_dist, row_cluster, & - col_cluster - LOGICAL, INTENT(OUT), OPTIONAL :: has_col_clusters, has_row_clusters + INTEGER, DIMENSION(:), OPTIONAL, POINTER :: row_dist, col_dist INTEGER, INTENT(OUT), OPTIONAL :: nrows, ncols LOGICAL, INTENT(OUT), OPTIONAL :: has_threads INTEGER, INTENT(OUT), OPTIONAL :: group, mynode, numnodes, nprows, npcols, & @@ -878,10 +870,6 @@ SUBROUTINE dbcsr_distribution_get(dist, row_dist, col_dist, & IF (PRESENT(row_dist)) row_dist => array_data(dist%prv%d%row_dist_block) IF (PRESENT(col_dist)) col_dist => array_data(dist%prv%d%col_dist_block) - IF (PRESENT(row_cluster)) row_cluster => array_data(dist%prv%d%row_dist_cluster) - IF (PRESENT(col_cluster)) col_cluster => array_data(dist%prv%d%col_dist_cluster) - IF (PRESENT(has_col_clusters)) has_col_clusters = array_exists(dist%prv%d%col_dist_cluster) - IF (PRESENT(has_row_clusters)) has_row_clusters = array_exists(dist%prv%d%row_dist_cluster) IF (PRESENT(nrows)) nrows = array_size(dist%prv%d%row_dist_block) IF (PRESENT(ncols)) ncols = array_size(dist%prv%d%col_dist_block) IF (PRESENT(has_threads)) has_threads = dist%prv%d%has_thread_dist @@ -1091,23 +1079,18 @@ END SUBROUTINE dbcsr_verify_matrix !> \param pgrid ... !> \param row_dist ... !> \param col_dist ... -!> \param row_cluster ... -!> \param col_cluster ... !> \param reuse_arrays ... ! ************************************************************************************************** SUBROUTINE dbcsr_distribution_new(dist, template, group, pgrid, row_dist, col_dist, & - row_cluster, col_cluster, reuse_arrays) + reuse_arrays) TYPE(dbcsr_distribution_type), INTENT(OUT) :: dist TYPE(dbcsr_distribution_type), INTENT(IN), & OPTIONAL :: template INTEGER, INTENT(IN), OPTIONAL :: group INTEGER, DIMENSION(:, :), OPTIONAL, POINTER :: pgrid INTEGER, DIMENSION(:), INTENT(IN), POINTER :: row_dist, col_dist - INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL, & - POINTER :: row_cluster, col_cluster LOGICAL, INTENT(IN), OPTIONAL :: reuse_arrays - INTEGER, DIMENSION(:), POINTER :: my_col_cluster, my_row_cluster TYPE(dbcsr_mp_obj) :: mp_env IF (PRESENT(pgrid) .AND. .NOT. PRESENT(group)) & @@ -1125,26 +1108,9 @@ SUBROUTINE dbcsr_distribution_new(dist, template, group, pgrid, row_dist, col_di DBCSR_ABORT("dbcsr_distribution_new: neither template nor group supplied") ENDIF - NULLIFY (my_row_cluster, my_col_cluster) - IF (PRESENT(row_cluster)) THEN - IF (ASSOCIATED(row_cluster)) THEN - IF (ALL(row_cluster > 0)) & - my_row_cluster => row_cluster - ENDIF - ENDIF - - IF (PRESENT(col_cluster)) THEN - IF (ASSOCIATED(col_cluster)) THEN - IF (ALL(col_cluster > 0)) & - my_col_cluster => col_cluster - ENDIF - ENDIF - CALL dbcsr_distribution_new_prv(dist%prv, mp_env, & row_dist_block=row_dist, & col_dist_block=col_dist, & - row_dist_cluster=my_row_cluster, & - col_dist_cluster=my_col_cluster, & reuse_arrays=reuse_arrays) IF (.NOT. PRESENT(template)) & diff --git a/src/dist/dbcsr_dist_methods.F b/src/dist/dbcsr_dist_methods.F index 68231fec20f..aa49bd53e38 100644 --- a/src/dist/dbcsr_dist_methods.F +++ b/src/dist/dbcsr_dist_methods.F @@ -71,20 +71,16 @@ MODULE dbcsr_dist_methods !> \param col_dist_block ... !> \param local_rows ... !> \param local_cols ... -!> \param row_dist_cluster ... -!> \param col_dist_cluster ... !> \param reuse_arrays ... ! ************************************************************************************************** SUBROUTINE dbcsr_distribution_new(dist, mp_env, row_dist_block, col_dist_block, & local_rows, local_cols, & - row_dist_cluster, col_dist_cluster, & reuse_arrays) TYPE(dbcsr_distribution_obj), INTENT(OUT) :: dist TYPE(dbcsr_mp_obj), INTENT(IN) :: mp_env INTEGER, DIMENSION(:), INTENT(IN), POINTER :: row_dist_block, col_dist_block INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL, & - POINTER :: local_rows, local_cols, & - row_dist_cluster, col_dist_cluster + POINTER :: local_rows, local_cols LOGICAL, OPTIONAL :: reuse_arrays CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_distribution_new', & @@ -108,19 +104,6 @@ SUBROUTINE dbcsr_distribution_new(dist, mp_env, row_dist_block, col_dist_block, col_dist_tmp => col_dist_block CALL array_new(dist%d%row_dist_block, row_dist_tmp, gift=reuse_arrays) CALL array_new(dist%d%col_dist_block, col_dist_tmp, gift=reuse_arrays) - CALL array_nullify(dist%d%row_dist_cluster) - CALL array_nullify(dist%d%col_dist_cluster) - - IF (PRESENT(row_dist_cluster)) THEN - IF (ASSOCIATED(row_dist_cluster)) THEN - CALL array_new(dist%d%row_dist_cluster, row_dist_cluster, gift=reuse_arrays) - ENDIF - ENDIF - IF (PRESENT(col_dist_cluster)) THEN - IF (ASSOCIATED(col_dist_cluster)) THEN - CALL array_new(dist%d%col_dist_cluster, col_dist_cluster, gift=reuse_arrays) - ENDIF - ENDIF dist%d%mp_env = mp_env CALL dbcsr_mp_hold(dist%d%mp_env) From b46f3783ac90455b0f2a3cb26db9d5a78e5f5891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Fri, 26 Apr 2019 10:48:32 +0200 Subject: [PATCH 30/88] cmake: fix forgotten rename of OMP_NUM_THREADS var (1186b1c) --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 769ac5e44ee..cac9817793e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -71,7 +71,7 @@ foreach (dbcsr_test ${DBCSR_TESTS}) else () add_test(NAME ${dbcsr_test} COMMAND ./${dbcsr_test}) endif () - set_tests_properties(${dbcsr_test} PROPERTIES ENVIRONMENT OMP_NUM_THREADS=${num_threads}) + set_tests_properties(${dbcsr_test} PROPERTIES ENVIRONMENT OMP_NUM_THREADS=${TEST_OMP_THREADS}) endforeach () if (USE_CUDA) From 7bb29ff617b5c52928d672cc2069a763ab222e0a Mon Sep 17 00:00:00 2001 From: Hans Pabst Date: Tue, 30 Apr 2019 10:11:11 +0200 Subject: [PATCH 31/88] Accommodate DBCSRv2 initialization flow (#176) * Moved libxsmm_init/libxsmm_finalize into dbcsr_init_lib_pre and dbcsr_finalize_lib respectively. * init flow would fail as the timer facility is used before LIBXSMM is initialized; Note: CP2K's timer hook may be based on LIBXSMM. * LIBXSMM init-flow should be limited to MM/host (dbcsr_mm_hostdrv_lib_init). --- src/core/dbcsr_lib.F | 13 ++++++++++++- src/core/dbcsr_timings.F | 6 +++--- src/mm/dbcsr_mm_hostdrv.F | 14 -------------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 708fc6dd611..9ef7ce6e3c2 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -138,6 +138,9 @@ END SUBROUTINE dbcsr_init_lib_hooks !> \param io_unit ... ! ************************************************************************************************** SUBROUTINE dbcsr_init_lib_pre(mp_comm, io_unit) +#if defined(__LIBXSMM) + USE libxsmm, ONLY: libxsmm_init +#endif INTEGER, INTENT(IN) :: mp_comm INTEGER, INTENT(IN), OPTIONAL :: io_unit @@ -151,6 +154,9 @@ SUBROUTINE dbcsr_init_lib_pre(mp_comm, io_unit) IF (mynode .EQ. 0) ext_io_unit = default_output_unit ENDIF CALL dbcsr_mp_make_env(mp_env, default_group, mp_comm) +#if defined(__LIBXSMM) + CALL libxsmm_init() +#endif END SUBROUTINE dbcsr_init_lib_pre ! ************************************************************************************************** @@ -205,6 +211,9 @@ END SUBROUTINE dbcsr_init_lib_low !> Cleans up after the DBCSR library. Used to deallocate persistent objects. ! ************************************************************************************************** SUBROUTINE dbcsr_finalize_lib() +#if defined(__LIBXSMM) + USE libxsmm, ONLY: libxsmm_finalize +#endif CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_finalize_lib', & routineP = moduleN//':'//routineN @@ -247,11 +256,13 @@ SUBROUTINE dbcsr_finalize_lib() ENDIF CALL dbcsr_mp_release(mp_env) CALL mp_comm_free(default_group) +#if defined(__LIBXSMM) + CALL libxsmm_finalize() +#endif NULLIFY(timeset_hook) NULLIFY(timestop_hook) NULLIFY(dbcsr_abort_hook) NULLIFY(dbcsr_warn_hook) - END SUBROUTINE dbcsr_finalize_lib ! ************************************************************************************************** diff --git a/src/core/dbcsr_timings.F b/src/core/dbcsr_timings.F index ad8653d0320..d6f5eed2276 100644 --- a/src/core/dbcsr_timings.F +++ b/src/core/dbcsr_timings.F @@ -160,7 +160,7 @@ SUBROUTINE timer_env_retain(timer_env) IF (.NOT. ASSOCIATED(timer_env)) & DBCSR_ABORT("timer_env_retain: not associated") IF (timer_env%ref_count < 0) & - DBCSR_ABORT("timer_env_retain: negativ ref_count") + DBCSR_ABORT("timer_env_retain: negative ref_count") timer_env%ref_count = timer_env%ref_count + 1 END SUBROUTINE timer_env_retain @@ -180,7 +180,7 @@ SUBROUTINE timer_env_release(timer_env) IF (.NOT. ASSOCIATED(timer_env)) & DBCSR_ABORT("timer_env_release: not associated") IF (timer_env%ref_count < 0) & - DBCSR_ABORT("timer_env_release: negativ ref_count") + DBCSR_ABORT("timer_env_release: negative ref_count") timer_env%ref_count = timer_env%ref_count - 1 IF (timer_env%ref_count > 0) RETURN @@ -461,7 +461,7 @@ SUBROUTINE print_stack(unit_nr) END SUBROUTINE print_stack ! ************************************************************************************************** -!> \brief Internal routine used by timestet and timings_setup_tracing. +!> \brief Internal routine used by timeset_handler and timings_setup_tracing. !> If no routine with given name is found in timer_env%routine_names !> then a new entry is created. !> \param routineN ... diff --git a/src/mm/dbcsr_mm_hostdrv.F b/src/mm/dbcsr_mm_hostdrv.F index f2f19e3af5b..df6e7af92b0 100644 --- a/src/mm/dbcsr_mm_hostdrv.F +++ b/src/mm/dbcsr_mm_hostdrv.F @@ -75,13 +75,6 @@ MODULE dbcsr_mm_hostdrv !> \author Ole Schuett ! ************************************************************************************************** SUBROUTINE dbcsr_mm_hostdrv_lib_init() -#if defined(__LIBXSMM) - USE libxsmm, ONLY: libxsmm_init -!$OMP MASTER - CALL libxsmm_init() -!$OMP END MASTER -!$OMP BARRIER -#endif END SUBROUTINE dbcsr_mm_hostdrv_lib_init ! ************************************************************************************************** @@ -89,13 +82,6 @@ END SUBROUTINE dbcsr_mm_hostdrv_lib_init !> \author Ole Schuett ! ************************************************************************************************** SUBROUTINE dbcsr_mm_hostdrv_lib_finalize() -#if defined(__LIBXSMM) - USE libxsmm, ONLY: libxsmm_finalize -!$OMP BARRIER -!$OMP MASTER - CALL libxsmm_finalize() -!$OMP END MASTER -#endif END SUBROUTINE dbcsr_mm_hostdrv_lib_finalize ! ************************************************************************************************** From bdd10902bd04215d5ce9ee5566add56ce1a058ad Mon Sep 17 00:00:00 2001 From: Hans Pabst Date: Tue, 30 Apr 2019 11:58:56 +0200 Subject: [PATCH 32/88] Fixed crash in dbcsr_finalize_lib when initialized per dbcsr_init_lib_hooks (#178) This change was tested with CP2K plus https://github.com/cp2k/cp2k/pull/330. The root cause of the issue was following flow (stacktrace): > dbcsr_finalize_lib dbcsr_print_timers timings_report_print collect_reports_from_ranks -> "timer_env => get_timer_env()". In get_timer_env, "timer_env => list_peek(timers_stack)" fails due to the "timers_stack" not being setup/allocated. The only function that does that i.e., initializing an internal private SAVE variable is "add_timer_env". PR #171 seems to intent to explicitly omit add_timer_env in case of a hook-based initialization (dbcsr_init_lib_hooks), likely assuming this is controlled externally. This leaves the conclusion that "dbcsr_print_timers" can only be called in dbcsr_finalize_lib in case of the non-hook initialization. The existing "test" of non-hook initialization has been reused (ASSOCIATED(logger)). --- src/core/dbcsr_lib.F | 22 ++++++++++++++-------- src/core/dbcsr_timings.F | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 9ef7ce6e3c2..4727da573d9 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -87,14 +87,18 @@ SUBROUTINE dbcsr_init_lib_def(mp_comm, io_unit) INTEGER, INTENT(IN), OPTIONAL :: io_unit CALL dbcsr_init_lib_pre(mp_comm, io_unit) - ! Declare loggers and timers + ! + ! Declare loggers CALL dbcsr_logger_create(logger, mp_env=mp_env, & default_global_unit_nr=ext_io_unit, & close_global_unit_on_dealloc=.FALSE.) CALL dbcsr_add_default_logger(logger) CALL dbcsr_logger_release(logger) + ! abort/warn hooks CALL dbcsr_error_handling_setup() + ! timeset/timestop hooks CALL timings_register_hooks() + ! timer environment CALL add_mp_perf_env() CALL add_timer_env() ! @@ -121,11 +125,13 @@ SUBROUTINE dbcsr_init_lib_hooks(mp_comm, & INTEGER, INTENT(IN), OPTIONAL :: io_unit CALL dbcsr_init_lib_pre(mp_comm, io_unit) - ! - timeset_hook => in_timeset_hook - timestop_hook => in_timestop_hook + ! abort/warn hooks dbcsr_abort_hook => in_abort_hook dbcsr_warn_hook => in_warn_hook + ! timeset/timestop hooks + timeset_hook => in_timeset_hook + timestop_hook => in_timestop_hook + ! timer environment is assumed ! CALL dbcsr_init_lib_low() END SUBROUTINE dbcsr_init_lib_hooks @@ -244,15 +250,15 @@ SUBROUTINE dbcsr_finalize_lib() is_initialized = .FALSE. - CALL dbcsr_print_timers() - - CALL timestop(error_handle) - IF (ASSOCIATED(logger)) THEN + CALL dbcsr_print_timers() + CALL timestop(error_handle) CALL dbcsr_rm_default_logger() CALL rm_mp_perf_env() CALL rm_timer_env() NULLIFY(logger) + ELSE + CALL timestop(error_handle) ENDIF CALL dbcsr_mp_release(mp_env) CALL mp_comm_free(default_group) diff --git a/src/core/dbcsr_timings.F b/src/core/dbcsr_timings.F index d6f5eed2276..32c64c18386 100644 --- a/src/core/dbcsr_timings.F +++ b/src/core/dbcsr_timings.F @@ -232,7 +232,7 @@ SUBROUTINE timeset_handler(routineN, handle) cs_entry%walltime_start = -HUGE(1.0_dp) cs_entry%energy_start = -HUGE(1.0_dp) ! - routine_name_dsl = routineN ! converte to default_string_length + routine_name_dsl = routineN ! converts to default_string_length routine_id = routine_name2id(routine_name_dsl) ! ! Take timings when the timings_level is appropriated From 8c1b929b181aa747b3a0981b47a1187eee7d7180 Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Tue, 30 Apr 2019 15:04:40 +0200 Subject: [PATCH 33/88] remove print statistics to a separate function --- src/core/dbcsr_lib.F | 54 ++++++++++++++++----- src/dbcsr_api.F | 10 +++- src/mm/dbcsr_mm.F | 76 ++++++++++++++++++------------ src/mm/dbcsr_mm_csr.F | 5 +- src/mm/dbcsr_mm_multrec.F | 5 +- src/mm/dbcsr_mm_sched.F | 18 +++---- src/mm/dbcsr_multiply_api.F | 4 +- tests/dbcsr_performance_driver.F | 4 +- tests/dbcsr_tas_unittest.F | 4 +- tests/dbcsr_tensor_unittest.F | 5 +- tests/dbcsr_test_csr_conversions.F | 3 +- tests/dbcsr_unittest1.F | 4 +- tests/dbcsr_unittest2.F | 4 +- tests/dbcsr_unittest3.F | 4 +- 14 files changed, 133 insertions(+), 67 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 4727da573d9..efc286f76a5 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -30,7 +30,9 @@ MODULE dbcsr_lib mp_comm_free USE dbcsr_multiply_api, ONLY: dbcsr_multiply_clear_mempools, & dbcsr_multiply_lib_finalize, & - dbcsr_multiply_lib_init + dbcsr_multiply_lib_init,& + dbcsr_multiply_lib_statistics + USE dbcsr_timings, ONLY: add_timer_env, & rm_timer_env, & timings_register_hooks @@ -63,6 +65,7 @@ MODULE dbcsr_lib CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'dbcsr_lib' PUBLIC :: dbcsr_init_lib, dbcsr_finalize_lib, dbcsr_clear_mempools + PUBLIC :: dbcsr_lib_statistics LOGICAL, PRIVATE, SAVE :: is_initialized = .FALSE. @@ -211,6 +214,7 @@ SUBROUTINE dbcsr_init_lib_low() CALL timestop(error_handle) END SUBROUTINE dbcsr_init_lib_low + ! ************************************************************************************************** !> \brief Finalize the DBCSR library !> @@ -228,16 +232,9 @@ SUBROUTINE dbcsr_finalize_lib() IF (.NOT. is_initialized) RETURN CALL timeset(routineN, error_handle) - IF (ext_io_unit > 0) THEN - WRITE (UNIT=ext_io_unit, FMT="(/,T2,A)") REPEAT("-", 79) - WRITE (UNIT=ext_io_unit, FMT="(T2,A,T80,A)") "-", "-" - WRITE (UNIT=ext_io_unit, FMT="(T2,A,T35,A,T80,A)") "-", "DBCSR STATISTICS", "-" - WRITE (UNIT=ext_io_unit, FMT="(T2,A,T80,A)") "-", "-" - WRITE (UNIT=ext_io_unit, FMT="(T2,A)") REPEAT("-", 79) - END IF !$OMP PARALLEL DEFAULT(NONE) PRIVATE(ithread) SHARED(ext_io_unit, default_group, cublas_handles) - CALL dbcsr_multiply_lib_finalize(default_group, ext_io_unit) + CALL dbcsr_multiply_lib_finalize() ithread = 0 !$ ithread = omp_get_thread_num() CALL cublas_handle_destroy(cublas_handles(ithread + 1)) @@ -246,12 +243,10 @@ SUBROUTINE dbcsr_finalize_lib() DEALLOCATE (cublas_handles) !$OMP END MASTER !$OMP END PARALLEL - IF (ext_io_unit > 0) WRITE (UNIT=ext_io_unit, FMT="(T2,A)") REPEAT("-", 79) is_initialized = .FALSE. IF (ASSOCIATED(logger)) THEN - CALL dbcsr_print_timers() CALL timestop(error_handle) CALL dbcsr_rm_default_logger() CALL rm_mp_perf_env() @@ -271,6 +266,41 @@ SUBROUTINE dbcsr_finalize_lib() NULLIFY(dbcsr_warn_hook) END SUBROUTINE dbcsr_finalize_lib + + +! ************************************************************************************************** +!> \brief Print the whole DBCSR statistics +!> +!> Prepares the DBCSR library for use. +! ************************************************************************************************** + subroutine dbcsr_lib_statistics(print_timers, callgraph_filename) + logical, intent(in) :: print_timers + CHARACTER(len=*), INTENT(IN), optional :: callgraph_filename + + IF (ext_io_unit > 0) THEN + WRITE (UNIT=ext_io_unit, FMT="(/,T2,A)") REPEAT("-", 79) + WRITE (UNIT=ext_io_unit, FMT="(T2,A,T80,A)") "-", "-" + WRITE (UNIT=ext_io_unit, FMT="(T2,A,T35,A,T80,A)") "-", "DBCSR STATISTICS", "-" + WRITE (UNIT=ext_io_unit, FMT="(T2,A,T80,A)") "-", "-" + WRITE (UNIT=ext_io_unit, FMT="(T2,A)") REPEAT("-", 79) + END IF + + + call dbcsr_multiply_lib_statistics(default_group, ext_io_unit) + + IF (ext_io_unit > 0) WRITE (UNIT=ext_io_unit, FMT="(T2,A)") REPEAT("-", 79) + + if (print_timers) call dbcsr_print_timers() + + ! Dump callgraph + + if (present(callgraph_filename)) then + CALL timings_report_callgraph(callgraph_filename) + endif + end subroutine + + + ! ************************************************************************************************** !> \brief Print timers !> @@ -279,8 +309,6 @@ END SUBROUTINE dbcsr_finalize_lib SUBROUTINE dbcsr_print_timers() CALL describe_mp_perf_env(ext_io_unit) CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) - ! Dump callgraph - ! CALL timings_report_callgraph("test.callgraph") END SUBROUTINE dbcsr_print_timers ! ************************************************************************************************** diff --git a/src/dbcsr_api.F b/src/dbcsr_api.F index ce5c4f6775e..eb608db1daf 100644 --- a/src/dbcsr_api.F +++ b/src/dbcsr_api.F @@ -57,7 +57,7 @@ MODULE dbcsr_api dbcsr_iterator_stop_prv => dbcsr_iterator_stop USE dbcsr_lib, ONLY: dbcsr_clear_mempools, & dbcsr_finalize_lib, & - dbcsr_init_lib + dbcsr_init_lib, dbcsr_lib_statistics USE dbcsr_methods, ONLY: & dbcsr_get_data_size_prv => dbcsr_get_data_size, & dbcsr_get_data_type_prv => dbcsr_get_data_type, & @@ -171,6 +171,7 @@ MODULE dbcsr_api PUBLIC :: dbcsr_print_config PUBLIC :: dbcsr_reset_randmat_seed PUBLIC :: dbcsr_mp_grid_setup + PUBLIC :: dbcsr_print_statistics ! create / release PUBLIC :: dbcsr_distribution_hold @@ -410,6 +411,13 @@ MODULE dbcsr_api CONTAINS + subroutine dbcsr_print_statistics(print_timers, callgraph_filename) + logical, intent(in) :: print_timers + CHARACTER(len=*), INTENT(IN), optional :: callgraph_filename + + call dbcsr_lib_statistics(print_timers, callgraph_filename) + end subroutine + ! ************************************************************************************************** !> \brief ... !> \param dist ... diff --git a/src/mm/dbcsr_mm.F b/src/mm/dbcsr_mm.F index 6a1a669230d..3908919a406 100644 --- a/src/mm/dbcsr_mm.F +++ b/src/mm/dbcsr_mm.F @@ -106,6 +106,9 @@ MODULE dbcsr_mm USE dbcsr_work_operations, ONLY: dbcsr_add_wm_from_matrix, & dbcsr_finalize, & dbcsr_work_create + + use dbcsr_mm_sched, only: dbcsr_mm_sched_lib_statistics + #include "base/dbcsr_base_uses.f90" !$ USE OMP_LIB, ONLY: omp_get_thread_num, omp_get_num_threads @@ -120,7 +123,7 @@ MODULE dbcsr_mm REAL, PRIVATE, SAVE :: marketing_flops = 0 - PUBLIC :: dbcsr_multiply_lib_init, dbcsr_multiply_lib_finalize + PUBLIC :: dbcsr_multiply_lib_init, dbcsr_multiply_lib_finalize, dbcsr_multiply_lib_statistics PUBLIC :: dbcsr_multiply_clear_mempools PUBLIC :: dbcsr_multiply_generic @@ -171,23 +174,14 @@ END SUBROUTINE dbcsr_multiply_lib_init !> \param output_unit ... !> \author Ole Schuett ! ************************************************************************************************** - SUBROUTINE dbcsr_multiply_lib_finalize(group, output_unit) - INTEGER, INTENT(IN) :: group, output_unit + SUBROUTINE dbcsr_multiply_lib_finalize() CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_multiply_lib_finalize', & routineP = moduleN//':'//routineN - CHARACTER(len=1000) :: msg - INTEGER :: ilimit, isqrt, isqrt2, ithread - INTEGER(KIND=int_8) :: total_nexchanged - INTEGER(KIND=int_8), & - DIMENSION(SIZE(dbcsr_mpi_size_limits) + 1, 2, 2) :: total_recv_breakdown - REAL :: average, total_marketing_flops, & - total_max_memory - REAL, DIMENSION(2) :: max_recv_data, min_recv_data, & - total_recv_data + INTEGER :: ithread - CALL dbcsr_mm_multrec_lib_finalize(group, output_unit) + CALL dbcsr_mm_multrec_lib_finalize() ithread = 0 !$ ithread = omp_get_thread_num() @@ -207,6 +201,41 @@ SUBROUTINE dbcsr_multiply_lib_finalize(group, output_unit) ! Release 3D communicators CALL release_layers_3D_C_reduction(release_buffers=.TRUE.) + IF (ASSOCIATED(memtype_trsbuffer_1%pool)) & + CALL dbcsr_mempool_destruct(memtype_trsbuffer_1%pool) + IF (ASSOCIATED(memtype_trsbuffer_2%pool)) & + CALL dbcsr_mempool_destruct(memtype_trsbuffer_2%pool) + IF (ASSOCIATED(memtype_abpanel_1%pool)) & + CALL dbcsr_mempool_destruct(memtype_abpanel_1%pool) + IF (ASSOCIATED(memtype_abpanel_2%pool)) & + CALL dbcsr_mempool_destruct(memtype_abpanel_2%pool) + IF (ASSOCIATED(memtype_mpi_product%pool)) & + CALL dbcsr_mempool_destruct(memtype_mpi_product%pool) + IF (acc_stream_associated(stream_1)) & + CALL acc_stream_destroy(stream_1) + IF (acc_stream_associated(stream_2)) & + CALL acc_stream_destroy(stream_2) + IF (has_acc) & + CALL acc_finalize() +!$OMP END MASTER + END SUBROUTINE dbcsr_multiply_lib_finalize + + + subroutine dbcsr_multiply_lib_statistics(group, output_unit) + INTEGER, INTENT(IN) :: group, output_unit + INTEGER(KIND=int_8) :: total_nexchanged + INTEGER(KIND=int_8), & + DIMENSION(SIZE(dbcsr_mpi_size_limits) + 1, 2, 2) :: total_recv_breakdown + REAL :: average, total_marketing_flops, & + total_max_memory + REAL, DIMENSION(2) :: max_recv_data, min_recv_data, & + total_recv_data + INTEGER :: ilimit, isqrt, isqrt2 + CHARACTER(len=1000) :: msg + + + call dbcsr_mm_sched_lib_statistics(group, output_unit) + total_max_memory = max_memory CALL mp_max(total_max_memory, group) @@ -278,25 +307,10 @@ SUBROUTINE dbcsr_multiply_lib_finalize(group, output_unit) DBCSR_WARN(msg) ENDIF ENDIF - IF (ASSOCIATED(memtype_trsbuffer_1%pool)) & - CALL dbcsr_mempool_destruct(memtype_trsbuffer_1%pool) - IF (ASSOCIATED(memtype_trsbuffer_2%pool)) & - CALL dbcsr_mempool_destruct(memtype_trsbuffer_2%pool) - IF (ASSOCIATED(memtype_abpanel_1%pool)) & - CALL dbcsr_mempool_destruct(memtype_abpanel_1%pool) - IF (ASSOCIATED(memtype_abpanel_2%pool)) & - CALL dbcsr_mempool_destruct(memtype_abpanel_2%pool) - IF (ASSOCIATED(memtype_mpi_product%pool)) & - CALL dbcsr_mempool_destruct(memtype_mpi_product%pool) - IF (acc_stream_associated(stream_1)) & - CALL acc_stream_destroy(stream_1) - IF (acc_stream_associated(stream_2)) & - CALL acc_stream_destroy(stream_2) - IF (has_acc) & - CALL acc_finalize() -!$OMP END MASTER - END SUBROUTINE dbcsr_multiply_lib_finalize + end subroutine + + ! ************************************************************************************************** !> \brief Deallocate memory contained in mempools !> \author Ole Schuett diff --git a/src/mm/dbcsr_mm_csr.F b/src/mm/dbcsr_mm_csr.F index 89a040bda0f..a41a78279c0 100644 --- a/src/mm/dbcsr_mm_csr.F +++ b/src/mm/dbcsr_mm_csr.F @@ -111,10 +111,9 @@ SUBROUTINE dbcsr_mm_csr_lib_init() !> \param output_unit ... !> \author Ole Schuett ! ************************************************************************************************** - SUBROUTINE dbcsr_mm_csr_lib_finalize(group, output_unit) - INTEGER, INTENT(IN) :: group, output_unit + SUBROUTINE dbcsr_mm_csr_lib_finalize() - CALL dbcsr_mm_sched_lib_finalize(group, output_unit) + CALL dbcsr_mm_sched_lib_finalize() END SUBROUTINE ! ************************************************************************************************** diff --git a/src/mm/dbcsr_mm_multrec.F b/src/mm/dbcsr_mm_multrec.F index 6dcf6bc0757..e9470e935ad 100644 --- a/src/mm/dbcsr_mm_multrec.F +++ b/src/mm/dbcsr_mm_multrec.F @@ -156,10 +156,9 @@ SUBROUTINE dbcsr_mm_multrec_lib_init() !> \param output_unit ... !> \author Ole Schuett ! ************************************************************************************************** - SUBROUTINE dbcsr_mm_multrec_lib_finalize(group, output_unit) - INTEGER, INTENT(IN) :: group, output_unit + SUBROUTINE dbcsr_mm_multrec_lib_finalize() - CALL dbcsr_mm_csr_lib_finalize(group, output_unit) + CALL dbcsr_mm_csr_lib_finalize() END SUBROUTINE ! ************************************************************************************************** diff --git a/src/mm/dbcsr_mm_sched.F b/src/mm/dbcsr_mm_sched.F index 8157b0c7250..bf4a12d5aa8 100644 --- a/src/mm/dbcsr_mm_sched.F +++ b/src/mm/dbcsr_mm_sched.F @@ -66,7 +66,7 @@ MODULE dbcsr_mm_sched PUBLIC :: dbcsr_mm_sched_type PUBLIC :: dbcsr_mm_sched_lib_init, dbcsr_mm_sched_lib_finalize PUBLIC :: dbcsr_mm_sched_phaseout - PUBLIC :: dbcsr_mm_sched_init, dbcsr_mm_sched_finalize + PUBLIC :: dbcsr_mm_sched_init, dbcsr_mm_sched_finalize, dbcsr_mm_sched_lib_statistics PUBLIC :: dbcsr_mm_sched_process PUBLIC :: dbcsr_mm_sched_begin_burst, dbcsr_mm_sched_end_burst PUBLIC :: dbcsr_mm_sched_barrier @@ -149,14 +149,18 @@ END SUBROUTINE dbcsr_mm_sched_lib_init !> \param output_unit ... !> \author Ole Schuett ! ************************************************************************************************** - SUBROUTINE dbcsr_mm_sched_lib_finalize(group, output_unit) - INTEGER, INTENT(IN) :: group, output_unit - - TYPE(stats_type) :: report + SUBROUTINE dbcsr_mm_sched_lib_finalize() CALL dbcsr_mm_accdrv_lib_finalize() CALL dbcsr_mm_hostdrv_lib_finalize() + END SUBROUTINE dbcsr_mm_sched_lib_finalize + + + subroutine dbcsr_mm_sched_lib_statistics(group, output_unit) + INTEGER, INTENT(IN) :: group, output_unit + + TYPE(stats_type) :: report ! Collect and output statistics --------------------------------------------- !$OMP MASTER @@ -166,9 +170,7 @@ SUBROUTINE dbcsr_mm_sched_lib_finalize(group, output_unit) CALL stats_print_report(report, output_unit) DEALLOCATE (stats_per_thread) !$OMP END MASTER - - END SUBROUTINE dbcsr_mm_sched_lib_finalize - + end subroutine ! ************************************************************************************************** !> \brief Makes sure that the product_wm is cleared. !> \param this ... diff --git a/src/mm/dbcsr_multiply_api.F b/src/mm/dbcsr_multiply_api.F index c37bd8a5c27..4d2eacf6deb 100644 --- a/src/mm/dbcsr_multiply_api.F +++ b/src/mm/dbcsr_multiply_api.F @@ -21,7 +21,8 @@ MODULE dbcsr_multiply_api USE dbcsr_mm, ONLY: dbcsr_multiply_clear_mempools, & dbcsr_multiply_generic, & dbcsr_multiply_lib_finalize, & - dbcsr_multiply_lib_init + dbcsr_multiply_lib_init,& + dbcsr_multiply_lib_statistics USE dbcsr_types, ONLY: dbcsr_type, & dbcsr_type_real_4, & dbcsr_type_real_8 @@ -38,6 +39,7 @@ MODULE dbcsr_multiply_api PUBLIC :: dbcsr_multiply PUBLIC :: dbcsr_multiply_clear_mempools PUBLIC :: dbcsr_multiply_lib_finalize, dbcsr_multiply_lib_init + PUBLIC :: dbcsr_multiply_lib_statistics INTERFACE dbcsr_multiply MODULE PROCEDURE dbcsr_multiply_generic diff --git a/tests/dbcsr_performance_driver.F b/tests/dbcsr_performance_driver.F index daa0265a6b6..b5223a0e99c 100644 --- a/tests/dbcsr_performance_driver.F +++ b/tests/dbcsr_performance_driver.F @@ -21,7 +21,8 @@ PROGRAM dbcsr_performance_driver USE dbcsr_files, ONLY: open_file USE dbcsr_kinds, ONLY: default_string_length USE dbcsr_lib, ONLY: dbcsr_finalize_lib, & - dbcsr_init_lib + dbcsr_init_lib,& + dbcsr_lib_statistics USE dbcsr_machine, ONLY: default_output_unit, & m_getarg, & m_iargc @@ -125,6 +126,7 @@ PROGRAM dbcsr_performance_driver ! free comm CALL mp_comm_free(group) + CALL dbcsr_finalize_lib() ! ! diff --git a/tests/dbcsr_tas_unittest.F b/tests/dbcsr_tas_unittest.F index ab24aee9e79..17bdfc14ca3 100644 --- a/tests/dbcsr_tas_unittest.F +++ b/tests/dbcsr_tas_unittest.F @@ -13,7 +13,8 @@ PROGRAM dbcsr_tas_unittest USE dbcsr_api, ONLY: dbcsr_finalize_lib, & - dbcsr_init_lib + dbcsr_init_lib, & + dbcsr_print_statistics USE dbcsr_tas_base, ONLY: dbcsr_tas_destroy, & dbcsr_tas_info, & dbcsr_tas_nblkcols_total, & @@ -143,6 +144,7 @@ PROGRAM dbcsr_tas_unittest CALL mp_comm_free(mp_comm_C) CALL mp_comm_free(mp_comm_Ct) + call dbcsr_print_statistics(.true.) CALL dbcsr_finalize_lib() CALL mp_world_finalize() diff --git a/tests/dbcsr_tensor_unittest.F b/tests/dbcsr_tensor_unittest.F index 325502b5b5f..aa4a4716123 100644 --- a/tests/dbcsr_tensor_unittest.F +++ b/tests/dbcsr_tensor_unittest.F @@ -14,7 +14,8 @@ PROGRAM dbcsr_tensor_unittest USE dbcsr_api, ONLY: dbcsr_finalize_lib, & dbcsr_init_lib, & - dbcsr_type_real_8 + dbcsr_type_real_8, & + dbcsr_print_statistics USE dbcsr_machine, ONLY: default_output_unit USE dbcsr_mpiwrap, ONLY: mp_cart_create, & mp_comm_free, & @@ -751,6 +752,8 @@ PROGRAM dbcsr_tensor_unittest !--------------------------------------------------------------------------------------------------! ! End tests ! !--------------------------------------------------------------------------------------------------! + + call dbcsr_print_statistics(.true.) ! finalize libdbcsr CALL dbcsr_finalize_lib() diff --git a/tests/dbcsr_test_csr_conversions.F b/tests/dbcsr_test_csr_conversions.F index 35fafe7f8fe..bc526c07c86 100644 --- a/tests/dbcsr_test_csr_conversions.F +++ b/tests/dbcsr_test_csr_conversions.F @@ -21,7 +21,7 @@ PROGRAM dbcsr_test_csr_conversions dbcsr_distribution_type, dbcsr_finalize, dbcsr_finalize_lib, dbcsr_get_stored_coordinates, & dbcsr_init_lib, dbcsr_nblkcols_total, dbcsr_nblkrows_total, dbcsr_norm, & dbcsr_norm_maxabsnorm, dbcsr_put_block, dbcsr_release, dbcsr_to_csr_filter, dbcsr_type, & - dbcsr_type_no_symmetry, dbcsr_type_real_8 + dbcsr_type_no_symmetry, dbcsr_type_real_8, dbcsr_print_statistics USE dbcsr_kinds, ONLY: dp, & real_8 USE dbcsr_machine, ONLY: default_output_unit @@ -137,6 +137,7 @@ PROGRAM dbcsr_test_csr_conversions DEALLOCATE (seed) CALL mp_comm_free(group) + call dbcsr_print_statistics(.true.) CALL dbcsr_finalize_lib() CALL mp_world_finalize() diff --git a/tests/dbcsr_unittest1.F b/tests/dbcsr_unittest1.F index ada8d9bf975..95862f5b7c2 100644 --- a/tests/dbcsr_unittest1.F +++ b/tests/dbcsr_unittest1.F @@ -23,7 +23,8 @@ PROGRAM dbcsr_unittest acc_set_active_device USE dbcsr_kinds, ONLY: dp USE dbcsr_lib, ONLY: dbcsr_finalize_lib, & - dbcsr_init_lib + dbcsr_init_lib, & + dbcsr_lib_statistics USE dbcsr_machine, ONLY: default_output_unit USE dbcsr_mp_methods, ONLY: dbcsr_mp_new, & dbcsr_mp_release @@ -325,6 +326,7 @@ PROGRAM dbcsr_unittest ! finalize mpi CALL mp_comm_free(group) + call dbcsr_lib_statistics(.true.) ! finalize libdbcsr CALL dbcsr_finalize_lib() CALL mp_world_finalize() diff --git a/tests/dbcsr_unittest2.F b/tests/dbcsr_unittest2.F index b6a56fd47c6..e950e0c2719 100644 --- a/tests/dbcsr_unittest2.F +++ b/tests/dbcsr_unittest2.F @@ -23,7 +23,8 @@ PROGRAM dbcsr_unittest acc_set_active_device USE dbcsr_kinds, ONLY: dp USE dbcsr_lib, ONLY: dbcsr_finalize_lib, & - dbcsr_init_lib + dbcsr_init_lib, & + dbcsr_lib_statistics USE dbcsr_machine, ONLY: default_output_unit USE dbcsr_mp_methods, ONLY: dbcsr_mp_new, & dbcsr_mp_release @@ -135,6 +136,7 @@ PROGRAM dbcsr_unittest ! finalize mpi CALL mp_comm_free(group) + call dbcsr_lib_statistics(.true.) ! finalize libdbcsr CALL dbcsr_finalize_lib() diff --git a/tests/dbcsr_unittest3.F b/tests/dbcsr_unittest3.F index 64383d00993..54b308d08f4 100644 --- a/tests/dbcsr_unittest3.F +++ b/tests/dbcsr_unittest3.F @@ -24,7 +24,8 @@ PROGRAM dbcsr_unittest acc_set_active_device USE dbcsr_kinds, ONLY: dp USE dbcsr_lib, ONLY: dbcsr_finalize_lib, & - dbcsr_init_lib + dbcsr_init_lib, & + dbcsr_lib_statistics USE dbcsr_machine, ONLY: default_output_unit USE dbcsr_mp_methods, ONLY: dbcsr_mp_new, & dbcsr_mp_release @@ -137,6 +138,7 @@ PROGRAM dbcsr_unittest ! finalize mpi CALL mp_comm_free(group) + call dbcsr_lib_statistics(.true.) ! finalize libdbcsr CALL dbcsr_finalize_lib() From 5f0f88cad023368c43c098c673a2cdc92918a815 Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Tue, 30 Apr 2019 15:32:05 +0200 Subject: [PATCH 34/88] fix --- tests/dbcsr_performance_driver.F | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/dbcsr_performance_driver.F b/tests/dbcsr_performance_driver.F index b5223a0e99c..7db5539d516 100644 --- a/tests/dbcsr_performance_driver.F +++ b/tests/dbcsr_performance_driver.F @@ -126,7 +126,7 @@ PROGRAM dbcsr_performance_driver ! free comm CALL mp_comm_free(group) - + call dbcsr_lib_statistics(.true.) CALL dbcsr_finalize_lib() ! ! From 63b0553ad547e4113d8afbe3355a27146a865500 Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Tue, 30 Apr 2019 16:02:28 +0200 Subject: [PATCH 35/88] fix --- src/core/dbcsr_lib.F | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index efc286f76a5..f341619132a 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -307,8 +307,10 @@ subroutine dbcsr_lib_statistics(print_timers, callgraph_filename) !> Prepares the DBCSR library for use. ! ************************************************************************************************** SUBROUTINE dbcsr_print_timers() - CALL describe_mp_perf_env(ext_io_unit) - CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) + IF (ASSOCIATED(logger)) THEN + CALL describe_mp_perf_env(ext_io_unit) + CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) + endif END SUBROUTINE dbcsr_print_timers ! ************************************************************************************************** From e948f9db58989f081fa0b4e4e4236e06b5566364 Mon Sep 17 00:00:00 2001 From: Ilia Sivkov Date: Tue, 30 Apr 2019 17:52:56 +0200 Subject: [PATCH 36/88] pretty --- src/core/dbcsr_lib.F | 21 +++++++-------------- src/dbcsr_api.F | 6 +++--- src/mm/dbcsr_mm.F | 10 ++-------- src/mm/dbcsr_mm_csr.F | 2 -- src/mm/dbcsr_mm_sched.F | 3 --- tests/dbcsr_tensor_unittest.F | 2 +- 6 files changed, 13 insertions(+), 31 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index f341619132a..9069dc9f427 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -214,7 +214,6 @@ SUBROUTINE dbcsr_init_lib_low() CALL timestop(error_handle) END SUBROUTINE dbcsr_init_lib_low - ! ************************************************************************************************** !> \brief Finalize the DBCSR library !> @@ -232,7 +231,6 @@ SUBROUTINE dbcsr_finalize_lib() IF (.NOT. is_initialized) RETURN CALL timeset(routineN, error_handle) - !$OMP PARALLEL DEFAULT(NONE) PRIVATE(ithread) SHARED(ext_io_unit, default_group, cublas_handles) CALL dbcsr_multiply_lib_finalize() ithread = 0 @@ -266,8 +264,6 @@ SUBROUTINE dbcsr_finalize_lib() NULLIFY(dbcsr_warn_hook) END SUBROUTINE dbcsr_finalize_lib - - ! ************************************************************************************************** !> \brief Print the whole DBCSR statistics !> @@ -276,7 +272,7 @@ END SUBROUTINE dbcsr_finalize_lib subroutine dbcsr_lib_statistics(print_timers, callgraph_filename) logical, intent(in) :: print_timers CHARACTER(len=*), INTENT(IN), optional :: callgraph_filename - + IF (ext_io_unit > 0) THEN WRITE (UNIT=ext_io_unit, FMT="(/,T2,A)") REPEAT("-", 79) WRITE (UNIT=ext_io_unit, FMT="(T2,A,T80,A)") "-", "-" @@ -284,30 +280,27 @@ subroutine dbcsr_lib_statistics(print_timers, callgraph_filename) WRITE (UNIT=ext_io_unit, FMT="(T2,A,T80,A)") "-", "-" WRITE (UNIT=ext_io_unit, FMT="(T2,A)") REPEAT("-", 79) END IF - - + call dbcsr_multiply_lib_statistics(default_group, ext_io_unit) - + IF (ext_io_unit > 0) WRITE (UNIT=ext_io_unit, FMT="(T2,A)") REPEAT("-", 79) - + if (print_timers) call dbcsr_print_timers() - + ! Dump callgraph - + if (present(callgraph_filename)) then CALL timings_report_callgraph(callgraph_filename) endif end subroutine - - ! ************************************************************************************************** !> \brief Print timers !> !> Prepares the DBCSR library for use. ! ************************************************************************************************** SUBROUTINE dbcsr_print_timers() - IF (ASSOCIATED(logger)) THEN + IF (ASSOCIATED(logger)) THEN CALL describe_mp_perf_env(ext_io_unit) CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) endif diff --git a/src/dbcsr_api.F b/src/dbcsr_api.F index eb608db1daf..90f725e63d4 100644 --- a/src/dbcsr_api.F +++ b/src/dbcsr_api.F @@ -411,12 +411,12 @@ MODULE dbcsr_api CONTAINS - subroutine dbcsr_print_statistics(print_timers, callgraph_filename) + subroutine dbcsr_print_statistics(print_timers, callgraph_filename) logical, intent(in) :: print_timers CHARACTER(len=*), INTENT(IN), optional :: callgraph_filename - call dbcsr_lib_statistics(print_timers, callgraph_filename) - end subroutine + call dbcsr_lib_statistics(print_timers, callgraph_filename) + end subroutine ! ************************************************************************************************** !> \brief ... diff --git a/src/mm/dbcsr_mm.F b/src/mm/dbcsr_mm.F index 3908919a406..cb329724500 100644 --- a/src/mm/dbcsr_mm.F +++ b/src/mm/dbcsr_mm.F @@ -107,7 +107,7 @@ MODULE dbcsr_mm dbcsr_finalize, & dbcsr_work_create - use dbcsr_mm_sched, only: dbcsr_mm_sched_lib_statistics + use dbcsr_mm_sched, only: dbcsr_mm_sched_lib_statistics #include "base/dbcsr_base_uses.f90" @@ -170,8 +170,6 @@ END SUBROUTINE dbcsr_multiply_lib_init ! ************************************************************************************************** !> \brief Finalize the library -!> \param group ... -!> \param output_unit ... !> \author Ole Schuett ! ************************************************************************************************** SUBROUTINE dbcsr_multiply_lib_finalize() @@ -220,7 +218,6 @@ SUBROUTINE dbcsr_multiply_lib_finalize() !$OMP END MASTER END SUBROUTINE dbcsr_multiply_lib_finalize - subroutine dbcsr_multiply_lib_statistics(group, output_unit) INTEGER, INTENT(IN) :: group, output_unit INTEGER(KIND=int_8) :: total_nexchanged @@ -232,8 +229,7 @@ subroutine dbcsr_multiply_lib_statistics(group, output_unit) total_recv_data INTEGER :: ilimit, isqrt, isqrt2 CHARACTER(len=1000) :: msg - - + call dbcsr_mm_sched_lib_statistics(group, output_unit) total_max_memory = max_memory @@ -309,8 +305,6 @@ subroutine dbcsr_multiply_lib_statistics(group, output_unit) ENDIF end subroutine - - ! ************************************************************************************************** !> \brief Deallocate memory contained in mempools !> \author Ole Schuett diff --git a/src/mm/dbcsr_mm_csr.F b/src/mm/dbcsr_mm_csr.F index a41a78279c0..1287eea2ac4 100644 --- a/src/mm/dbcsr_mm_csr.F +++ b/src/mm/dbcsr_mm_csr.F @@ -107,8 +107,6 @@ SUBROUTINE dbcsr_mm_csr_lib_init() ! ************************************************************************************************** !> \brief Finalize the library -!> \param group ... -!> \param output_unit ... !> \author Ole Schuett ! ************************************************************************************************** SUBROUTINE dbcsr_mm_csr_lib_finalize() diff --git a/src/mm/dbcsr_mm_sched.F b/src/mm/dbcsr_mm_sched.F index bf4a12d5aa8..a261fdac05a 100644 --- a/src/mm/dbcsr_mm_sched.F +++ b/src/mm/dbcsr_mm_sched.F @@ -145,8 +145,6 @@ END SUBROUTINE dbcsr_mm_sched_lib_init ! ************************************************************************************************** !> \brief Finalize the library and prints DBCSR statistics -!> \param group ... -!> \param output_unit ... !> \author Ole Schuett ! ************************************************************************************************** SUBROUTINE dbcsr_mm_sched_lib_finalize() @@ -156,7 +154,6 @@ SUBROUTINE dbcsr_mm_sched_lib_finalize() END SUBROUTINE dbcsr_mm_sched_lib_finalize - subroutine dbcsr_mm_sched_lib_statistics(group, output_unit) INTEGER, INTENT(IN) :: group, output_unit diff --git a/tests/dbcsr_tensor_unittest.F b/tests/dbcsr_tensor_unittest.F index aa4a4716123..dd6b1f5090f 100644 --- a/tests/dbcsr_tensor_unittest.F +++ b/tests/dbcsr_tensor_unittest.F @@ -752,7 +752,7 @@ PROGRAM dbcsr_tensor_unittest !--------------------------------------------------------------------------------------------------! ! End tests ! !--------------------------------------------------------------------------------------------------! - + call dbcsr_print_statistics(.true.) ! finalize libdbcsr From 0ce32e62f7640f6d1776aae25831d3ce34aa1f83 Mon Sep 17 00:00:00 2001 From: alazzaro Date: Thu, 2 May 2019 14:29:24 +0200 Subject: [PATCH 37/88] Fix bug in the logger and during print of statistics (it was not considering threads) --- Makefile.inc | 2 +- src/core/dbcsr_lib.F | 58 ++++++++++++++++---------------- src/core/dbcsr_log_handling.F | 2 -- src/dbcsr_api.F | 21 +++++++----- src/mm/dbcsr_mm.F | 18 ++++++---- src/mm/dbcsr_mm_csr.F | 1 - src/mm/dbcsr_mm_multrec.F | 4 --- src/mm/dbcsr_mm_sched.F | 26 ++++++++------ src/mm/dbcsr_multiply_api.F | 9 +---- tests/dbcsr_performance_driver.F | 10 ++++-- tests/dbcsr_unittest1.F | 4 +-- tests/dbcsr_unittest2.F | 4 +-- tests/dbcsr_unittest3.F | 4 +-- 13 files changed, 85 insertions(+), 78 deletions(-) diff --git a/Makefile.inc b/Makefile.inc index 8c6bd2ebff6..eeb0cdeb509 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -231,6 +231,6 @@ endif # # Enable for MacOS compilation (-D__ACCELERATE is for Apple Accelerate library) -#FCFLAGS += -D__NO_STATM_ACCESS -D__ACCELERATE +FCFLAGS += -D__NO_STATM_ACCESS -D__ACCELERATEa ####################################################################################### diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 9069dc9f427..d9ba0fe2852 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -28,11 +28,10 @@ MODULE dbcsr_lib mp_environ, mp_cart_rank,& rm_mp_perf_env,& mp_comm_free - USE dbcsr_multiply_api, ONLY: dbcsr_multiply_clear_mempools, & - dbcsr_multiply_lib_finalize, & - dbcsr_multiply_lib_init,& - dbcsr_multiply_lib_statistics - + USE dbcsr_mm, ONLY: dbcsr_multiply_clear_mempools, & + dbcsr_multiply_lib_finalize, & + dbcsr_multiply_lib_init,& + dbcsr_multiply_print_statistics USE dbcsr_timings, ONLY: add_timer_env, & rm_timer_env, & timings_register_hooks @@ -65,7 +64,7 @@ MODULE dbcsr_lib CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'dbcsr_lib' PUBLIC :: dbcsr_init_lib, dbcsr_finalize_lib, dbcsr_clear_mempools - PUBLIC :: dbcsr_lib_statistics + PUBLIC :: dbcsr_print_statistics LOGICAL, PRIVATE, SAVE :: is_initialized = .FALSE. @@ -243,35 +242,37 @@ SUBROUTINE dbcsr_finalize_lib() !$OMP END PARALLEL is_initialized = .FALSE. + CALL timestop(error_handle) IF (ASSOCIATED(logger)) THEN - CALL timestop(error_handle) CALL dbcsr_rm_default_logger() CALL rm_mp_perf_env() CALL rm_timer_env() NULLIFY(logger) - ELSE - CALL timestop(error_handle) ENDIF + NULLIFY(timeset_hook) + NULLIFY(timestop_hook) + NULLIFY(dbcsr_abort_hook) + NULLIFY(dbcsr_warn_hook) CALL dbcsr_mp_release(mp_env) CALL mp_comm_free(default_group) #if defined(__LIBXSMM) CALL libxsmm_finalize() #endif - NULLIFY(timeset_hook) - NULLIFY(timestop_hook) - NULLIFY(dbcsr_abort_hook) - NULLIFY(dbcsr_warn_hook) END SUBROUTINE dbcsr_finalize_lib ! ************************************************************************************************** -!> \brief Print the whole DBCSR statistics +!> \brief Show the whole DBCSR statistics !> !> Prepares the DBCSR library for use. +!> \param print_timers ... +!> \param callgraph_filename ... ! ************************************************************************************************** - subroutine dbcsr_lib_statistics(print_timers, callgraph_filename) - logical, intent(in) :: print_timers - CHARACTER(len=*), INTENT(IN), optional :: callgraph_filename + SUBROUTINE dbcsr_print_statistics(print_timers, callgraph_filename) + LOGICAL, INTENT(IN), OPTIONAL :: print_timers + CHARACTER(len=*), INTENT(IN), OPTIONAL :: callgraph_filename + + LOGICAL :: my_print_timers IF (ext_io_unit > 0) THEN WRITE (UNIT=ext_io_unit, FMT="(/,T2,A)") REPEAT("-", 79) @@ -281,29 +282,28 @@ subroutine dbcsr_lib_statistics(print_timers, callgraph_filename) WRITE (UNIT=ext_io_unit, FMT="(T2,A)") REPEAT("-", 79) END IF - call dbcsr_multiply_lib_statistics(default_group, ext_io_unit) + call dbcsr_multiply_print_statistics(default_group, ext_io_unit) IF (ext_io_unit > 0) WRITE (UNIT=ext_io_unit, FMT="(T2,A)") REPEAT("-", 79) - if (print_timers) call dbcsr_print_timers() + my_print_timers = .FALSE. + IF (PRESENT(print_timers)) my_print_timers = print_timers + IF (my_print_timers) CALL dbcsr_print_timers() ! Dump callgraph - - if (present(callgraph_filename)) then + IF (PRESENT(callgraph_filename) .AND. ASSOCIATED(logger)) THEN CALL timings_report_callgraph(callgraph_filename) - endif - end subroutine + ENDIF + END SUBROUTINE dbcsr_print_statistics ! ************************************************************************************************** !> \brief Print timers -!> -!> Prepares the DBCSR library for use. ! ************************************************************************************************** SUBROUTINE dbcsr_print_timers() - IF (ASSOCIATED(logger)) THEN - CALL describe_mp_perf_env(ext_io_unit) - CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) - endif + IF (ASSOCIATED(logger)) THEN + CALL describe_mp_perf_env(ext_io_unit) + CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) + ENDIF END SUBROUTINE dbcsr_print_timers ! ************************************************************************************************** diff --git a/src/core/dbcsr_log_handling.F b/src/core/dbcsr_log_handling.F index 251e71698a7..240d6cc0a1c 100644 --- a/src/core/dbcsr_log_handling.F +++ b/src/core/dbcsr_log_handling.F @@ -459,7 +459,6 @@ SUBROUTINE dbcsr_logger_release(logger) DEALLOCATE (logger) END IF END IF - NULLIFY (logger) END SUBROUTINE dbcsr_logger_release ! ************************************************************************************************** @@ -807,4 +806,3 @@ FUNCTION dbcsr_logical_to_string(val) RESULT(res) END FUNCTION dbcsr_logical_to_string END MODULE dbcsr_log_handling - diff --git a/src/dbcsr_api.F b/src/dbcsr_api.F index 90f725e63d4..b87f1df5fdb 100644 --- a/src/dbcsr_api.F +++ b/src/dbcsr_api.F @@ -57,7 +57,7 @@ MODULE dbcsr_api dbcsr_iterator_stop_prv => dbcsr_iterator_stop USE dbcsr_lib, ONLY: dbcsr_clear_mempools, & dbcsr_finalize_lib, & - dbcsr_init_lib, dbcsr_lib_statistics + dbcsr_init_lib, dbcsr_print_statistics_prv => dbcsr_print_statistics USE dbcsr_methods, ONLY: & dbcsr_get_data_size_prv => dbcsr_get_data_size, & dbcsr_get_data_type_prv => dbcsr_get_data_type, & @@ -411,13 +411,6 @@ MODULE dbcsr_api CONTAINS - subroutine dbcsr_print_statistics(print_timers, callgraph_filename) - logical, intent(in) :: print_timers - CHARACTER(len=*), INTENT(IN), optional :: callgraph_filename - - call dbcsr_lib_statistics(print_timers, callgraph_filename) - end subroutine - ! ************************************************************************************************** !> \brief ... !> \param dist ... @@ -1187,6 +1180,18 @@ SUBROUTINE dbcsr_mp_new(mp_env, group, pgrid) END SUBROUTINE dbcsr_mp_new +! ************************************************************************************************** +!> \brief Print statistics +!> \param print_timers ... +!> \param callgraph_filename ... +! ************************************************************************************************** + SUBROUTINE dbcsr_print_statistics(print_timers, callgraph_filename) + LOGICAL, INTENT(IN), OPTIONAL :: print_timers + CHARACTER(len=*), INTENT(IN), OPTIONAL :: callgraph_filename + + CALL dbcsr_print_statistics_prv(print_timers, callgraph_filename) + END SUBROUTINE dbcsr_print_statistics + ! ************************************************************************************************** !> \brief ... !> \param matrix ... diff --git a/src/mm/dbcsr_mm.F b/src/mm/dbcsr_mm.F index cb329724500..a33f830dd0b 100644 --- a/src/mm/dbcsr_mm.F +++ b/src/mm/dbcsr_mm.F @@ -106,8 +106,7 @@ MODULE dbcsr_mm USE dbcsr_work_operations, ONLY: dbcsr_add_wm_from_matrix, & dbcsr_finalize, & dbcsr_work_create - - use dbcsr_mm_sched, only: dbcsr_mm_sched_lib_statistics + USE dbcsr_mm_sched, ONLY: dbcsr_mm_sched_print_statistics #include "base/dbcsr_base_uses.f90" @@ -123,7 +122,8 @@ MODULE dbcsr_mm REAL, PRIVATE, SAVE :: marketing_flops = 0 - PUBLIC :: dbcsr_multiply_lib_init, dbcsr_multiply_lib_finalize, dbcsr_multiply_lib_statistics + PUBLIC :: dbcsr_multiply_lib_init, dbcsr_multiply_lib_finalize + PUBLIC :: dbcsr_multiply_print_statistics PUBLIC :: dbcsr_multiply_clear_mempools PUBLIC :: dbcsr_multiply_generic @@ -218,8 +218,14 @@ SUBROUTINE dbcsr_multiply_lib_finalize() !$OMP END MASTER END SUBROUTINE dbcsr_multiply_lib_finalize - subroutine dbcsr_multiply_lib_statistics(group, output_unit) +! ************************************************************************************************** +!> \brief Print statistics +!> \param group ... +!> \param output_unit ... +! ************************************************************************************************** + SUBROUTINE dbcsr_multiply_print_statistics(group, output_unit) INTEGER, INTENT(IN) :: group, output_unit + INTEGER(KIND=int_8) :: total_nexchanged INTEGER(KIND=int_8), & DIMENSION(SIZE(dbcsr_mpi_size_limits) + 1, 2, 2) :: total_recv_breakdown @@ -230,7 +236,7 @@ subroutine dbcsr_multiply_lib_statistics(group, output_unit) INTEGER :: ilimit, isqrt, isqrt2 CHARACTER(len=1000) :: msg - call dbcsr_mm_sched_lib_statistics(group, output_unit) + call dbcsr_mm_sched_print_statistics(group, output_unit) total_max_memory = max_memory CALL mp_max(total_max_memory, group) @@ -303,7 +309,7 @@ subroutine dbcsr_multiply_lib_statistics(group, output_unit) DBCSR_WARN(msg) ENDIF ENDIF - end subroutine + END SUBROUTINE dbcsr_multiply_print_statistics ! ************************************************************************************************** !> \brief Deallocate memory contained in mempools diff --git a/src/mm/dbcsr_mm_csr.F b/src/mm/dbcsr_mm_csr.F index 1287eea2ac4..26e4139e5c7 100644 --- a/src/mm/dbcsr_mm_csr.F +++ b/src/mm/dbcsr_mm_csr.F @@ -110,7 +110,6 @@ SUBROUTINE dbcsr_mm_csr_lib_init() !> \author Ole Schuett ! ************************************************************************************************** SUBROUTINE dbcsr_mm_csr_lib_finalize() - CALL dbcsr_mm_sched_lib_finalize() END SUBROUTINE diff --git a/src/mm/dbcsr_mm_multrec.F b/src/mm/dbcsr_mm_multrec.F index e9470e935ad..398db70ff1f 100644 --- a/src/mm/dbcsr_mm_multrec.F +++ b/src/mm/dbcsr_mm_multrec.F @@ -152,12 +152,9 @@ SUBROUTINE dbcsr_mm_multrec_lib_init() ! ************************************************************************************************** !> \brief Finalize the library -!> \param group ... -!> \param output_unit ... !> \author Ole Schuett ! ************************************************************************************************** SUBROUTINE dbcsr_mm_multrec_lib_finalize() - CALL dbcsr_mm_csr_lib_finalize() END SUBROUTINE @@ -854,4 +851,3 @@ END SUBROUTINE local_filter_sp #:include "dbcsr_mm_multrec.f90" END MODULE dbcsr_mm_multrec - diff --git a/src/mm/dbcsr_mm_sched.F b/src/mm/dbcsr_mm_sched.F index a261fdac05a..bcf146b9f80 100644 --- a/src/mm/dbcsr_mm_sched.F +++ b/src/mm/dbcsr_mm_sched.F @@ -66,7 +66,8 @@ MODULE dbcsr_mm_sched PUBLIC :: dbcsr_mm_sched_type PUBLIC :: dbcsr_mm_sched_lib_init, dbcsr_mm_sched_lib_finalize PUBLIC :: dbcsr_mm_sched_phaseout - PUBLIC :: dbcsr_mm_sched_init, dbcsr_mm_sched_finalize, dbcsr_mm_sched_lib_statistics + PUBLIC :: dbcsr_mm_sched_init, dbcsr_mm_sched_finalize + PUBLIC :: dbcsr_mm_sched_print_statistics PUBLIC :: dbcsr_mm_sched_process PUBLIC :: dbcsr_mm_sched_begin_burst, dbcsr_mm_sched_end_burst PUBLIC :: dbcsr_mm_sched_barrier @@ -148,26 +149,28 @@ END SUBROUTINE dbcsr_mm_sched_lib_init !> \author Ole Schuett ! ************************************************************************************************** SUBROUTINE dbcsr_mm_sched_lib_finalize() - CALL dbcsr_mm_accdrv_lib_finalize() CALL dbcsr_mm_hostdrv_lib_finalize() - END SUBROUTINE dbcsr_mm_sched_lib_finalize - subroutine dbcsr_mm_sched_lib_statistics(group, output_unit) +! ************************************************************************************************** +!> \brief Prints DBCSR statistics +!> \param group ... +!> \param output_unit ... +! ************************************************************************************************** + SUBROUTINE dbcsr_mm_sched_print_statistics(group, output_unit) INTEGER, INTENT(IN) :: group, output_unit TYPE(stats_type) :: report ! Collect and output statistics --------------------------------------------- -!$OMP MASTER CALL stats_init(report) CALL stats_collect_from_threads(report) CALL stats_collect_from_ranks(report, group) CALL stats_print_report(report, output_unit) DEALLOCATE (stats_per_thread) -!$OMP END MASTER - end subroutine + END SUBROUTINE dbcsr_mm_sched_print_statistics + ! ************************************************************************************************** !> \brief Makes sure that the product_wm is cleared. !> \param this ... @@ -548,10 +551,12 @@ SUBROUTINE stats_collect_from_threads(report) INTEGER :: i, j, nthreads TYPE(stats_type), POINTER :: istats -!$OMP MASTER - +!$OMP PARALLEL DEFAULT(NONE) SHARED(nthreads) +!$OMP MASTER nthreads = 1 !$ nthreads = OMP_GET_NUM_THREADS() +!$OMP END MASTER +!$OMP END PARALLEL DO i = 0, nthreads - 1 istats => stats_per_thread(i) @@ -575,7 +580,7 @@ SUBROUTINE stats_collect_from_threads(report) nstacks_acc=istats%num_mnk_stacks(j, 9)) END DO END DO -!$OMP END MASTER + END SUBROUTINE stats_collect_from_threads ! ************************************************************************************************** @@ -724,4 +729,3 @@ SUBROUTINE stats_print_report(report, output_unit) END SUBROUTINE stats_print_report END MODULE dbcsr_mm_sched - diff --git a/src/mm/dbcsr_multiply_api.F b/src/mm/dbcsr_multiply_api.F index 4d2eacf6deb..6065a90efb5 100644 --- a/src/mm/dbcsr_multiply_api.F +++ b/src/mm/dbcsr_multiply_api.F @@ -18,11 +18,7 @@ MODULE dbcsr_multiply_api real_4, & real_8 USE dbcsr_methods, ONLY: dbcsr_get_data_type - USE dbcsr_mm, ONLY: dbcsr_multiply_clear_mempools, & - dbcsr_multiply_generic, & - dbcsr_multiply_lib_finalize, & - dbcsr_multiply_lib_init,& - dbcsr_multiply_lib_statistics + USE dbcsr_mm, ONLY: dbcsr_multiply_generic USE dbcsr_types, ONLY: dbcsr_type, & dbcsr_type_real_4, & dbcsr_type_real_8 @@ -37,9 +33,6 @@ MODULE dbcsr_multiply_api CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'dbcsr_multiply_api' PUBLIC :: dbcsr_multiply - PUBLIC :: dbcsr_multiply_clear_mempools - PUBLIC :: dbcsr_multiply_lib_finalize, dbcsr_multiply_lib_init - PUBLIC :: dbcsr_multiply_lib_statistics INTERFACE dbcsr_multiply MODULE PROCEDURE dbcsr_multiply_generic diff --git a/tests/dbcsr_performance_driver.F b/tests/dbcsr_performance_driver.F index 7db5539d516..56216b78367 100644 --- a/tests/dbcsr_performance_driver.F +++ b/tests/dbcsr_performance_driver.F @@ -22,7 +22,7 @@ PROGRAM dbcsr_performance_driver USE dbcsr_kinds, ONLY: default_string_length USE dbcsr_lib, ONLY: dbcsr_finalize_lib, & dbcsr_init_lib,& - dbcsr_lib_statistics + dbcsr_print_statistics USE dbcsr_machine, ONLY: default_output_unit, & m_getarg, & m_iargc @@ -126,7 +126,13 @@ PROGRAM dbcsr_performance_driver ! free comm CALL mp_comm_free(group) - call dbcsr_lib_statistics(.true.) + ! + ! print statistics + + CALL dbcsr_print_statistics(.true., "test.callgraph") + + ! + ! finalize DBCSR CALL dbcsr_finalize_lib() ! ! diff --git a/tests/dbcsr_unittest1.F b/tests/dbcsr_unittest1.F index 95862f5b7c2..6e508283bba 100644 --- a/tests/dbcsr_unittest1.F +++ b/tests/dbcsr_unittest1.F @@ -24,7 +24,7 @@ PROGRAM dbcsr_unittest USE dbcsr_kinds, ONLY: dp USE dbcsr_lib, ONLY: dbcsr_finalize_lib, & dbcsr_init_lib, & - dbcsr_lib_statistics + dbcsr_print_statistics USE dbcsr_machine, ONLY: default_output_unit USE dbcsr_mp_methods, ONLY: dbcsr_mp_new, & dbcsr_mp_release @@ -326,7 +326,7 @@ PROGRAM dbcsr_unittest ! finalize mpi CALL mp_comm_free(group) - call dbcsr_lib_statistics(.true.) + call dbcsr_print_statistics(.true.) ! finalize libdbcsr CALL dbcsr_finalize_lib() CALL mp_world_finalize() diff --git a/tests/dbcsr_unittest2.F b/tests/dbcsr_unittest2.F index e950e0c2719..d34b697758c 100644 --- a/tests/dbcsr_unittest2.F +++ b/tests/dbcsr_unittest2.F @@ -24,7 +24,7 @@ PROGRAM dbcsr_unittest USE dbcsr_kinds, ONLY: dp USE dbcsr_lib, ONLY: dbcsr_finalize_lib, & dbcsr_init_lib, & - dbcsr_lib_statistics + dbcsr_print_statistics USE dbcsr_machine, ONLY: default_output_unit USE dbcsr_mp_methods, ONLY: dbcsr_mp_new, & dbcsr_mp_release @@ -136,7 +136,7 @@ PROGRAM dbcsr_unittest ! finalize mpi CALL mp_comm_free(group) - call dbcsr_lib_statistics(.true.) + call dbcsr_print_statistics(.true.) ! finalize libdbcsr CALL dbcsr_finalize_lib() diff --git a/tests/dbcsr_unittest3.F b/tests/dbcsr_unittest3.F index 54b308d08f4..d81b8f91bb9 100644 --- a/tests/dbcsr_unittest3.F +++ b/tests/dbcsr_unittest3.F @@ -25,7 +25,7 @@ PROGRAM dbcsr_unittest USE dbcsr_kinds, ONLY: dp USE dbcsr_lib, ONLY: dbcsr_finalize_lib, & dbcsr_init_lib, & - dbcsr_lib_statistics + dbcsr_print_statistics USE dbcsr_machine, ONLY: default_output_unit USE dbcsr_mp_methods, ONLY: dbcsr_mp_new, & dbcsr_mp_release @@ -138,7 +138,7 @@ PROGRAM dbcsr_unittest ! finalize mpi CALL mp_comm_free(group) - call dbcsr_lib_statistics(.true.) + call dbcsr_print_statistics(.true.) ! finalize libdbcsr CALL dbcsr_finalize_lib() From 8ff5de28bb3294c80916d7636c8718dc540165ae Mon Sep 17 00:00:00 2001 From: alazzaro Date: Thu, 2 May 2019 14:47:52 +0200 Subject: [PATCH 38/88] Allow multiple prints of statistics --- src/core/dbcsr_lib.F | 8 ++++---- src/mm/dbcsr_mm_sched.F | 4 +++- tests/dbcsr_performance_driver.F | 1 - 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index d9ba0fe2852..c3a5ab7b0dc 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -300,10 +300,10 @@ END SUBROUTINE dbcsr_print_statistics !> \brief Print timers ! ************************************************************************************************** SUBROUTINE dbcsr_print_timers() - IF (ASSOCIATED(logger)) THEN - CALL describe_mp_perf_env(ext_io_unit) - CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) - ENDIF + IF (ASSOCIATED(logger)) THEN + CALL describe_mp_perf_env(ext_io_unit) + CALL timings_report_print(ext_io_unit, 0.0_dp, .FALSE., cost_type_time, .TRUE., mp_env) + ENDIF END SUBROUTINE dbcsr_print_timers ! ************************************************************************************************** diff --git a/src/mm/dbcsr_mm_sched.F b/src/mm/dbcsr_mm_sched.F index bcf146b9f80..d3ce663be8f 100644 --- a/src/mm/dbcsr_mm_sched.F +++ b/src/mm/dbcsr_mm_sched.F @@ -151,6 +151,9 @@ END SUBROUTINE dbcsr_mm_sched_lib_init SUBROUTINE dbcsr_mm_sched_lib_finalize() CALL dbcsr_mm_accdrv_lib_finalize() CALL dbcsr_mm_hostdrv_lib_finalize() +!$OMP MASTER + DEALLOCATE (stats_per_thread) +!$OMP END MASTER END SUBROUTINE dbcsr_mm_sched_lib_finalize ! ************************************************************************************************** @@ -168,7 +171,6 @@ SUBROUTINE dbcsr_mm_sched_print_statistics(group, output_unit) CALL stats_collect_from_threads(report) CALL stats_collect_from_ranks(report, group) CALL stats_print_report(report, output_unit) - DEALLOCATE (stats_per_thread) END SUBROUTINE dbcsr_mm_sched_print_statistics ! ************************************************************************************************** diff --git a/tests/dbcsr_performance_driver.F b/tests/dbcsr_performance_driver.F index 56216b78367..6c24cd00995 100644 --- a/tests/dbcsr_performance_driver.F +++ b/tests/dbcsr_performance_driver.F @@ -128,7 +128,6 @@ PROGRAM dbcsr_performance_driver ! ! print statistics - CALL dbcsr_print_statistics(.true., "test.callgraph") ! From 721c946ed91011f39ebe4dccb8e0ffa22dc20628 Mon Sep 17 00:00:00 2001 From: alazzaro Date: Thu, 2 May 2019 16:01:15 +0200 Subject: [PATCH 39/88] Close #138 --- Makefile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 44e6ef6110f..e0c9109278e 100644 --- a/Makefile +++ b/Makefile @@ -25,11 +25,6 @@ LIBNAME := dbcsr LIBRARY := lib$(LIBNAME) default_target: $(LIBRARY) -# Check if FYPP is available =============================================== -ifeq (, $(shell which $(FYPPEXE) 2>/dev/null )) -$(error "No FYPP submodule available, please read README.md on how to properly download DBCSR") -endif - # Read the configuration ==================================================== MODDEPS = "lower" include $(INCLUDEMAKE) @@ -235,6 +230,12 @@ OTHER_HELP += "toolflags : Print flags used with build tools" else # stage 2: Include $(OBJDIR)/all.dep, expand target all, and get list of dependencies. + +# Check if FYPP is available =============================================== +ifeq (, $(shell which $(FYPPEXE) 2>/dev/null )) +$(error "No FYPP submodule available, please read README.md on how to properly download DBCSR") +endif + all: $(foreach e, $(BIN_NAMES), $(e)) ifeq ($(BIN_NAME),) From 7a18f558d0527bb42fe0bdd39440c0e6323bfe3b Mon Sep 17 00:00:00 2001 From: alazzaro Date: Thu, 2 May 2019 20:01:00 +0200 Subject: [PATCH 40/88] Remove CP2K in the comment --- src/acc/libsmm_acc/libcusmm/libcusmm_benchmark.cu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/acc/libsmm_acc/libcusmm/libcusmm_benchmark.cu b/src/acc/libsmm_acc/libcusmm/libcusmm_benchmark.cu index cff7ab13172..ee09e73f094 100644 --- a/src/acc/libsmm_acc/libcusmm/libcusmm_benchmark.cu +++ b/src/acc/libsmm_acc/libcusmm/libcusmm_benchmark.cu @@ -108,7 +108,7 @@ void matInit(double* mat, int mat_n, int x, int y, int seed){ //=========================================================================== -// initialize the task list ("stack" in CP2K lingo) +// initialize the task list ("stack" in DBCSR lingo) // for each of the result matrices we have a random number void stackInit(int *stack, int n_stack, int n_c, double* mat_c, int n_a, double * mat_a, int n_b, double* mat_b, @@ -149,7 +149,7 @@ void stackInit(int *stack, int n_stack, int n_c, double* mat_c, //=========================================================================== -// initialize the task list ("stack" in CP2K lingo) +// initialize the task list ("stack" in DBCSR lingo) void stackInitTransp(int *stack, int n_stack, int mat_m, int mat_n){ int* s = stack; From 5f3e0e6215ecc524827622212ea873bf830fc9bb Mon Sep 17 00:00:00 2001 From: alazzaro Date: Mon, 6 May 2019 11:16:53 +0200 Subject: [PATCH 41/88] Rename trace_ab in dot, close #68 --- src/dbcsr_api.F | 27 +- src/ops/dbcsr_operations.F | 619 ++++++++++++++++++++++++++++++++--- src/ops/dbcsr_operations.f90 | 571 -------------------------------- 3 files changed, 590 insertions(+), 627 deletions(-) delete mode 100644 src/ops/dbcsr_operations.f90 diff --git a/src/dbcsr_api.F b/src/dbcsr_api.F index b87f1df5fdb..67bd0317858 100644 --- a/src/dbcsr_api.F +++ b/src/dbcsr_api.F @@ -99,6 +99,7 @@ MODULE dbcsr_api dbcsr_set_prv => dbcsr_set, & dbcsr_sum_replicated_prv => dbcsr_sum_replicated, & dbcsr_trace_prv => dbcsr_trace, & + dbcsr_dot_prv => dbcsr_dot, & dbcsr_triu_prv => dbcsr_triu, & dbcsr_clear_prv => dbcsr_clear USE dbcsr_test_methods, ONLY: dbcsr_reset_randmat_seed @@ -199,6 +200,7 @@ MODULE dbcsr_api PUBLIC :: dbcsr_get_diag PUBLIC :: dbcsr_filter PUBLIC :: dbcsr_trace + PUBLIC :: dbcsr_dot PUBLIC :: dbcsr_complete_redistribute PUBLIC :: dbcsr_get_block_p PUBLIC :: dbcsr_clear @@ -327,10 +329,13 @@ MODULE dbcsr_api END INTERFACE INTERFACE dbcsr_trace - MODULE PROCEDURE dbcsr_trace_ab_d, dbcsr_trace_ab_s - MODULE PROCEDURE dbcsr_trace_a_d, dbcsr_trace_a_s - MODULE PROCEDURE dbcsr_trace_ab_z, dbcsr_trace_ab_c - MODULE PROCEDURE dbcsr_trace_a_z, dbcsr_trace_a_c + MODULE PROCEDURE dbcsr_trace_d, dbcsr_trace_s + MODULE PROCEDURE dbcsr_trace_z, dbcsr_trace_c + END INTERFACE + + INTERFACE dbcsr_dot + MODULE PROCEDURE dbcsr_dot_d, dbcsr_dot_s + MODULE PROCEDURE dbcsr_dot_z, dbcsr_dot_c END INTERFACE INTERFACE dbcsr_set @@ -1853,7 +1858,7 @@ END SUBROUTINE dbcsr_get_block_p_${nametype1}$ !> \param matrix_a ... !> \param trace ... ! ************************************************************************************************** - SUBROUTINE dbcsr_trace_a_${nametype1}$ (matrix_a, trace) + SUBROUTINE dbcsr_trace_${nametype1}$ (matrix_a, trace) TYPE(dbcsr_type), INTENT(IN) :: matrix_a ${type1}$, INTENT(OUT) :: trace @@ -1863,20 +1868,20 @@ SUBROUTINE dbcsr_trace_a_${nametype1}$ (matrix_a, trace) CALL dbcsr_trace_prv(matrix_a%prv, trace_scalar) CALL dbcsr_scalar_fill_all(trace_scalar) CALL dbcsr_scalar_get_value(trace_scalar, trace) - END SUBROUTINE dbcsr_trace_a_${nametype1}$ + END SUBROUTINE dbcsr_trace_${nametype1}$ ! ************************************************************************************************** !> \brief ... !> \param matrix_a ... !> \param matrix_b ... -!> \param trace ... +!> \param result ... ! ************************************************************************************************** - SUBROUTINE dbcsr_trace_ab_${nametype1}$ (matrix_a, matrix_b, trace) + SUBROUTINE dbcsr_dot_${nametype1}$ (matrix_a, matrix_b, result) TYPE(dbcsr_type), INTENT(IN) :: matrix_a, matrix_b - ${type1}$, INTENT(INOUT) :: trace + ${type1}$, INTENT(INOUT) :: result - CALL dbcsr_trace_prv(matrix_a%prv, matrix_b%prv, trace) - END SUBROUTINE dbcsr_trace_ab_${nametype1}$ + CALL dbcsr_dot_prv(matrix_a%prv, matrix_b%prv, result) + END SUBROUTINE dbcsr_dot_${nametype1}$ ! ************************************************************************************************** !> \brief ... diff --git a/src/ops/dbcsr_operations.F b/src/ops/dbcsr_operations.F index ffbf19e2e07..834df0120ad 100644 --- a/src/ops/dbcsr_operations.F +++ b/src/ops/dbcsr_operations.F @@ -112,7 +112,7 @@ MODULE dbcsr_operations CHARACTER, PARAMETER :: xa = dbcsr_type_hermitian, xb = dbcsr_type_antihermitian, & xc = dbcsr_type_no_symmetry - PUBLIC :: dbcsr_trace, dbcsr_add_on_diag, & + PUBLIC :: dbcsr_trace, dbcsr_dot, dbcsr_add_on_diag, & dbcsr_set, dbcsr_scale, dbcsr_add, dbcsr_copy, & dbcsr_copy_into_existing, & dbcsr_get_diag, dbcsr_set_diag, & @@ -133,11 +133,14 @@ MODULE dbcsr_operations ! generic files. INTERFACE dbcsr_trace - MODULE PROCEDURE dbcsr_trace_a_any - MODULE PROCEDURE dbcsr_trace_a_s, dbcsr_trace_a_d, & - dbcsr_trace_a_c, dbcsr_trace_a_z - MODULE PROCEDURE dbcsr_trace_ab_s, dbcsr_trace_a_b_d, & - dbcsr_trace_ab_c, dbcsr_trace_ab_z + MODULE PROCEDURE dbcsr_trace_any + MODULE PROCEDURE dbcsr_trace_s, dbcsr_trace_d, & + dbcsr_trace_c, dbcsr_trace_z + END INTERFACE + + INTERFACE dbcsr_dot + MODULE PROCEDURE dbcsr_dot_s, dbcsr_dot_d, & + dbcsr_dot_c, dbcsr_dot_z END INTERFACE INTERFACE dbcsr_scale @@ -2514,48 +2517,13 @@ END SUBROUTINE dbcsr_sum_replicated ! ************************************************************************************************** !> \brief ... !> \param matrix_a ... -!> \param matrix_b ... !> \param trace ... ! ************************************************************************************************** - SUBROUTINE dbcsr_trace_a_b_d(matrix_a, matrix_b, trace) - TYPE(dbcsr_type), INTENT(IN) :: matrix_a, matrix_b - REAL(kind=real_8), INTENT(INOUT) :: trace - - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_trace_a_b_d', & - routineP = moduleN//':'//routineN - - INTEGER :: handle - REAL(kind=real_4) :: trace_4 - - CALL timeset(routineN, handle) - IF (dbcsr_get_data_type(matrix_a) .EQ. dbcsr_type_real_8 .AND. & - dbcsr_get_data_type(matrix_b) .EQ. dbcsr_type_real_8 .OR. & - dbcsr_get_data_type(matrix_a) .EQ. dbcsr_type_real_4 .AND. & - dbcsr_get_data_type(matrix_b) .EQ. dbcsr_type_real_8 .OR. & - dbcsr_get_data_type(matrix_a) .EQ. dbcsr_type_real_8 .AND. & - dbcsr_get_data_type(matrix_b) .EQ. dbcsr_type_real_4) THEN - CALL dbcsr_trace_ab_d(matrix_a, matrix_b, trace) - ELSEIF (dbcsr_get_data_type(matrix_a) .EQ. dbcsr_type_real_4 .AND. & - dbcsr_get_data_type(matrix_b) .EQ. dbcsr_type_real_4) THEN - trace_4 = 0.0_real_4 - CALL dbcsr_trace_ab_s(matrix_a, matrix_b, trace_4) - trace = REAL(trace_4, real_8) - ELSE - DBCSR_ABORT("Invalid combination of data type, NYI") - ENDIF - CALL timestop(handle) - END SUBROUTINE dbcsr_trace_a_b_d - -! ************************************************************************************************** -!> \brief ... -!> \param matrix_a ... -!> \param trace ... -! ************************************************************************************************** - SUBROUTINE dbcsr_trace_a_any(matrix_a, trace) + SUBROUTINE dbcsr_trace_any(matrix_a, trace) TYPE(dbcsr_type), INTENT(IN) :: matrix_a TYPE(dbcsr_scalar_type), INTENT(INOUT) :: trace - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_trace_a_any', & + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_trace_any', & routineP = moduleN//':'//routineN INTEGER :: error_handle @@ -2577,7 +2545,7 @@ SUBROUTINE dbcsr_trace_a_any(matrix_a, trace) END SELECT CALL timestop(error_handle) - END SUBROUTINE dbcsr_trace_a_any + END SUBROUTINE dbcsr_trace_any ! ************************************************************************************************** !> \brief check if a block is not in the limits @@ -2768,6 +2736,567 @@ SUBROUTINE dbcsr_clear(matrix) END SUBROUTINE -#:include "dbcsr_operations.f90" +#:include '../data/dbcsr.fypp' +#:for n, nametype1, base1, prec1, kind1, type1, dkind1 in inst_params_float +! ************************************************************************************************** +!> \brief traces a DBCSR matrix +!> \param[in] matrix_a DBCSR matrix +!> \param[out] trace the trace of the matrix +!> +! ************************************************************************************************** + SUBROUTINE dbcsr_trace_${nametype1}$ (matrix_a, trace) + TYPE(dbcsr_type), INTENT(IN) :: matrix_a + ${type1}$, INTENT(INOUT) :: trace + + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_trace_${nametype1}$', & + routineP = moduleN//':'//routineN + + INTEGER :: a_blk, a_col, a_col_size, & + a_nze, a_row, a_row_size, i, & + mynode, error_handle + INTEGER, DIMENSION(:), POINTER :: col_blk_size, row_blk_size, & + row_dist, col_dist + ${type1}$, DIMENSION(:), POINTER :: a_data, data_p + INTEGER, DIMENSION(:, :), POINTER :: pgrid + TYPE(dbcsr_distribution_obj) :: dist + +! --------------------------------------------------------------------------- + CALL timeset(routineN, error_handle) + + row_blk_size => array_data(matrix_a%row_blk_size) + col_blk_size => array_data(matrix_a%col_blk_size) + IF (dbcsr_get_data_type(matrix_a) /= ${dkind1}$) & + DBCSR_ABORT("Incompatible data types") + CALL dbcsr_get_data(matrix_a%data_area, data_p) + dist = dbcsr_distribution(matrix_a) + mynode = dbcsr_mp_mynode(dbcsr_distribution_mp(dist)) + pgrid => dbcsr_mp_pgrid(dbcsr_distribution_mp(dist)) + row_dist => dbcsr_distribution_row_dist(dist) + col_dist => dbcsr_distribution_col_dist(dist) + ! + ! let's go + trace = REAL(0.0, ${kind1}$) + DO a_row = 1, matrix_a%nblkrows_total + a_row_size = row_blk_size(a_row) + DO a_blk = matrix_a%row_p(a_row) + 1, matrix_a%row_p(a_row + 1) + IF (a_blk .EQ. 0) CYCLE + a_col = matrix_a%col_i(a_blk) + IF (a_col .ne. a_row) CYCLE + ! We must skip non-local blocks in a replicated matrix. + IF (matrix_a%replication_type .NE. dbcsr_repl_full) THEN + IF (mynode .NE. checker_square_proc(a_row, a_col, pgrid, row_dist, col_dist)) & + CYCLE + ENDIF + a_col_size = col_blk_size(a_col) + IF (a_row_size .NE. a_col_size) & + DBCSR_ABORT("is that a square matrix?") + a_nze = a_row_size**2 + a_data => pointer_view(data_p, ABS(matrix_a%blk_p(a_blk)), & + ABS(matrix_a%blk_p(a_blk)) + a_nze - 1) + !data_a => matrix_a%data(ABS(matrix_a%blk_p(a_blk)):ABS(matrix_a%blk_p(a_blk))+a_nze-1) + ! + ! let's trace the block + DO i = 1, a_row_size + trace = trace + a_data((i - 1)*a_row_size + i) + ENDDO + ENDDO ! a_col + ENDDO ! a_row + ! + ! summe + CALL mp_sum(trace, dbcsr_mp_group(dbcsr_distribution_mp(matrix_a%dist))) + + CALL timestop(error_handle) + END SUBROUTINE dbcsr_trace_${nametype1}$ + +! ************************************************************************************************** +!> \brief Dot product of DBCSR matrices +!> \param[in] matrix_a DBCSR matrices +!> \param[in] matrix_b DBCSR matrices +!> \param[out] trace the trace of the product of the matrices +! ************************************************************************************************** + SUBROUTINE dbcsr_dot_${nametype1}$ (matrix_a, matrix_b, trace) + TYPE(dbcsr_type), INTENT(IN) :: matrix_a, matrix_b + ${type1}$, INTENT(INOUT) :: trace + + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_dot_${nametype1}$', & + routineP = moduleN//':'//routineN + + INTEGER :: a_blk, a_col, a_col_size, a_row_size, b_blk, b_col_size, & + b_frst_blk, b_last_blk, b_row_size, nze, row, a_beg, a_end, b_beg, b_end + CHARACTER :: matrix_a_type, matrix_b_type + INTEGER, DIMENSION(:), POINTER :: a_col_blk_size, & + a_row_blk_size, & + b_col_blk_size, b_row_blk_size + ${type1}$ :: sym_fac, fac + LOGICAL :: found, matrix_a_symm, matrix_b_symm +#if defined (__ACCELERATE) + REAL(real_8), EXTERNAL :: SDOT +#else + REAL(real_4), EXTERNAL :: SDOT +#endif + REAL(real_8), EXTERNAL :: DDOT + COMPLEX(real_4), EXTERNAL :: CDOTU + COMPLEX(real_8), EXTERNAL :: ZDOTU + ${type1}$, DIMENSION(:), POINTER :: a_data, b_data + +! --------------------------------------------------------------------------- + + IF (matrix_a%replication_type .NE. dbcsr_repl_none & + .OR. matrix_b%replication_type .NE. dbcsr_repl_none) & + DBCSR_ABORT("Trace of product of replicated matrices not yet possible.") + + sym_fac = REAL(1.0, ${kind1}$) + matrix_a_type = dbcsr_get_matrix_type(matrix_a) + matrix_b_type = dbcsr_get_matrix_type(matrix_b) + matrix_a_symm = matrix_a_type == dbcsr_type_symmetric .OR. matrix_a_type == dbcsr_type_antisymmetric + matrix_b_symm = matrix_b_type == dbcsr_type_symmetric .OR. matrix_b_type == dbcsr_type_antisymmetric + + IF (matrix_a_symm .AND. matrix_b_symm) sym_fac = REAL(2.0, ${kind1}$) + + ! tracing a symmetric with a general matrix is not implemented, as it would require communication of blocks + IF (matrix_a_symm .NEQV. matrix_b_symm) & + DBCSR_ABORT("Tracing general with symmetric matrix NYI") + + a_row_blk_size => array_data(matrix_a%row_blk_size) + a_col_blk_size => array_data(matrix_a%col_blk_size) + b_row_blk_size => array_data(matrix_b%row_blk_size) + b_col_blk_size => array_data(matrix_b%col_blk_size) + + CALL dbcsr_get_data(matrix_a%data_area, a_data) + CALL dbcsr_get_data(matrix_b%data_area, b_data) + + ! let's go + trace = REAL(0.0, ${kind1}$) + IF (matrix_a%nblkrows_total .NE. matrix_b%nblkrows_total) & + DBCSR_ABORT("this combination of transpose is NYI") + DO row = 1, matrix_a%nblkrows_total + a_row_size = a_row_blk_size(row) + b_row_size = b_row_blk_size(row) + IF (a_row_size .NE. b_row_size) DBCSR_ABORT("matrices not consistent") + b_blk = matrix_b%row_p(row) + 1 + b_frst_blk = matrix_b%row_p(row) + 1 + b_last_blk = matrix_b%row_p(row + 1) + DO a_blk = matrix_a%row_p(row) + 1, matrix_a%row_p(row + 1) + IF (matrix_a%blk_p(a_blk) .EQ. 0) CYCLE ! Deleted block + a_col = matrix_a%col_i(a_blk) + a_col_size = a_col_blk_size(a_col) + ! + ! find the b_blk we assume here that the columns are ordered ! + CALL dbcsr_find_column(a_col, b_frst_blk, b_last_blk, matrix_b%col_i, & + matrix_b%blk_p, b_blk, found) + IF (found) THEN + b_col_size = b_col_blk_size(a_col) + IF (a_col_size .NE. b_col_size) DBCSR_ABORT("matrices not consistent") + ! + nze = a_row_size*a_col_size + ! + IF (nze .GT. 0) THEN + ! + ! let's trace the blocks + a_beg = ABS(matrix_a%blk_p(a_blk)) + a_end = a_beg + nze - 1 + b_beg = ABS(matrix_b%blk_p(b_blk)) + b_end = b_beg + nze - 1 + fac = REAL(1.0, ${kind1}$) + IF (row .NE. a_col) fac = sym_fac + + trace = trace + fac*SUM(a_data(a_beg:a_end)*b_data(b_beg:b_end)) + + ENDIF + ENDIF + ENDDO ! a_col + ENDDO ! a_row + ! + ! sum + CALL mp_sum(trace, dbcsr_mp_group(dbcsr_distribution_mp(matrix_a%dist))) + + END SUBROUTINE dbcsr_dot_${nametype1}$ + +! ************************************************************************************************** +!> \brief Interface for matrix scaling by a scalar +!> \param matrix_a ... +!> \param alpha_scalar ... +!> \param last_column ... +! ************************************************************************************************** + SUBROUTINE dbcsr_scale_${nametype1}$ (matrix_a, alpha_scalar, last_column) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a + ${type1}$, INTENT(IN) :: alpha_scalar + INTEGER, INTENT(IN), OPTIONAL :: last_column + + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_scale_${nametype1}$', & + routineP = moduleN//':'//routineN + + INTEGER :: error_handler + TYPE(dbcsr_scalar_type) :: sc + + sc = dbcsr_scalar(alpha_scalar) + CALL dbcsr_scalar_fill_all(sc) + sc%data_type = dbcsr_get_data_type(matrix_a) + CALL timeset(routineN, error_handler) + IF (PRESENT(last_column)) THEN + CALL dbcsr_scale_anytype(matrix_a, & + alpha_scalar=sc, & + limits=(/0, 0, 0, last_column/)) + ELSE + CALL dbcsr_scale_anytype(matrix_a, alpha_scalar=sc) + ENDIF + CALL timestop(error_handler) + END SUBROUTINE dbcsr_scale_${nametype1}$ + +! ************************************************************************************************** +!> \brief Interface for matrix scaling by a vector +!> \param matrix_a ... +!> \param alpha ... +!> \param side ... +! ************************************************************************************************** + SUBROUTINE dbcsr_scale_by_vector_${nametype1}$ (matrix_a, alpha, side) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a + ${type1}$, DIMENSION(:), INTENT(IN), TARGET :: alpha + CHARACTER(LEN=*), INTENT(IN) :: side + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_scale_by_vector_${nametype1}$', & + routineP = moduleN//':'//routineN + ${type1}$, DIMENSION(:), POINTER :: tmp_p + TYPE(dbcsr_data_obj) :: enc_alpha_vec + + CALL dbcsr_data_init(enc_alpha_vec) + CALL dbcsr_data_new(enc_alpha_vec, ${dkind1}$) + tmp_p => alpha + CALL dbcsr_data_set_pointer(enc_alpha_vec, tmp_p) + CALL dbcsr_scale_by_vector_anytype(matrix_a, enc_alpha_vec, side) + CALL dbcsr_data_clear_pointer(enc_alpha_vec) + CALL dbcsr_data_release(enc_alpha_vec) + END SUBROUTINE dbcsr_scale_by_vector_${nametype1}$ + +! ************************************************************************************************** +!> \brief Interface for dbcsr_set +!> \param matrix ... +!> \param alpha ... +! ************************************************************************************************** + SUBROUTINE dbcsr_set_${nametype1}$ (matrix, alpha) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix + ${type1}$, INTENT(IN) :: alpha + + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_set' + + INTEGER :: col, handle, row + TYPE(dbcsr_iterator) :: iter + ${type1}$, DIMENSION(:, :), POINTER :: block + LOGICAL :: tr + + CALL timeset(routineN, handle) + + IF (alpha == ${zero1[n]}$) THEN + CALL dbcsr_zero(matrix) + ELSE + IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & + DBCSR_ABORT("Incompatible data types") + + !TODO: could be speedup by direct assignment to data_area, similar to dbcsr_zero() + CALL dbcsr_iterator_start(iter, matrix) + DO WHILE (dbcsr_iterator_blocks_left(iter)) + CALL dbcsr_iterator_next_block(iter, row, col, block, tr) + block(:, :) = alpha + ENDDO + CALL dbcsr_iterator_stop(iter) + ENDIF + + CALL timestop(handle) + END SUBROUTINE dbcsr_set_${nametype1}$ + +! ************************************************************************************************** +!> \brief ... +!> \param matrix ... +!> \param eps ... +!> \param method ... +!> \param use_absolute ... +!> \param filter_diag ... +! ************************************************************************************************** + SUBROUTINE dbcsr_filter_${nametype1}$ (matrix, eps, method, use_absolute, & + filter_diag) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix + ${type1}$, INTENT(IN) :: eps + INTEGER, INTENT(IN), OPTIONAL :: method + LOGICAL, INTENT(in), OPTIONAL :: use_absolute, filter_diag + CALL dbcsr_filter_anytype(matrix, dbcsr_scalar(eps), method, & + use_absolute, filter_diag) + END SUBROUTINE dbcsr_filter_${nametype1}$ + +! ************************************************************************************************** +!> \brief ... +!> \param matrix ... +!> \param diag ... +! ************************************************************************************************** + SUBROUTINE dbcsr_set_diag_${nametype1}$ (matrix, diag) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix + ${type1}$, DIMENSION(:), INTENT(IN) :: diag + + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_set_diag' + + INTEGER :: icol, irow, row_offset, handle, i + LOGICAL :: tr + TYPE(dbcsr_iterator) :: iter + ${type1}$, DIMENSION(:, :), POINTER :: block + + CALL timeset(routineN, handle) + + IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & + DBCSR_ABORT("Incompatible data types") + + IF (dbcsr_nfullrows_total(matrix) /= SIZE(diag)) & + DBCSR_ABORT("Diagonal has wrong size") + + IF (.NOT. array_equality(dbcsr_row_block_offsets(matrix), dbcsr_row_block_offsets(matrix))) & + DBCSR_ABORT("matrix not quadratic") + + CALL dbcsr_iterator_start(iter, matrix) + DO WHILE (dbcsr_iterator_blocks_left(iter)) + CALL dbcsr_iterator_next_block(iter, irow, icol, block, tr, row_offset=row_offset) + IF (irow /= icol) CYCLE + + IF (sIZE(block, 1) /= sIZE(block, 2)) & + DBCSR_ABORT("Diagonal block non-squared") + + DO i = 1, sIZE(block, 1) + block(i, i) = diag(row_offset + i - 1) + END DO + ENDDO + CALL dbcsr_iterator_stop(iter) + + CALL timestop(handle) + END SUBROUTINE dbcsr_set_diag_${nametype1}$ + +! ************************************************************************************************** +!> \brief ... +!> \param matrix ... +!> \param diag ... +! ************************************************************************************************** + SUBROUTINE dbcsr_get_diag_${nametype1}$ (matrix, diag) + TYPE(dbcsr_type), INTENT(IN) :: matrix + ${type1}$, DIMENSION(:), INTENT(OUT) :: diag + + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_get_diag' + + INTEGER :: icol, irow, row_offset, handle, i + LOGICAL :: tr + TYPE(dbcsr_iterator) :: iter + ${type1}$, DIMENSION(:, :), POINTER :: block + + CALL timeset(routineN, handle) + + IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & + DBCSR_ABORT("Incompatible data types") + + IF (dbcsr_nfullrows_total(matrix) /= SIZE(diag)) & + DBCSR_ABORT("Diagonal has wrong size") + + IF (.NOT. array_equality(dbcsr_row_block_offsets(matrix), dbcsr_row_block_offsets(matrix))) & + DBCSR_ABORT("matrix not quadratic") + + diag(:) = ${zero1[n]}$ + + CALL dbcsr_iterator_start(iter, matrix) + DO WHILE (dbcsr_iterator_blocks_left(iter)) + CALL dbcsr_iterator_next_block(iter, irow, icol, block, tr, row_offset=row_offset) + IF (irow /= icol) CYCLE + + IF (sIZE(block, 1) /= sIZE(block, 2)) & + DBCSR_ABORT("Diagonal block non-squared") + + DO i = 1, sIZE(block, 1) + diag(row_offset + i - 1) = block(i, i) + END DO + ENDDO + CALL dbcsr_iterator_stop(iter) + + CALL timestop(handle) + END SUBROUTINE dbcsr_get_diag_${nametype1}$ + +! ************************************************************************************************** +!> \brief add a constant to the diagonal of a matrix +!> \param[inout] matrix DBCSR matrix +!> \param[in] alpha scalar +! ************************************************************************************************** + SUBROUTINE dbcsr_add_on_diag_${nametype1}$ (matrix, alpha) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix + ${type1}$, INTENT(IN) :: alpha + + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_add_on_diag' + + INTEGER :: handle, mynode, node, irow, i, row_size + LOGICAL :: found, tr + ${type1}$, DIMENSION(:, :), POINTER :: block + + CALL timeset(routineN, handle) + + IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & + DBCSR_ABORT("Incompatible data types") + + IF (.NOT. array_equality(dbcsr_row_block_offsets(matrix), dbcsr_row_block_offsets(matrix))) & + DBCSR_ABORT("matrix not quadratic") + + mynode = dbcsr_mp_mynode(dbcsr_distribution_mp(dbcsr_distribution(matrix))) + + CALL dbcsr_work_create(matrix, work_mutable=.TRUE.) + + DO irow = 1, dbcsr_nblkrows_total(matrix) + CALL dbcsr_get_stored_coordinates(matrix, irow, irow, node) + IF (node /= mynode) CYCLE + + CALL dbcsr_get_block_p(matrix, irow, irow, block, tr, found, row_size=row_size) + IF (.NOT. found) THEN + ALLOCATE (block(row_size, row_size)) + block(:, :) = ${zero1[n]}$ + ENDIF + + DO i = 1, row_size + block(i, i) = block(i, i) + alpha + END DO + + IF (.NOT. found) THEN + CALL dbcsr_put_block(matrix, irow, irow, block) + DEALLOCATE (block) + ENDIF + ENDDO + + CALL dbcsr_finalize(matrix) + CALL timestop(handle) + END SUBROUTINE dbcsr_add_on_diag_${nametype1}$ + +! ************************************************************************************************** +!> \brief Low level function to sum contiguous chunks of blocks of the matrices (matrix_a = matrix_a + beta*matrix_b) +!> \param[inout] matrix_a DBCSR matrix +!> \param[in] matrix_b DBCSR matrix +!> \param[in] first_lb_a ... +!> \param[in] first_lb_b ... +!> \param[in] nze ... +!> \param[in] do_scale ... +!> \param[in] my_beta_scalar ... +!> \param[in] found ... +!> \param[in] iw ... +! ************************************************************************************************** + SUBROUTINE dbcsr_update_contiguous_blocks_${nametype1}$ (matrix_a, matrix_b, first_lb_a, first_lb_b, nze, & + do_scale, my_beta_scalar, found, iw) + + TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a + TYPE(dbcsr_type), INTENT(IN) :: matrix_b + TYPE(dbcsr_scalar_type), INTENT(IN) :: my_beta_scalar + INTEGER, INTENT(IN) :: first_lb_a, first_lb_b, nze, iw + LOGICAL, INTENT(IN) :: found, do_scale + + INTEGER :: ub_a, ub_b + + ub_a = first_lb_a + nze - 1 + ub_b = first_lb_b + nze - 1 + + IF (found) THEN + IF (do_scale) THEN + CALL ${nametype1}$axpy(nze, my_beta_scalar%${base1}$_${prec1}$, & + matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b), 1, & + matrix_a%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a), 1) + ELSE + matrix_a%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) = & + matrix_a%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) + & + matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b) + ENDIF + ELSE + IF (do_scale) THEN + matrix_a%wms(iw)%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) = & + my_beta_scalar%${base1}$_${prec1}$* & + matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b) + ELSE + matrix_a%wms(iw)%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) = & + matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b) + ENDIF + ENDIF + END SUBROUTINE dbcsr_update_contiguous_blocks_${nametype1}$ + +! ************************************************************************************************** +!> \brief Low level function to sum two matrices (matrix_a = matrix_a + beta*matrix_b +!> \param[inout] matrix_a DBCSR matrix +!> \param[in] matrix_b DBCSR matrix +!> \param[in] iter ... +!> \param[in] iw ... +!> \param[in] do_scale ... +!> \param[in] my_beta_scalar ... +!> \param[inout] my_flop ... +! ************************************************************************************************** + + SUBROUTINE dbcsr_add_anytype_${nametype1}$ (matrix_a, matrix_b, iter, iw, do_scale, & + my_beta_scalar, my_flop) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a + TYPE(dbcsr_type), INTENT(IN) :: matrix_b + TYPE(dbcsr_iterator), INTENT(INOUT) :: iter + INTEGER, INTENT(IN) :: iw + LOGICAL, INTENT(IN) :: do_scale + TYPE(dbcsr_scalar_type), INTENT(IN) :: my_beta_scalar + INTEGER(KIND=int_8), INTENT(INOUT) :: my_flop + + INTEGER :: row, col, row_size, col_size, & + nze, tot_nze, blk, & + lb_a, first_lb_a, lb_a_val, & + lb_b, first_lb_b + INTEGER, DIMENSION(2) :: lb_row_blk + LOGICAL :: was_found, found, tr + + ! some start values + lb_row_blk(:) = 0 + first_lb_a = matrix_a%wms(iw)%datasize + 1 + first_lb_b = 0 + tot_nze = 0 + ! + DO WHILE (dbcsr_iterator_blocks_left(iter)) + CALL dbcsr_iterator_next_block(iter, row, col, blk, tr, lb_b, row_size, col_size) + nze = row_size*col_size + IF (nze .LE. 0) CYCLE + IF (lb_row_blk(1) .LT. row) THEN + lb_row_blk(1) = row + lb_row_blk(2) = matrix_a%row_p(row) + 1 + ENDIF + ! get b-block index + lb_b = ABS(lb_b) + CALL dbcsr_find_column(col, lb_row_blk(2), matrix_a%row_p(row + 1), matrix_a%col_i, matrix_a%blk_p, blk, found) + lb_row_blk(2) = blk + 1 + ! get index of a-block lb_a whether found (from matrix_a) or not (from workspace array) + IF (found) THEN + my_flop = my_flop + nze*2 + lb_a = ABS(matrix_a%blk_p(blk)) + ELSE + lb_a = matrix_a%wms(iw)%datasize + 1 + lb_a_val = lb_a + IF (tr) lb_a_val = -lb_a + matrix_a%wms(iw)%lastblk = matrix_a%wms(iw)%lastblk + 1 + matrix_a%wms(iw)%row_i(matrix_a%wms(iw)%lastblk) = row + matrix_a%wms(iw)%col_i(matrix_a%wms(iw)%lastblk) = col + matrix_a%wms(iw)%blk_p(matrix_a%wms(iw)%lastblk) = lb_a_val + matrix_a%wms(iw)%datasize = matrix_a%wms(iw)%datasize + nze + ENDIF + ! at the first iteration we skip this and go directly to initialization after + IF (first_lb_b .NE. 0) THEN + ! if found status is the same as before then probably we are in contiguous blocks + IF ((found .EQV. was_found) .AND. & + (first_lb_b + tot_nze .EQ. lb_b) .AND. & + (first_lb_a + tot_nze) .EQ. lb_a) THEN + tot_nze = tot_nze + nze + CYCLE + ENDIF + ! save block chunk + CALL dbcsr_update_contiguous_blocks_${nametype1}$ (matrix_a, matrix_b, first_lb_a, first_lb_b, tot_nze, & + do_scale, my_beta_scalar, was_found, iw) + ENDIF + ! + first_lb_a = lb_a + first_lb_b = lb_b + tot_nze = nze + was_found = found + ENDDO + + ! save the last block or chunk of blocks + IF (first_lb_b .NE. 0) THEN + call dbcsr_update_contiguous_blocks_${nametype1}$ (matrix_a, matrix_b, first_lb_a, first_lb_b, tot_nze, & + do_scale, my_beta_scalar, was_found, iw) + ENDIF + + END SUBROUTINE dbcsr_add_anytype_${nametype1}$ +#:endfor END MODULE dbcsr_operations diff --git a/src/ops/dbcsr_operations.f90 b/src/ops/dbcsr_operations.f90 deleted file mode 100644 index 137603aeedd..00000000000 --- a/src/ops/dbcsr_operations.f90 +++ /dev/null @@ -1,571 +0,0 @@ -!--------------------------------------------------------------------------------------------------! -! Copyright (C) by the DBCSR developers group - All rights reserved ! -! This file is part of the DBCSR library. ! -! ! -! For information on the license, see the LICENSE file. ! -! For further information please visit https://dbcsr.cp2k.org ! -! SPDX-License-Identifier: GPL-2.0+ ! -!--------------------------------------------------------------------------------------------------! - -#:include '../data/dbcsr.fypp' -#:for n, nametype1, base1, prec1, kind1, type1, dkind1 in inst_params_float -! ************************************************************************************************** -!> \brief traces a DBCSR matrix -!> \param[in] matrix_a DBCSR matrix -!> \param[out] trace the trace of the matrix -!> -! ************************************************************************************************** - SUBROUTINE dbcsr_trace_a_${nametype1}$ (matrix_a, trace) - TYPE(dbcsr_type), INTENT(IN) :: matrix_a - ${type1}$, INTENT(INOUT) :: trace - - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_trace_a_${nametype1}$', & - routineP = moduleN//':'//routineN - - INTEGER :: a_blk, a_col, a_col_size, & - a_nze, a_row, a_row_size, i, & - mynode, error_handle - INTEGER, DIMENSION(:), POINTER :: col_blk_size, row_blk_size, & - row_dist, col_dist - ${type1}$, DIMENSION(:), POINTER :: a_data, data_p - INTEGER, DIMENSION(:, :), POINTER :: pgrid - TYPE(dbcsr_distribution_obj) :: dist - -! --------------------------------------------------------------------------- - CALL timeset(routineN, error_handle) - - row_blk_size => array_data(matrix_a%row_blk_size) - col_blk_size => array_data(matrix_a%col_blk_size) - IF (dbcsr_get_data_type(matrix_a) /= ${dkind1}$) & - DBCSR_ABORT("Incompatible data types") - CALL dbcsr_get_data(matrix_a%data_area, data_p) - dist = dbcsr_distribution(matrix_a) - mynode = dbcsr_mp_mynode(dbcsr_distribution_mp(dist)) - pgrid => dbcsr_mp_pgrid(dbcsr_distribution_mp(dist)) - row_dist => dbcsr_distribution_row_dist(dist) - col_dist => dbcsr_distribution_col_dist(dist) - ! - ! let's go - trace = REAL(0.0, ${kind1}$) - DO a_row = 1, matrix_a%nblkrows_total - a_row_size = row_blk_size(a_row) - DO a_blk = matrix_a%row_p(a_row) + 1, matrix_a%row_p(a_row + 1) - IF (a_blk .EQ. 0) CYCLE - a_col = matrix_a%col_i(a_blk) - IF (a_col .ne. a_row) CYCLE - ! We must skip non-local blocks in a replicated matrix. - IF (matrix_a%replication_type .NE. dbcsr_repl_full) THEN - IF (mynode .NE. checker_square_proc(a_row, a_col, pgrid, row_dist, col_dist)) & - CYCLE - ENDIF - a_col_size = col_blk_size(a_col) - IF (a_row_size .NE. a_col_size) & - DBCSR_ABORT("is that a square matrix?") - a_nze = a_row_size**2 - a_data => pointer_view(data_p, ABS(matrix_a%blk_p(a_blk)), & - ABS(matrix_a%blk_p(a_blk)) + a_nze - 1) - !data_a => matrix_a%data(ABS(matrix_a%blk_p(a_blk)):ABS(matrix_a%blk_p(a_blk))+a_nze-1) - ! - ! let's trace the block - DO i = 1, a_row_size - trace = trace + a_data((i - 1)*a_row_size + i) - ENDDO - ENDDO ! a_col - ENDDO ! a_row - ! - ! summe - CALL mp_sum(trace, dbcsr_mp_group(dbcsr_distribution_mp(matrix_a%dist))) - - CALL timestop(error_handle) - END SUBROUTINE dbcsr_trace_a_${nametype1}$ - -! ************************************************************************************************** -!> \brief traces a product of DBCSR matrices -!> \param[in] matrix_a DBCSR matrices -!> \param[in] matrix_b DBCSR matrices -!> \param[out] trace the trace of the product of the matrices -! ************************************************************************************************** - SUBROUTINE dbcsr_trace_ab_${nametype1}$ (matrix_a, matrix_b, trace) - TYPE(dbcsr_type), INTENT(IN) :: matrix_a, matrix_b - ${type1}$, INTENT(INOUT) :: trace - - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_trace_ab_${nametype1}$', & - routineP = moduleN//':'//routineN - - INTEGER :: a_blk, a_col, a_col_size, a_row_size, b_blk, b_col_size, & - b_frst_blk, b_last_blk, b_row_size, nze, row, a_beg, a_end, b_beg, b_end - CHARACTER :: matrix_a_type, matrix_b_type - INTEGER, DIMENSION(:), POINTER :: a_col_blk_size, & - a_row_blk_size, & - b_col_blk_size, b_row_blk_size - ${type1}$ :: sym_fac, fac - LOGICAL :: found, matrix_a_symm, matrix_b_symm -#if defined (__ACCELERATE) - REAL(real_8), EXTERNAL :: SDOT -#else - REAL(real_4), EXTERNAL :: SDOT -#endif - REAL(real_8), EXTERNAL :: DDOT - COMPLEX(real_4), EXTERNAL :: CDOTU - COMPLEX(real_8), EXTERNAL :: ZDOTU - ${type1}$, DIMENSION(:), POINTER :: a_data, b_data - -! --------------------------------------------------------------------------- - - IF (matrix_a%replication_type .NE. dbcsr_repl_none & - .OR. matrix_b%replication_type .NE. dbcsr_repl_none) & - DBCSR_ABORT("Trace of product of replicated matrices not yet possible.") - - sym_fac = REAL(1.0, ${kind1}$) - matrix_a_type = dbcsr_get_matrix_type(matrix_a) - matrix_b_type = dbcsr_get_matrix_type(matrix_b) - matrix_a_symm = matrix_a_type == dbcsr_type_symmetric .OR. matrix_a_type == dbcsr_type_antisymmetric - matrix_b_symm = matrix_b_type == dbcsr_type_symmetric .OR. matrix_b_type == dbcsr_type_antisymmetric - - IF (matrix_a_symm .AND. matrix_b_symm) sym_fac = REAL(2.0, ${kind1}$) - - ! tracing a symmetric with a general matrix is not implemented, as it would require communication of blocks - IF (matrix_a_symm .NEQV. matrix_b_symm) & - DBCSR_ABORT("Tracing general with symmetric matrix NYI") - - a_row_blk_size => array_data(matrix_a%row_blk_size) - a_col_blk_size => array_data(matrix_a%col_blk_size) - b_row_blk_size => array_data(matrix_b%row_blk_size) - b_col_blk_size => array_data(matrix_b%col_blk_size) - - CALL dbcsr_get_data(matrix_a%data_area, a_data) - CALL dbcsr_get_data(matrix_b%data_area, b_data) - - ! let's go - trace = REAL(0.0, ${kind1}$) - IF (matrix_a%nblkrows_total .NE. matrix_b%nblkrows_total) & - DBCSR_ABORT("this combination of transpose is NYI") - DO row = 1, matrix_a%nblkrows_total - a_row_size = a_row_blk_size(row) - b_row_size = b_row_blk_size(row) - IF (a_row_size .NE. b_row_size) DBCSR_ABORT("matrices not consistent") - b_blk = matrix_b%row_p(row) + 1 - b_frst_blk = matrix_b%row_p(row) + 1 - b_last_blk = matrix_b%row_p(row + 1) - DO a_blk = matrix_a%row_p(row) + 1, matrix_a%row_p(row + 1) - IF (matrix_a%blk_p(a_blk) .EQ. 0) CYCLE ! Deleted block - a_col = matrix_a%col_i(a_blk) - a_col_size = a_col_blk_size(a_col) - ! - ! find the b_blk we assume here that the columns are ordered ! - CALL dbcsr_find_column(a_col, b_frst_blk, b_last_blk, matrix_b%col_i, & - matrix_b%blk_p, b_blk, found) - IF (found) THEN - b_col_size = b_col_blk_size(a_col) - IF (a_col_size .NE. b_col_size) DBCSR_ABORT("matrices not consistent") - ! - nze = a_row_size*a_col_size - ! - IF (nze .GT. 0) THEN - ! - ! let's trace the blocks - a_beg = ABS(matrix_a%blk_p(a_blk)) - a_end = a_beg + nze - 1 - b_beg = ABS(matrix_b%blk_p(b_blk)) - b_end = b_beg + nze - 1 - fac = REAL(1.0, ${kind1}$) - IF (row .NE. a_col) fac = sym_fac - - trace = trace + fac*SUM(a_data(a_beg:a_end)*b_data(b_beg:b_end)) - - ENDIF - ENDIF - ENDDO ! a_col - ENDDO ! a_row - ! - ! sum - CALL mp_sum(trace, dbcsr_mp_group(dbcsr_distribution_mp(matrix_a%dist))) - - END SUBROUTINE dbcsr_trace_ab_${nametype1}$ - -! ************************************************************************************************** -!> \brief Interface for matrix scaling by a scalar -!> \param matrix_a ... -!> \param alpha_scalar ... -!> \param last_column ... -! ************************************************************************************************** - SUBROUTINE dbcsr_scale_${nametype1}$ (matrix_a, alpha_scalar, last_column) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a - ${type1}$, INTENT(IN) :: alpha_scalar - INTEGER, INTENT(IN), OPTIONAL :: last_column - - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_scale_${nametype1}$', & - routineP = moduleN//':'//routineN - - INTEGER :: error_handler - TYPE(dbcsr_scalar_type) :: sc - - sc = dbcsr_scalar(alpha_scalar) - CALL dbcsr_scalar_fill_all(sc) - sc%data_type = dbcsr_get_data_type(matrix_a) - CALL timeset(routineN, error_handler) - IF (PRESENT(last_column)) THEN - CALL dbcsr_scale_anytype(matrix_a, & - alpha_scalar=sc, & - limits=(/0, 0, 0, last_column/)) - ELSE - CALL dbcsr_scale_anytype(matrix_a, alpha_scalar=sc) - ENDIF - CALL timestop(error_handler) - END SUBROUTINE dbcsr_scale_${nametype1}$ - -! ************************************************************************************************** -!> \brief Interface for matrix scaling by a vector -!> \param matrix_a ... -!> \param alpha ... -!> \param side ... -! ************************************************************************************************** - SUBROUTINE dbcsr_scale_by_vector_${nametype1}$ (matrix_a, alpha, side) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a - ${type1}$, DIMENSION(:), INTENT(IN), TARGET :: alpha - CHARACTER(LEN=*), INTENT(IN) :: side - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_scale_by_vector_${nametype1}$', & - routineP = moduleN//':'//routineN - ${type1}$, DIMENSION(:), POINTER :: tmp_p - TYPE(dbcsr_data_obj) :: enc_alpha_vec - - CALL dbcsr_data_init(enc_alpha_vec) - CALL dbcsr_data_new(enc_alpha_vec, ${dkind1}$) - tmp_p => alpha - CALL dbcsr_data_set_pointer(enc_alpha_vec, tmp_p) - CALL dbcsr_scale_by_vector_anytype(matrix_a, enc_alpha_vec, side) - CALL dbcsr_data_clear_pointer(enc_alpha_vec) - CALL dbcsr_data_release(enc_alpha_vec) - END SUBROUTINE dbcsr_scale_by_vector_${nametype1}$ - -! ************************************************************************************************** -!> \brief Interface for dbcsr_set -!> \param matrix ... -!> \param alpha ... -! ************************************************************************************************** - SUBROUTINE dbcsr_set_${nametype1}$ (matrix, alpha) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix - ${type1}$, INTENT(IN) :: alpha - - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_set' - - INTEGER :: col, handle, row - TYPE(dbcsr_iterator) :: iter - ${type1}$, DIMENSION(:, :), POINTER :: block - LOGICAL :: tr - - CALL timeset(routineN, handle) - - IF (alpha == ${zero1[n]}$) THEN - CALL dbcsr_zero(matrix) - ELSE - IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & - DBCSR_ABORT("Incompatible data types") - - !TODO: could be speedup by direct assignment to data_area, similar to dbcsr_zero() - CALL dbcsr_iterator_start(iter, matrix) - DO WHILE (dbcsr_iterator_blocks_left(iter)) - CALL dbcsr_iterator_next_block(iter, row, col, block, tr) - block(:, :) = alpha - ENDDO - CALL dbcsr_iterator_stop(iter) - ENDIF - - CALL timestop(handle) - END SUBROUTINE dbcsr_set_${nametype1}$ - -! ************************************************************************************************** -!> \brief ... -!> \param matrix ... -!> \param eps ... -!> \param method ... -!> \param use_absolute ... -!> \param filter_diag ... -! ************************************************************************************************** - SUBROUTINE dbcsr_filter_${nametype1}$ (matrix, eps, method, use_absolute, & - filter_diag) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix - ${type1}$, INTENT(IN) :: eps - INTEGER, INTENT(IN), OPTIONAL :: method - LOGICAL, INTENT(in), OPTIONAL :: use_absolute, filter_diag - CALL dbcsr_filter_anytype(matrix, dbcsr_scalar(eps), method, & - use_absolute, filter_diag) - END SUBROUTINE dbcsr_filter_${nametype1}$ - -! ************************************************************************************************** -!> \brief ... -!> \param matrix ... -!> \param diag ... -! ************************************************************************************************** - SUBROUTINE dbcsr_set_diag_${nametype1}$ (matrix, diag) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix - ${type1}$, DIMENSION(:), INTENT(IN) :: diag - - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_set_diag' - - INTEGER :: icol, irow, row_offset, handle, i - LOGICAL :: tr - TYPE(dbcsr_iterator) :: iter - ${type1}$, DIMENSION(:, :), POINTER :: block - - CALL timeset(routineN, handle) - - IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & - DBCSR_ABORT("Incompatible data types") - - IF (dbcsr_nfullrows_total(matrix) /= SIZE(diag)) & - DBCSR_ABORT("Diagonal has wrong size") - - IF (.NOT. array_equality(dbcsr_row_block_offsets(matrix), dbcsr_row_block_offsets(matrix))) & - DBCSR_ABORT("matrix not quadratic") - - CALL dbcsr_iterator_start(iter, matrix) - DO WHILE (dbcsr_iterator_blocks_left(iter)) - CALL dbcsr_iterator_next_block(iter, irow, icol, block, tr, row_offset=row_offset) - IF (irow /= icol) CYCLE - - IF (sIZE(block, 1) /= sIZE(block, 2)) & - DBCSR_ABORT("Diagonal block non-squared") - - DO i = 1, sIZE(block, 1) - block(i, i) = diag(row_offset + i - 1) - END DO - ENDDO - CALL dbcsr_iterator_stop(iter) - - CALL timestop(handle) - END SUBROUTINE dbcsr_set_diag_${nametype1}$ - -! ************************************************************************************************** -!> \brief ... -!> \param matrix ... -!> \param diag ... -! ************************************************************************************************** - SUBROUTINE dbcsr_get_diag_${nametype1}$ (matrix, diag) - TYPE(dbcsr_type), INTENT(IN) :: matrix - ${type1}$, DIMENSION(:), INTENT(OUT) :: diag - - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_get_diag' - - INTEGER :: icol, irow, row_offset, handle, i - LOGICAL :: tr - TYPE(dbcsr_iterator) :: iter - ${type1}$, DIMENSION(:, :), POINTER :: block - - CALL timeset(routineN, handle) - - IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & - DBCSR_ABORT("Incompatible data types") - - IF (dbcsr_nfullrows_total(matrix) /= SIZE(diag)) & - DBCSR_ABORT("Diagonal has wrong size") - - IF (.NOT. array_equality(dbcsr_row_block_offsets(matrix), dbcsr_row_block_offsets(matrix))) & - DBCSR_ABORT("matrix not quadratic") - - diag(:) = ${zero1[n]}$ - - CALL dbcsr_iterator_start(iter, matrix) - DO WHILE (dbcsr_iterator_blocks_left(iter)) - CALL dbcsr_iterator_next_block(iter, irow, icol, block, tr, row_offset=row_offset) - IF (irow /= icol) CYCLE - - IF (sIZE(block, 1) /= sIZE(block, 2)) & - DBCSR_ABORT("Diagonal block non-squared") - - DO i = 1, sIZE(block, 1) - diag(row_offset + i - 1) = block(i, i) - END DO - ENDDO - CALL dbcsr_iterator_stop(iter) - - CALL timestop(handle) - END SUBROUTINE dbcsr_get_diag_${nametype1}$ - -! ************************************************************************************************** -!> \brief add a constant to the diagonal of a matrix -!> \param[inout] matrix DBCSR matrix -!> \param[in] alpha scalar -! ************************************************************************************************** - SUBROUTINE dbcsr_add_on_diag_${nametype1}$ (matrix, alpha) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix - ${type1}$, INTENT(IN) :: alpha - - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_add_on_diag' - - INTEGER :: handle, mynode, node, irow, i, row_size - LOGICAL :: found, tr - ${type1}$, DIMENSION(:, :), POINTER :: block - - CALL timeset(routineN, handle) - - IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & - DBCSR_ABORT("Incompatible data types") - - IF (.NOT. array_equality(dbcsr_row_block_offsets(matrix), dbcsr_row_block_offsets(matrix))) & - DBCSR_ABORT("matrix not quadratic") - - mynode = dbcsr_mp_mynode(dbcsr_distribution_mp(dbcsr_distribution(matrix))) - - CALL dbcsr_work_create(matrix, work_mutable=.TRUE.) - - DO irow = 1, dbcsr_nblkrows_total(matrix) - CALL dbcsr_get_stored_coordinates(matrix, irow, irow, node) - IF (node /= mynode) CYCLE - - CALL dbcsr_get_block_p(matrix, irow, irow, block, tr, found, row_size=row_size) - IF (.NOT. found) THEN - ALLOCATE (block(row_size, row_size)) - block(:, :) = ${zero1[n]}$ - ENDIF - - DO i = 1, row_size - block(i, i) = block(i, i) + alpha - END DO - - IF (.NOT. found) THEN - CALL dbcsr_put_block(matrix, irow, irow, block) - DEALLOCATE (block) - ENDIF - ENDDO - - CALL dbcsr_finalize(matrix) - CALL timestop(handle) - END SUBROUTINE dbcsr_add_on_diag_${nametype1}$ - -! ************************************************************************************************** -!> \brief Low level function to sum contiguous chunks of blocks of the matrices (matrix_a = matrix_a + beta*matrix_b) -!> \param[inout] matrix_a DBCSR matrix -!> \param[in] matrix_b DBCSR matrix -!> \param[in] first_lb_a ... -!> \param[in] first_lb_b ... -!> \param[in] nze ... -!> \param[in] do_scale ... -!> \param[in] my_beta_scalar ... -!> \param[in] found ... -!> \param[in] iw ... -! ************************************************************************************************** - SUBROUTINE dbcsr_update_contiguous_blocks_${nametype1}$ (matrix_a, matrix_b, first_lb_a, first_lb_b, nze, & - do_scale, my_beta_scalar, found, iw) - - TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a - TYPE(dbcsr_type), INTENT(IN) :: matrix_b - TYPE(dbcsr_scalar_type), INTENT(IN) :: my_beta_scalar - INTEGER, INTENT(IN) :: first_lb_a, first_lb_b, nze, iw - LOGICAL, INTENT(IN) :: found, do_scale - - INTEGER :: ub_a, ub_b - - ub_a = first_lb_a + nze - 1 - ub_b = first_lb_b + nze - 1 - - IF (found) THEN - IF (do_scale) THEN - CALL ${nametype1}$axpy(nze, my_beta_scalar%${base1}$_${prec1}$, & - matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b), 1, & - matrix_a%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a), 1) - ELSE - matrix_a%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) = & - matrix_a%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) + & - matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b) - ENDIF - ELSE - IF (do_scale) THEN - matrix_a%wms(iw)%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) = & - my_beta_scalar%${base1}$_${prec1}$* & - matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b) - ELSE - matrix_a%wms(iw)%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) = & - matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b) - ENDIF - ENDIF - END SUBROUTINE dbcsr_update_contiguous_blocks_${nametype1}$ - -! ************************************************************************************************** -!> \brief Low level function to sum two matrices (matrix_a = matrix_a + beta*matrix_b -!> \param[inout] matrix_a DBCSR matrix -!> \param[in] matrix_b DBCSR matrix -!> \param[in] iter ... -!> \param[in] iw ... -!> \param[in] do_scale ... -!> \param[in] my_beta_scalar ... -!> \param[inout] my_flop ... -! ************************************************************************************************** - - SUBROUTINE dbcsr_add_anytype_${nametype1}$ (matrix_a, matrix_b, iter, iw, do_scale, & - my_beta_scalar, my_flop) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a - TYPE(dbcsr_type), INTENT(IN) :: matrix_b - TYPE(dbcsr_iterator), INTENT(INOUT) :: iter - INTEGER, INTENT(IN) :: iw - LOGICAL, INTENT(IN) :: do_scale - TYPE(dbcsr_scalar_type), INTENT(IN) :: my_beta_scalar - INTEGER(KIND=int_8), INTENT(INOUT) :: my_flop - - INTEGER :: row, col, row_size, col_size, & - nze, tot_nze, blk, & - lb_a, first_lb_a, lb_a_val, & - lb_b, first_lb_b - INTEGER, DIMENSION(2) :: lb_row_blk - LOGICAL :: was_found, found, tr - - ! some start values - lb_row_blk(:) = 0 - first_lb_a = matrix_a%wms(iw)%datasize + 1 - first_lb_b = 0 - tot_nze = 0 - ! - DO WHILE (dbcsr_iterator_blocks_left(iter)) - CALL dbcsr_iterator_next_block(iter, row, col, blk, tr, lb_b, row_size, col_size) - nze = row_size*col_size - IF (nze .LE. 0) CYCLE - IF (lb_row_blk(1) .LT. row) THEN - lb_row_blk(1) = row - lb_row_blk(2) = matrix_a%row_p(row) + 1 - ENDIF - ! get b-block index - lb_b = ABS(lb_b) - CALL dbcsr_find_column(col, lb_row_blk(2), matrix_a%row_p(row + 1), matrix_a%col_i, matrix_a%blk_p, blk, found) - lb_row_blk(2) = blk + 1 - ! get index of a-block lb_a whether found (from matrix_a) or not (from workspace array) - IF (found) THEN - my_flop = my_flop + nze*2 - lb_a = ABS(matrix_a%blk_p(blk)) - ELSE - lb_a = matrix_a%wms(iw)%datasize + 1 - lb_a_val = lb_a - IF (tr) lb_a_val = -lb_a - matrix_a%wms(iw)%lastblk = matrix_a%wms(iw)%lastblk + 1 - matrix_a%wms(iw)%row_i(matrix_a%wms(iw)%lastblk) = row - matrix_a%wms(iw)%col_i(matrix_a%wms(iw)%lastblk) = col - matrix_a%wms(iw)%blk_p(matrix_a%wms(iw)%lastblk) = lb_a_val - matrix_a%wms(iw)%datasize = matrix_a%wms(iw)%datasize + nze - ENDIF - ! at the first iteration we skip this and go directly to initialization after - IF (first_lb_b .NE. 0) THEN - ! if found status is the same as before then probably we are in contiguous blocks - IF ((found .EQV. was_found) .AND. & - (first_lb_b + tot_nze .EQ. lb_b) .AND. & - (first_lb_a + tot_nze) .EQ. lb_a) THEN - tot_nze = tot_nze + nze - CYCLE - ENDIF - ! save block chunk - CALL dbcsr_update_contiguous_blocks_${nametype1}$ (matrix_a, matrix_b, first_lb_a, first_lb_b, tot_nze, & - do_scale, my_beta_scalar, was_found, iw) - ENDIF - ! - first_lb_a = lb_a - first_lb_b = lb_b - tot_nze = nze - was_found = found - ENDDO - - ! save the last block or chunk of blocks - IF (first_lb_b .NE. 0) THEN - call dbcsr_update_contiguous_blocks_${nametype1}$ (matrix_a, matrix_b, first_lb_a, first_lb_b, tot_nze, & - do_scale, my_beta_scalar, was_found, iw) - ENDIF - - END SUBROUTINE dbcsr_add_anytype_${nametype1}$ -#:endfor From 7b44e60c1d23099affd2fcad8dcd64260c438919 Mon Sep 17 00:00:00 2001 From: alazzaro Date: Mon, 6 May 2019 11:42:26 +0200 Subject: [PATCH 42/88] Remove unsed variables and comments --- examples/dbcsr_example_1.F | 8 - examples/dbcsr_example_2.F | 10 - examples/dbcsr_example_3.F | 11 - src/ops/dbcsr_operations.F | 850 ++++++++++++++++++------------------- 4 files changed, 425 insertions(+), 454 deletions(-) diff --git a/examples/dbcsr_example_1.F b/examples/dbcsr_example_1.F index 7f9882d0400..a186ee4b8f3 100644 --- a/examples/dbcsr_example_1.F +++ b/examples/dbcsr_example_1.F @@ -10,12 +10,6 @@ ! ************************************************************************************************** !> \brief DBCSR example 1 !> This example shows how to create a dbcsr matrix -!> \author V. Weber -!> \date 2010 -!> \version 1.0 -!> -!> Modification history: -!> - Created 2010 ! ************************************************************************************************** PROGRAM dbcsr_example_1 USE mpi @@ -24,8 +18,6 @@ PROGRAM dbcsr_example_1 dbcsr_finalize, dbcsr_finalize_lib, dbcsr_init_lib, dbcsr_print, dbcsr_release, & dbcsr_type, dbcsr_type_no_symmetry, dbcsr_type_real_8 -!$ USE OMP_LIB, ONLY: omp_get_max_threads, omp_get_thread_num, omp_get_num_threads - IMPLICIT NONE TYPE(dbcsr_type) :: matrix_a diff --git a/examples/dbcsr_example_2.F b/examples/dbcsr_example_2.F index 321f5be281c..32f5ac162d9 100644 --- a/examples/dbcsr_example_2.F +++ b/examples/dbcsr_example_2.F @@ -10,12 +10,6 @@ ! ************************************************************************************************** !> \brief DBCSR example 2 !> This example shows how to set a dbcsr matrix -!> \author V. Weber -!> \date 2010 -!> \version 1.0 -!> -!> Modification history: -!> - Created 2010 ! ************************************************************************************************** PROGRAM dbcsr_example_2 USE mpi @@ -25,8 +19,6 @@ PROGRAM dbcsr_example_2 dbcsr_init_lib, dbcsr_nblkcols_total, dbcsr_nblkrows_total, dbcsr_print, dbcsr_put_block, & dbcsr_release, dbcsr_type, dbcsr_type_no_symmetry, dbcsr_type_real_8 -!$ USE OMP_LIB, ONLY: omp_get_max_threads, omp_get_thread_num, omp_get_num_threads - IMPLICIT NONE TYPE(dbcsr_type) :: matrix_a @@ -39,7 +31,6 @@ PROGRAM dbcsr_example_2 INTEGER, DIMENSION(:), POINTER :: col_dist, row_dist TYPE(dbcsr_distribution_type) :: dist REAL(KIND=KIND(0.0D0)), DIMENSION(:), ALLOCATABLE :: values - LOGICAL :: tr LOGICAL, DIMENSION(2) :: period = .TRUE. !$ INTEGER :: provided_tsl @@ -131,7 +122,6 @@ PROGRAM dbcsr_example_2 DO col = MAX(row-1, 1), MIN(row+1, dbcsr_nblkcols_total(matrix_a)) ! ! get the node id that holds this (row, col) block - tr = .FALSE. row_s = row; col_s = col CALL dbcsr_get_stored_coordinates(matrix_a, row_s, col_s, node_holds_blk) ! diff --git a/examples/dbcsr_example_3.F b/examples/dbcsr_example_3.F index aebf476214f..8f815dba64c 100644 --- a/examples/dbcsr_example_3.F +++ b/examples/dbcsr_example_3.F @@ -10,12 +10,6 @@ ! ************************************************************************************************** !> \brief DBCSR example 3 !> This example shows how to multiply two dbcsr matrices -!> \author V. Weber -!> \date 2010 -!> \version 1.0 -!> -!> Modification history: -!> - Created 2010 ! ************************************************************************************************** PROGRAM dbcsr_example_3 USE mpi @@ -25,8 +19,6 @@ PROGRAM dbcsr_example_3 dbcsr_init_lib, dbcsr_multiply, dbcsr_nblkcols_total, dbcsr_nblkrows_total, dbcsr_print, & dbcsr_put_block, dbcsr_release, dbcsr_type, dbcsr_type_no_symmetry, dbcsr_type_real_8 -!$ USE OMP_LIB, ONLY: omp_get_max_threads, omp_get_thread_num, omp_get_num_threads - IMPLICIT NONE TYPE(dbcsr_type) :: matrix_a, matrix_b, matrix_c @@ -40,7 +32,6 @@ PROGRAM dbcsr_example_3 INTEGER, DIMENSION(:), POINTER :: col_dist, row_dist TYPE(dbcsr_distribution_type) :: dist REAL(KIND=KIND(0.0D0)), DIMENSION(:), ALLOCATABLE :: values - LOGICAL :: tr LOGICAL, DIMENSION(2) :: period = .TRUE. !$ INTEGER :: provided_tsl @@ -138,7 +129,6 @@ PROGRAM dbcsr_example_3 ALLOCATE (values(max_nze)) DO row = 1, dbcsr_nblkrows_total(matrix_a) DO col = MAX(row-1, 1), MIN(row+1, dbcsr_nblkcols_total(matrix_a)) - tr = .FALSE. row_s = row; col_s = col CALL dbcsr_get_stored_coordinates(matrix_a, row_s, col_s, node_holds_blk) IF (node_holds_blk .EQ. mynode) THEN @@ -156,7 +146,6 @@ PROGRAM dbcsr_example_3 ALLOCATE (values(max_nze)) DO row = 1, dbcsr_nblkrows_total(matrix_b) DO col = MAX(row-1, 1), MIN(row+1, dbcsr_nblkcols_total(matrix_b)) - tr = .FALSE. row_s = row; col_s = col CALL dbcsr_get_stored_coordinates(matrix_b, row_s, col_s, node_holds_blk) IF (node_holds_blk .EQ. mynode) THEN diff --git a/src/ops/dbcsr_operations.F b/src/ops/dbcsr_operations.F index 834df0120ad..24b435501cb 100644 --- a/src/ops/dbcsr_operations.F +++ b/src/ops/dbcsr_operations.F @@ -2744,69 +2744,69 @@ SUBROUTINE dbcsr_clear(matrix) !> \param[out] trace the trace of the matrix !> ! ************************************************************************************************** - SUBROUTINE dbcsr_trace_${nametype1}$ (matrix_a, trace) - TYPE(dbcsr_type), INTENT(IN) :: matrix_a - ${type1}$, INTENT(INOUT) :: trace - - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_trace_${nametype1}$', & - routineP = moduleN//':'//routineN - - INTEGER :: a_blk, a_col, a_col_size, & - a_nze, a_row, a_row_size, i, & - mynode, error_handle - INTEGER, DIMENSION(:), POINTER :: col_blk_size, row_blk_size, & - row_dist, col_dist - ${type1}$, DIMENSION(:), POINTER :: a_data, data_p - INTEGER, DIMENSION(:, :), POINTER :: pgrid - TYPE(dbcsr_distribution_obj) :: dist + SUBROUTINE dbcsr_trace_${nametype1}$ (matrix_a, trace) + TYPE(dbcsr_type), INTENT(IN) :: matrix_a + ${type1}$, INTENT(INOUT) :: trace + + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_trace_${nametype1}$', & + routineP = moduleN//':'//routineN + + INTEGER :: a_blk, a_col, a_col_size, & + a_nze, a_row, a_row_size, i, & + mynode, error_handle + INTEGER, DIMENSION(:), POINTER :: col_blk_size, row_blk_size, & + row_dist, col_dist + ${type1}$, DIMENSION(:), POINTER :: a_data, data_p + INTEGER, DIMENSION(:, :), POINTER :: pgrid + TYPE(dbcsr_distribution_obj) :: dist ! --------------------------------------------------------------------------- - CALL timeset(routineN, error_handle) - - row_blk_size => array_data(matrix_a%row_blk_size) - col_blk_size => array_data(matrix_a%col_blk_size) - IF (dbcsr_get_data_type(matrix_a) /= ${dkind1}$) & - DBCSR_ABORT("Incompatible data types") - CALL dbcsr_get_data(matrix_a%data_area, data_p) - dist = dbcsr_distribution(matrix_a) - mynode = dbcsr_mp_mynode(dbcsr_distribution_mp(dist)) - pgrid => dbcsr_mp_pgrid(dbcsr_distribution_mp(dist)) - row_dist => dbcsr_distribution_row_dist(dist) - col_dist => dbcsr_distribution_col_dist(dist) - ! - ! let's go - trace = REAL(0.0, ${kind1}$) - DO a_row = 1, matrix_a%nblkrows_total - a_row_size = row_blk_size(a_row) - DO a_blk = matrix_a%row_p(a_row) + 1, matrix_a%row_p(a_row + 1) - IF (a_blk .EQ. 0) CYCLE - a_col = matrix_a%col_i(a_blk) - IF (a_col .ne. a_row) CYCLE - ! We must skip non-local blocks in a replicated matrix. - IF (matrix_a%replication_type .NE. dbcsr_repl_full) THEN - IF (mynode .NE. checker_square_proc(a_row, a_col, pgrid, row_dist, col_dist)) & - CYCLE - ENDIF - a_col_size = col_blk_size(a_col) - IF (a_row_size .NE. a_col_size) & - DBCSR_ABORT("is that a square matrix?") - a_nze = a_row_size**2 - a_data => pointer_view(data_p, ABS(matrix_a%blk_p(a_blk)), & - ABS(matrix_a%blk_p(a_blk)) + a_nze - 1) - !data_a => matrix_a%data(ABS(matrix_a%blk_p(a_blk)):ABS(matrix_a%blk_p(a_blk))+a_nze-1) - ! - ! let's trace the block - DO i = 1, a_row_size - trace = trace + a_data((i - 1)*a_row_size + i) - ENDDO - ENDDO ! a_col - ENDDO ! a_row - ! - ! summe - CALL mp_sum(trace, dbcsr_mp_group(dbcsr_distribution_mp(matrix_a%dist))) - - CALL timestop(error_handle) - END SUBROUTINE dbcsr_trace_${nametype1}$ + CALL timeset(routineN, error_handle) + + row_blk_size => array_data(matrix_a%row_blk_size) + col_blk_size => array_data(matrix_a%col_blk_size) + IF (dbcsr_get_data_type(matrix_a) /= ${dkind1}$) & + DBCSR_ABORT("Incompatible data types") + CALL dbcsr_get_data(matrix_a%data_area, data_p) + dist = dbcsr_distribution(matrix_a) + mynode = dbcsr_mp_mynode(dbcsr_distribution_mp(dist)) + pgrid => dbcsr_mp_pgrid(dbcsr_distribution_mp(dist)) + row_dist => dbcsr_distribution_row_dist(dist) + col_dist => dbcsr_distribution_col_dist(dist) + ! + ! let's go + trace = REAL(0.0, ${kind1}$) + DO a_row = 1, matrix_a%nblkrows_total + a_row_size = row_blk_size(a_row) + DO a_blk = matrix_a%row_p(a_row) + 1, matrix_a%row_p(a_row + 1) + IF (a_blk .EQ. 0) CYCLE + a_col = matrix_a%col_i(a_blk) + IF (a_col .ne. a_row) CYCLE + ! We must skip non-local blocks in a replicated matrix. + IF (matrix_a%replication_type .NE. dbcsr_repl_full) THEN + IF (mynode .NE. checker_square_proc(a_row, a_col, pgrid, row_dist, col_dist)) & + CYCLE + ENDIF + a_col_size = col_blk_size(a_col) + IF (a_row_size .NE. a_col_size) & + DBCSR_ABORT("is that a square matrix?") + a_nze = a_row_size**2 + a_data => pointer_view(data_p, ABS(matrix_a%blk_p(a_blk)), & + ABS(matrix_a%blk_p(a_blk)) + a_nze - 1) + !data_a => matrix_a%data(ABS(matrix_a%blk_p(a_blk)):ABS(matrix_a%blk_p(a_blk))+a_nze-1) + ! + ! let's trace the block + DO i = 1, a_row_size + trace = trace + a_data((i - 1)*a_row_size + i) + ENDDO + ENDDO ! a_col + ENDDO ! a_row + ! + ! summe + CALL mp_sum(trace, dbcsr_mp_group(dbcsr_distribution_mp(matrix_a%dist))) + + CALL timestop(error_handle) + END SUBROUTINE dbcsr_trace_${nametype1}$ ! ************************************************************************************************** !> \brief Dot product of DBCSR matrices @@ -2814,103 +2814,103 @@ END SUBROUTINE dbcsr_trace_${nametype1}$ !> \param[in] matrix_b DBCSR matrices !> \param[out] trace the trace of the product of the matrices ! ************************************************************************************************** - SUBROUTINE dbcsr_dot_${nametype1}$ (matrix_a, matrix_b, trace) - TYPE(dbcsr_type), INTENT(IN) :: matrix_a, matrix_b - ${type1}$, INTENT(INOUT) :: trace - - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_dot_${nametype1}$', & - routineP = moduleN//':'//routineN - - INTEGER :: a_blk, a_col, a_col_size, a_row_size, b_blk, b_col_size, & - b_frst_blk, b_last_blk, b_row_size, nze, row, a_beg, a_end, b_beg, b_end - CHARACTER :: matrix_a_type, matrix_b_type - INTEGER, DIMENSION(:), POINTER :: a_col_blk_size, & - a_row_blk_size, & - b_col_blk_size, b_row_blk_size - ${type1}$ :: sym_fac, fac - LOGICAL :: found, matrix_a_symm, matrix_b_symm + SUBROUTINE dbcsr_dot_${nametype1}$ (matrix_a, matrix_b, trace) + TYPE(dbcsr_type), INTENT(IN) :: matrix_a, matrix_b + ${type1}$, INTENT(INOUT) :: trace + + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_dot_${nametype1}$', & + routineP = moduleN//':'//routineN + + INTEGER :: a_blk, a_col, a_col_size, a_row_size, b_blk, b_col_size, & + b_frst_blk, b_last_blk, b_row_size, nze, row, a_beg, a_end, b_beg, b_end + CHARACTER :: matrix_a_type, matrix_b_type + INTEGER, DIMENSION(:), POINTER :: a_col_blk_size, & + a_row_blk_size, & + b_col_blk_size, b_row_blk_size + ${type1}$ :: sym_fac, fac + LOGICAL :: found, matrix_a_symm, matrix_b_symm #if defined (__ACCELERATE) - REAL(real_8), EXTERNAL :: SDOT + REAL(real_8), EXTERNAL :: SDOT #else - REAL(real_4), EXTERNAL :: SDOT + REAL(real_4), EXTERNAL :: SDOT #endif - REAL(real_8), EXTERNAL :: DDOT - COMPLEX(real_4), EXTERNAL :: CDOTU - COMPLEX(real_8), EXTERNAL :: ZDOTU - ${type1}$, DIMENSION(:), POINTER :: a_data, b_data + REAL(real_8), EXTERNAL :: DDOT + COMPLEX(real_4), EXTERNAL :: CDOTU + COMPLEX(real_8), EXTERNAL :: ZDOTU + ${type1}$, DIMENSION(:), POINTER :: a_data, b_data ! --------------------------------------------------------------------------- - IF (matrix_a%replication_type .NE. dbcsr_repl_none & - .OR. matrix_b%replication_type .NE. dbcsr_repl_none) & - DBCSR_ABORT("Trace of product of replicated matrices not yet possible.") - - sym_fac = REAL(1.0, ${kind1}$) - matrix_a_type = dbcsr_get_matrix_type(matrix_a) - matrix_b_type = dbcsr_get_matrix_type(matrix_b) - matrix_a_symm = matrix_a_type == dbcsr_type_symmetric .OR. matrix_a_type == dbcsr_type_antisymmetric - matrix_b_symm = matrix_b_type == dbcsr_type_symmetric .OR. matrix_b_type == dbcsr_type_antisymmetric - - IF (matrix_a_symm .AND. matrix_b_symm) sym_fac = REAL(2.0, ${kind1}$) - - ! tracing a symmetric with a general matrix is not implemented, as it would require communication of blocks - IF (matrix_a_symm .NEQV. matrix_b_symm) & - DBCSR_ABORT("Tracing general with symmetric matrix NYI") - - a_row_blk_size => array_data(matrix_a%row_blk_size) - a_col_blk_size => array_data(matrix_a%col_blk_size) - b_row_blk_size => array_data(matrix_b%row_blk_size) - b_col_blk_size => array_data(matrix_b%col_blk_size) - - CALL dbcsr_get_data(matrix_a%data_area, a_data) - CALL dbcsr_get_data(matrix_b%data_area, b_data) - - ! let's go - trace = REAL(0.0, ${kind1}$) - IF (matrix_a%nblkrows_total .NE. matrix_b%nblkrows_total) & - DBCSR_ABORT("this combination of transpose is NYI") - DO row = 1, matrix_a%nblkrows_total - a_row_size = a_row_blk_size(row) - b_row_size = b_row_blk_size(row) - IF (a_row_size .NE. b_row_size) DBCSR_ABORT("matrices not consistent") - b_blk = matrix_b%row_p(row) + 1 - b_frst_blk = matrix_b%row_p(row) + 1 - b_last_blk = matrix_b%row_p(row + 1) - DO a_blk = matrix_a%row_p(row) + 1, matrix_a%row_p(row + 1) - IF (matrix_a%blk_p(a_blk) .EQ. 0) CYCLE ! Deleted block - a_col = matrix_a%col_i(a_blk) - a_col_size = a_col_blk_size(a_col) - ! - ! find the b_blk we assume here that the columns are ordered ! - CALL dbcsr_find_column(a_col, b_frst_blk, b_last_blk, matrix_b%col_i, & - matrix_b%blk_p, b_blk, found) - IF (found) THEN - b_col_size = b_col_blk_size(a_col) - IF (a_col_size .NE. b_col_size) DBCSR_ABORT("matrices not consistent") - ! - nze = a_row_size*a_col_size - ! - IF (nze .GT. 0) THEN - ! - ! let's trace the blocks - a_beg = ABS(matrix_a%blk_p(a_blk)) - a_end = a_beg + nze - 1 - b_beg = ABS(matrix_b%blk_p(b_blk)) - b_end = b_beg + nze - 1 - fac = REAL(1.0, ${kind1}$) - IF (row .NE. a_col) fac = sym_fac - - trace = trace + fac*SUM(a_data(a_beg:a_end)*b_data(b_beg:b_end)) - - ENDIF - ENDIF - ENDDO ! a_col - ENDDO ! a_row - ! - ! sum - CALL mp_sum(trace, dbcsr_mp_group(dbcsr_distribution_mp(matrix_a%dist))) - - END SUBROUTINE dbcsr_dot_${nametype1}$ + IF (matrix_a%replication_type .NE. dbcsr_repl_none & + .OR. matrix_b%replication_type .NE. dbcsr_repl_none) & + DBCSR_ABORT("Trace of product of replicated matrices not yet possible.") + + sym_fac = REAL(1.0, ${kind1}$) + matrix_a_type = dbcsr_get_matrix_type(matrix_a) + matrix_b_type = dbcsr_get_matrix_type(matrix_b) + matrix_a_symm = matrix_a_type == dbcsr_type_symmetric .OR. matrix_a_type == dbcsr_type_antisymmetric + matrix_b_symm = matrix_b_type == dbcsr_type_symmetric .OR. matrix_b_type == dbcsr_type_antisymmetric + + IF (matrix_a_symm .AND. matrix_b_symm) sym_fac = REAL(2.0, ${kind1}$) + + ! tracing a symmetric with a general matrix is not implemented, as it would require communication of blocks + IF (matrix_a_symm .NEQV. matrix_b_symm) & + DBCSR_ABORT("Tracing general with symmetric matrix NYI") + + a_row_blk_size => array_data(matrix_a%row_blk_size) + a_col_blk_size => array_data(matrix_a%col_blk_size) + b_row_blk_size => array_data(matrix_b%row_blk_size) + b_col_blk_size => array_data(matrix_b%col_blk_size) + + CALL dbcsr_get_data(matrix_a%data_area, a_data) + CALL dbcsr_get_data(matrix_b%data_area, b_data) + + ! let's go + trace = REAL(0.0, ${kind1}$) + IF (matrix_a%nblkrows_total .NE. matrix_b%nblkrows_total) & + DBCSR_ABORT("this combination of transpose is NYI") + DO row = 1, matrix_a%nblkrows_total + a_row_size = a_row_blk_size(row) + b_row_size = b_row_blk_size(row) + IF (a_row_size .NE. b_row_size) DBCSR_ABORT("matrices not consistent") + b_blk = matrix_b%row_p(row) + 1 + b_frst_blk = matrix_b%row_p(row) + 1 + b_last_blk = matrix_b%row_p(row + 1) + DO a_blk = matrix_a%row_p(row) + 1, matrix_a%row_p(row + 1) + IF (matrix_a%blk_p(a_blk) .EQ. 0) CYCLE ! Deleted block + a_col = matrix_a%col_i(a_blk) + a_col_size = a_col_blk_size(a_col) + ! + ! find the b_blk we assume here that the columns are ordered ! + CALL dbcsr_find_column(a_col, b_frst_blk, b_last_blk, matrix_b%col_i, & + matrix_b%blk_p, b_blk, found) + IF (found) THEN + b_col_size = b_col_blk_size(a_col) + IF (a_col_size .NE. b_col_size) DBCSR_ABORT("matrices not consistent") + ! + nze = a_row_size*a_col_size + ! + IF (nze .GT. 0) THEN + ! + ! let's trace the blocks + a_beg = ABS(matrix_a%blk_p(a_blk)) + a_end = a_beg + nze - 1 + b_beg = ABS(matrix_b%blk_p(b_blk)) + b_end = b_beg + nze - 1 + fac = REAL(1.0, ${kind1}$) + IF (row .NE. a_col) fac = sym_fac + + trace = trace + fac*SUM(a_data(a_beg:a_end)*b_data(b_beg:b_end)) + + ENDIF + ENDIF + ENDDO ! a_col + ENDDO ! a_row + ! + ! sum + CALL mp_sum(trace, dbcsr_mp_group(dbcsr_distribution_mp(matrix_a%dist))) + + END SUBROUTINE dbcsr_dot_${nametype1}$ ! ************************************************************************************************** !> \brief Interface for matrix scaling by a scalar @@ -2918,30 +2918,30 @@ END SUBROUTINE dbcsr_dot_${nametype1}$ !> \param alpha_scalar ... !> \param last_column ... ! ************************************************************************************************** - SUBROUTINE dbcsr_scale_${nametype1}$ (matrix_a, alpha_scalar, last_column) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a - ${type1}$, INTENT(IN) :: alpha_scalar - INTEGER, INTENT(IN), OPTIONAL :: last_column - - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_scale_${nametype1}$', & - routineP = moduleN//':'//routineN + SUBROUTINE dbcsr_scale_${nametype1}$ (matrix_a, alpha_scalar, last_column) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a + ${type1}$, INTENT(IN) :: alpha_scalar + INTEGER, INTENT(IN), OPTIONAL :: last_column - INTEGER :: error_handler - TYPE(dbcsr_scalar_type) :: sc + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_scale_${nametype1}$', & + routineP = moduleN//':'//routineN - sc = dbcsr_scalar(alpha_scalar) - CALL dbcsr_scalar_fill_all(sc) - sc%data_type = dbcsr_get_data_type(matrix_a) - CALL timeset(routineN, error_handler) - IF (PRESENT(last_column)) THEN - CALL dbcsr_scale_anytype(matrix_a, & - alpha_scalar=sc, & - limits=(/0, 0, 0, last_column/)) - ELSE - CALL dbcsr_scale_anytype(matrix_a, alpha_scalar=sc) - ENDIF - CALL timestop(error_handler) - END SUBROUTINE dbcsr_scale_${nametype1}$ + INTEGER :: error_handler + TYPE(dbcsr_scalar_type) :: sc + + sc = dbcsr_scalar(alpha_scalar) + CALL dbcsr_scalar_fill_all(sc) + sc%data_type = dbcsr_get_data_type(matrix_a) + CALL timeset(routineN, error_handler) + IF (PRESENT(last_column)) THEN + CALL dbcsr_scale_anytype(matrix_a, & + alpha_scalar=sc, & + limits=(/0, 0, 0, last_column/)) + ELSE + CALL dbcsr_scale_anytype(matrix_a, alpha_scalar=sc) + ENDIF + CALL timestop(error_handler) + END SUBROUTINE dbcsr_scale_${nametype1}$ ! ************************************************************************************************** !> \brief Interface for matrix scaling by a vector @@ -2949,59 +2949,59 @@ END SUBROUTINE dbcsr_scale_${nametype1}$ !> \param alpha ... !> \param side ... ! ************************************************************************************************** - SUBROUTINE dbcsr_scale_by_vector_${nametype1}$ (matrix_a, alpha, side) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a - ${type1}$, DIMENSION(:), INTENT(IN), TARGET :: alpha - CHARACTER(LEN=*), INTENT(IN) :: side - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_scale_by_vector_${nametype1}$', & - routineP = moduleN//':'//routineN - ${type1}$, DIMENSION(:), POINTER :: tmp_p - TYPE(dbcsr_data_obj) :: enc_alpha_vec - - CALL dbcsr_data_init(enc_alpha_vec) - CALL dbcsr_data_new(enc_alpha_vec, ${dkind1}$) - tmp_p => alpha - CALL dbcsr_data_set_pointer(enc_alpha_vec, tmp_p) - CALL dbcsr_scale_by_vector_anytype(matrix_a, enc_alpha_vec, side) - CALL dbcsr_data_clear_pointer(enc_alpha_vec) - CALL dbcsr_data_release(enc_alpha_vec) - END SUBROUTINE dbcsr_scale_by_vector_${nametype1}$ + SUBROUTINE dbcsr_scale_by_vector_${nametype1}$ (matrix_a, alpha, side) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a + ${type1}$, DIMENSION(:), INTENT(IN), TARGET :: alpha + CHARACTER(LEN=*), INTENT(IN) :: side + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_scale_by_vector_${nametype1}$', & + routineP = moduleN//':'//routineN + ${type1}$, DIMENSION(:), POINTER :: tmp_p + TYPE(dbcsr_data_obj) :: enc_alpha_vec + + CALL dbcsr_data_init(enc_alpha_vec) + CALL dbcsr_data_new(enc_alpha_vec, ${dkind1}$) + tmp_p => alpha + CALL dbcsr_data_set_pointer(enc_alpha_vec, tmp_p) + CALL dbcsr_scale_by_vector_anytype(matrix_a, enc_alpha_vec, side) + CALL dbcsr_data_clear_pointer(enc_alpha_vec) + CALL dbcsr_data_release(enc_alpha_vec) + END SUBROUTINE dbcsr_scale_by_vector_${nametype1}$ ! ************************************************************************************************** !> \brief Interface for dbcsr_set !> \param matrix ... !> \param alpha ... ! ************************************************************************************************** - SUBROUTINE dbcsr_set_${nametype1}$ (matrix, alpha) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix - ${type1}$, INTENT(IN) :: alpha + SUBROUTINE dbcsr_set_${nametype1}$ (matrix, alpha) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix + ${type1}$, INTENT(IN) :: alpha - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_set' + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_set' - INTEGER :: col, handle, row - TYPE(dbcsr_iterator) :: iter - ${type1}$, DIMENSION(:, :), POINTER :: block - LOGICAL :: tr + INTEGER :: col, handle, row + TYPE(dbcsr_iterator) :: iter + ${type1}$, DIMENSION(:, :), POINTER :: block + LOGICAL :: tr - CALL timeset(routineN, handle) + CALL timeset(routineN, handle) - IF (alpha == ${zero1[n]}$) THEN - CALL dbcsr_zero(matrix) - ELSE - IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & - DBCSR_ABORT("Incompatible data types") + IF (alpha == ${zero1[n]}$) THEN + CALL dbcsr_zero(matrix) + ELSE + IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & + DBCSR_ABORT("Incompatible data types") - !TODO: could be speedup by direct assignment to data_area, similar to dbcsr_zero() - CALL dbcsr_iterator_start(iter, matrix) - DO WHILE (dbcsr_iterator_blocks_left(iter)) - CALL dbcsr_iterator_next_block(iter, row, col, block, tr) - block(:, :) = alpha - ENDDO - CALL dbcsr_iterator_stop(iter) - ENDIF + !TODO: could be speedup by direct assignment to data_area, similar to dbcsr_zero() + CALL dbcsr_iterator_start(iter, matrix) + DO WHILE (dbcsr_iterator_blocks_left(iter)) + CALL dbcsr_iterator_next_block(iter, row, col, block, tr) + block(:, :) = alpha + ENDDO + CALL dbcsr_iterator_stop(iter) + ENDIF - CALL timestop(handle) - END SUBROUTINE dbcsr_set_${nametype1}$ + CALL timestop(handle) + END SUBROUTINE dbcsr_set_${nametype1}$ ! ************************************************************************************************** !> \brief ... @@ -3011,156 +3011,156 @@ END SUBROUTINE dbcsr_set_${nametype1}$ !> \param use_absolute ... !> \param filter_diag ... ! ************************************************************************************************** - SUBROUTINE dbcsr_filter_${nametype1}$ (matrix, eps, method, use_absolute, & - filter_diag) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix - ${type1}$, INTENT(IN) :: eps - INTEGER, INTENT(IN), OPTIONAL :: method - LOGICAL, INTENT(in), OPTIONAL :: use_absolute, filter_diag - CALL dbcsr_filter_anytype(matrix, dbcsr_scalar(eps), method, & - use_absolute, filter_diag) - END SUBROUTINE dbcsr_filter_${nametype1}$ + SUBROUTINE dbcsr_filter_${nametype1}$ (matrix, eps, method, use_absolute, & + filter_diag) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix + ${type1}$, INTENT(IN) :: eps + INTEGER, INTENT(IN), OPTIONAL :: method + LOGICAL, INTENT(in), OPTIONAL :: use_absolute, filter_diag + CALL dbcsr_filter_anytype(matrix, dbcsr_scalar(eps), method, & + use_absolute, filter_diag) + END SUBROUTINE dbcsr_filter_${nametype1}$ ! ************************************************************************************************** !> \brief ... !> \param matrix ... !> \param diag ... ! ************************************************************************************************** - SUBROUTINE dbcsr_set_diag_${nametype1}$ (matrix, diag) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix - ${type1}$, DIMENSION(:), INTENT(IN) :: diag + SUBROUTINE dbcsr_set_diag_${nametype1}$ (matrix, diag) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix + ${type1}$, DIMENSION(:), INTENT(IN) :: diag - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_set_diag' + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_set_diag' - INTEGER :: icol, irow, row_offset, handle, i - LOGICAL :: tr - TYPE(dbcsr_iterator) :: iter - ${type1}$, DIMENSION(:, :), POINTER :: block + INTEGER :: icol, irow, row_offset, handle, i + LOGICAL :: tr + TYPE(dbcsr_iterator) :: iter + ${type1}$, DIMENSION(:, :), POINTER :: block - CALL timeset(routineN, handle) + CALL timeset(routineN, handle) - IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & - DBCSR_ABORT("Incompatible data types") + IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & + DBCSR_ABORT("Incompatible data types") - IF (dbcsr_nfullrows_total(matrix) /= SIZE(diag)) & - DBCSR_ABORT("Diagonal has wrong size") + IF (dbcsr_nfullrows_total(matrix) /= SIZE(diag)) & + DBCSR_ABORT("Diagonal has wrong size") - IF (.NOT. array_equality(dbcsr_row_block_offsets(matrix), dbcsr_row_block_offsets(matrix))) & - DBCSR_ABORT("matrix not quadratic") + IF (.NOT. array_equality(dbcsr_row_block_offsets(matrix), dbcsr_row_block_offsets(matrix))) & + DBCSR_ABORT("matrix not quadratic") - CALL dbcsr_iterator_start(iter, matrix) - DO WHILE (dbcsr_iterator_blocks_left(iter)) - CALL dbcsr_iterator_next_block(iter, irow, icol, block, tr, row_offset=row_offset) - IF (irow /= icol) CYCLE + CALL dbcsr_iterator_start(iter, matrix) + DO WHILE (dbcsr_iterator_blocks_left(iter)) + CALL dbcsr_iterator_next_block(iter, irow, icol, block, tr, row_offset=row_offset) + IF (irow /= icol) CYCLE - IF (sIZE(block, 1) /= sIZE(block, 2)) & - DBCSR_ABORT("Diagonal block non-squared") + IF (sIZE(block, 1) /= sIZE(block, 2)) & + DBCSR_ABORT("Diagonal block non-squared") - DO i = 1, sIZE(block, 1) - block(i, i) = diag(row_offset + i - 1) - END DO - ENDDO - CALL dbcsr_iterator_stop(iter) + DO i = 1, sIZE(block, 1) + block(i, i) = diag(row_offset + i - 1) + END DO + ENDDO + CALL dbcsr_iterator_stop(iter) - CALL timestop(handle) - END SUBROUTINE dbcsr_set_diag_${nametype1}$ + CALL timestop(handle) + END SUBROUTINE dbcsr_set_diag_${nametype1}$ ! ************************************************************************************************** !> \brief ... !> \param matrix ... !> \param diag ... ! ************************************************************************************************** - SUBROUTINE dbcsr_get_diag_${nametype1}$ (matrix, diag) - TYPE(dbcsr_type), INTENT(IN) :: matrix - ${type1}$, DIMENSION(:), INTENT(OUT) :: diag + SUBROUTINE dbcsr_get_diag_${nametype1}$ (matrix, diag) + TYPE(dbcsr_type), INTENT(IN) :: matrix + ${type1}$, DIMENSION(:), INTENT(OUT) :: diag - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_get_diag' + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_get_diag' - INTEGER :: icol, irow, row_offset, handle, i - LOGICAL :: tr - TYPE(dbcsr_iterator) :: iter - ${type1}$, DIMENSION(:, :), POINTER :: block + INTEGER :: icol, irow, row_offset, handle, i + LOGICAL :: tr + TYPE(dbcsr_iterator) :: iter + ${type1}$, DIMENSION(:, :), POINTER :: block - CALL timeset(routineN, handle) + CALL timeset(routineN, handle) - IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & - DBCSR_ABORT("Incompatible data types") + IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & + DBCSR_ABORT("Incompatible data types") - IF (dbcsr_nfullrows_total(matrix) /= SIZE(diag)) & - DBCSR_ABORT("Diagonal has wrong size") + IF (dbcsr_nfullrows_total(matrix) /= SIZE(diag)) & + DBCSR_ABORT("Diagonal has wrong size") - IF (.NOT. array_equality(dbcsr_row_block_offsets(matrix), dbcsr_row_block_offsets(matrix))) & - DBCSR_ABORT("matrix not quadratic") + IF (.NOT. array_equality(dbcsr_row_block_offsets(matrix), dbcsr_row_block_offsets(matrix))) & + DBCSR_ABORT("matrix not quadratic") - diag(:) = ${zero1[n]}$ + diag(:) = ${zero1[n]}$ - CALL dbcsr_iterator_start(iter, matrix) - DO WHILE (dbcsr_iterator_blocks_left(iter)) - CALL dbcsr_iterator_next_block(iter, irow, icol, block, tr, row_offset=row_offset) - IF (irow /= icol) CYCLE + CALL dbcsr_iterator_start(iter, matrix) + DO WHILE (dbcsr_iterator_blocks_left(iter)) + CALL dbcsr_iterator_next_block(iter, irow, icol, block, tr, row_offset=row_offset) + IF (irow /= icol) CYCLE - IF (sIZE(block, 1) /= sIZE(block, 2)) & - DBCSR_ABORT("Diagonal block non-squared") + IF (sIZE(block, 1) /= sIZE(block, 2)) & + DBCSR_ABORT("Diagonal block non-squared") - DO i = 1, sIZE(block, 1) - diag(row_offset + i - 1) = block(i, i) - END DO - ENDDO - CALL dbcsr_iterator_stop(iter) + DO i = 1, sIZE(block, 1) + diag(row_offset + i - 1) = block(i, i) + END DO + ENDDO + CALL dbcsr_iterator_stop(iter) - CALL timestop(handle) - END SUBROUTINE dbcsr_get_diag_${nametype1}$ + CALL timestop(handle) + END SUBROUTINE dbcsr_get_diag_${nametype1}$ ! ************************************************************************************************** !> \brief add a constant to the diagonal of a matrix !> \param[inout] matrix DBCSR matrix !> \param[in] alpha scalar ! ************************************************************************************************** - SUBROUTINE dbcsr_add_on_diag_${nametype1}$ (matrix, alpha) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix - ${type1}$, INTENT(IN) :: alpha + SUBROUTINE dbcsr_add_on_diag_${nametype1}$ (matrix, alpha) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix + ${type1}$, INTENT(IN) :: alpha - CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_add_on_diag' + CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_add_on_diag' - INTEGER :: handle, mynode, node, irow, i, row_size - LOGICAL :: found, tr - ${type1}$, DIMENSION(:, :), POINTER :: block + INTEGER :: handle, mynode, node, irow, i, row_size + LOGICAL :: found, tr + ${type1}$, DIMENSION(:, :), POINTER :: block - CALL timeset(routineN, handle) + CALL timeset(routineN, handle) - IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & - DBCSR_ABORT("Incompatible data types") + IF (dbcsr_get_data_type(matrix) /= ${dkind1}$) & + DBCSR_ABORT("Incompatible data types") - IF (.NOT. array_equality(dbcsr_row_block_offsets(matrix), dbcsr_row_block_offsets(matrix))) & - DBCSR_ABORT("matrix not quadratic") + IF (.NOT. array_equality(dbcsr_row_block_offsets(matrix), dbcsr_row_block_offsets(matrix))) & + DBCSR_ABORT("matrix not quadratic") - mynode = dbcsr_mp_mynode(dbcsr_distribution_mp(dbcsr_distribution(matrix))) + mynode = dbcsr_mp_mynode(dbcsr_distribution_mp(dbcsr_distribution(matrix))) - CALL dbcsr_work_create(matrix, work_mutable=.TRUE.) + CALL dbcsr_work_create(matrix, work_mutable=.TRUE.) - DO irow = 1, dbcsr_nblkrows_total(matrix) - CALL dbcsr_get_stored_coordinates(matrix, irow, irow, node) - IF (node /= mynode) CYCLE + DO irow = 1, dbcsr_nblkrows_total(matrix) + CALL dbcsr_get_stored_coordinates(matrix, irow, irow, node) + IF (node /= mynode) CYCLE - CALL dbcsr_get_block_p(matrix, irow, irow, block, tr, found, row_size=row_size) - IF (.NOT. found) THEN - ALLOCATE (block(row_size, row_size)) - block(:, :) = ${zero1[n]}$ - ENDIF + CALL dbcsr_get_block_p(matrix, irow, irow, block, tr, found, row_size=row_size) + IF (.NOT. found) THEN + ALLOCATE (block(row_size, row_size)) + block(:, :) = ${zero1[n]}$ + ENDIF - DO i = 1, row_size - block(i, i) = block(i, i) + alpha - END DO + DO i = 1, row_size + block(i, i) = block(i, i) + alpha + END DO - IF (.NOT. found) THEN - CALL dbcsr_put_block(matrix, irow, irow, block) - DEALLOCATE (block) - ENDIF - ENDDO + IF (.NOT. found) THEN + CALL dbcsr_put_block(matrix, irow, irow, block) + DEALLOCATE (block) + ENDIF + ENDDO - CALL dbcsr_finalize(matrix) - CALL timestop(handle) - END SUBROUTINE dbcsr_add_on_diag_${nametype1}$ + CALL dbcsr_finalize(matrix) + CALL timestop(handle) + END SUBROUTINE dbcsr_add_on_diag_${nametype1}$ ! ************************************************************************************************** !> \brief Low level function to sum contiguous chunks of blocks of the matrices (matrix_a = matrix_a + beta*matrix_b) @@ -3174,41 +3174,41 @@ END SUBROUTINE dbcsr_add_on_diag_${nametype1}$ !> \param[in] found ... !> \param[in] iw ... ! ************************************************************************************************** - SUBROUTINE dbcsr_update_contiguous_blocks_${nametype1}$ (matrix_a, matrix_b, first_lb_a, first_lb_b, nze, & - do_scale, my_beta_scalar, found, iw) - - TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a - TYPE(dbcsr_type), INTENT(IN) :: matrix_b - TYPE(dbcsr_scalar_type), INTENT(IN) :: my_beta_scalar - INTEGER, INTENT(IN) :: first_lb_a, first_lb_b, nze, iw - LOGICAL, INTENT(IN) :: found, do_scale - - INTEGER :: ub_a, ub_b - - ub_a = first_lb_a + nze - 1 - ub_b = first_lb_b + nze - 1 - - IF (found) THEN - IF (do_scale) THEN - CALL ${nametype1}$axpy(nze, my_beta_scalar%${base1}$_${prec1}$, & - matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b), 1, & - matrix_a%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a), 1) - ELSE - matrix_a%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) = & - matrix_a%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) + & - matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b) - ENDIF - ELSE - IF (do_scale) THEN - matrix_a%wms(iw)%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) = & - my_beta_scalar%${base1}$_${prec1}$* & - matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b) - ELSE - matrix_a%wms(iw)%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) = & - matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b) - ENDIF - ENDIF - END SUBROUTINE dbcsr_update_contiguous_blocks_${nametype1}$ + SUBROUTINE dbcsr_update_contiguous_blocks_${nametype1}$ (matrix_a, matrix_b, first_lb_a, first_lb_b, nze, & + do_scale, my_beta_scalar, found, iw) + + TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a + TYPE(dbcsr_type), INTENT(IN) :: matrix_b + TYPE(dbcsr_scalar_type), INTENT(IN) :: my_beta_scalar + INTEGER, INTENT(IN) :: first_lb_a, first_lb_b, nze, iw + LOGICAL, INTENT(IN) :: found, do_scale + + INTEGER :: ub_a, ub_b + + ub_a = first_lb_a + nze - 1 + ub_b = first_lb_b + nze - 1 + + IF (found) THEN + IF (do_scale) THEN + CALL ${nametype1}$axpy(nze, my_beta_scalar%${base1}$_${prec1}$, & + matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b), 1, & + matrix_a%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a), 1) + ELSE + matrix_a%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) = & + matrix_a%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) + & + matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b) + ENDIF + ELSE + IF (do_scale) THEN + matrix_a%wms(iw)%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) = & + my_beta_scalar%${base1}$_${prec1}$* & + matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b) + ELSE + matrix_a%wms(iw)%data_area%d%${base1}$_${prec1}$ (first_lb_a:ub_a) = & + matrix_b%data_area%d%${base1}$_${prec1}$ (first_lb_b:ub_b) + ENDIF + ENDIF + END SUBROUTINE dbcsr_update_contiguous_blocks_${nametype1}$ ! ************************************************************************************************** !> \brief Low level function to sum two matrices (matrix_a = matrix_a + beta*matrix_b @@ -3221,82 +3221,82 @@ END SUBROUTINE dbcsr_update_contiguous_blocks_${nametype1}$ !> \param[inout] my_flop ... ! ************************************************************************************************** - SUBROUTINE dbcsr_add_anytype_${nametype1}$ (matrix_a, matrix_b, iter, iw, do_scale, & - my_beta_scalar, my_flop) - TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a - TYPE(dbcsr_type), INTENT(IN) :: matrix_b - TYPE(dbcsr_iterator), INTENT(INOUT) :: iter - INTEGER, INTENT(IN) :: iw - LOGICAL, INTENT(IN) :: do_scale - TYPE(dbcsr_scalar_type), INTENT(IN) :: my_beta_scalar - INTEGER(KIND=int_8), INTENT(INOUT) :: my_flop - - INTEGER :: row, col, row_size, col_size, & - nze, tot_nze, blk, & - lb_a, first_lb_a, lb_a_val, & - lb_b, first_lb_b - INTEGER, DIMENSION(2) :: lb_row_blk - LOGICAL :: was_found, found, tr - - ! some start values - lb_row_blk(:) = 0 - first_lb_a = matrix_a%wms(iw)%datasize + 1 - first_lb_b = 0 - tot_nze = 0 - ! - DO WHILE (dbcsr_iterator_blocks_left(iter)) - CALL dbcsr_iterator_next_block(iter, row, col, blk, tr, lb_b, row_size, col_size) - nze = row_size*col_size - IF (nze .LE. 0) CYCLE - IF (lb_row_blk(1) .LT. row) THEN - lb_row_blk(1) = row - lb_row_blk(2) = matrix_a%row_p(row) + 1 - ENDIF - ! get b-block index - lb_b = ABS(lb_b) - CALL dbcsr_find_column(col, lb_row_blk(2), matrix_a%row_p(row + 1), matrix_a%col_i, matrix_a%blk_p, blk, found) - lb_row_blk(2) = blk + 1 - ! get index of a-block lb_a whether found (from matrix_a) or not (from workspace array) - IF (found) THEN - my_flop = my_flop + nze*2 - lb_a = ABS(matrix_a%blk_p(blk)) - ELSE - lb_a = matrix_a%wms(iw)%datasize + 1 - lb_a_val = lb_a - IF (tr) lb_a_val = -lb_a - matrix_a%wms(iw)%lastblk = matrix_a%wms(iw)%lastblk + 1 - matrix_a%wms(iw)%row_i(matrix_a%wms(iw)%lastblk) = row - matrix_a%wms(iw)%col_i(matrix_a%wms(iw)%lastblk) = col - matrix_a%wms(iw)%blk_p(matrix_a%wms(iw)%lastblk) = lb_a_val - matrix_a%wms(iw)%datasize = matrix_a%wms(iw)%datasize + nze - ENDIF - ! at the first iteration we skip this and go directly to initialization after - IF (first_lb_b .NE. 0) THEN - ! if found status is the same as before then probably we are in contiguous blocks - IF ((found .EQV. was_found) .AND. & - (first_lb_b + tot_nze .EQ. lb_b) .AND. & - (first_lb_a + tot_nze) .EQ. lb_a) THEN - tot_nze = tot_nze + nze - CYCLE - ENDIF - ! save block chunk - CALL dbcsr_update_contiguous_blocks_${nametype1}$ (matrix_a, matrix_b, first_lb_a, first_lb_b, tot_nze, & - do_scale, my_beta_scalar, was_found, iw) - ENDIF - ! - first_lb_a = lb_a - first_lb_b = lb_b - tot_nze = nze - was_found = found - ENDDO - - ! save the last block or chunk of blocks - IF (first_lb_b .NE. 0) THEN - call dbcsr_update_contiguous_blocks_${nametype1}$ (matrix_a, matrix_b, first_lb_a, first_lb_b, tot_nze, & - do_scale, my_beta_scalar, was_found, iw) - ENDIF - - END SUBROUTINE dbcsr_add_anytype_${nametype1}$ + SUBROUTINE dbcsr_add_anytype_${nametype1}$ (matrix_a, matrix_b, iter, iw, do_scale, & + my_beta_scalar, my_flop) + TYPE(dbcsr_type), INTENT(INOUT) :: matrix_a + TYPE(dbcsr_type), INTENT(IN) :: matrix_b + TYPE(dbcsr_iterator), INTENT(INOUT) :: iter + INTEGER, INTENT(IN) :: iw + LOGICAL, INTENT(IN) :: do_scale + TYPE(dbcsr_scalar_type), INTENT(IN) :: my_beta_scalar + INTEGER(KIND=int_8), INTENT(INOUT) :: my_flop + + INTEGER :: row, col, row_size, col_size, & + nze, tot_nze, blk, & + lb_a, first_lb_a, lb_a_val, & + lb_b, first_lb_b + INTEGER, DIMENSION(2) :: lb_row_blk + LOGICAL :: was_found, found, tr + + ! some start values + lb_row_blk(:) = 0 + first_lb_a = matrix_a%wms(iw)%datasize + 1 + first_lb_b = 0 + tot_nze = 0 + ! + DO WHILE (dbcsr_iterator_blocks_left(iter)) + CALL dbcsr_iterator_next_block(iter, row, col, blk, tr, lb_b, row_size, col_size) + nze = row_size*col_size + IF (nze .LE. 0) CYCLE + IF (lb_row_blk(1) .LT. row) THEN + lb_row_blk(1) = row + lb_row_blk(2) = matrix_a%row_p(row) + 1 + ENDIF + ! get b-block index + lb_b = ABS(lb_b) + CALL dbcsr_find_column(col, lb_row_blk(2), matrix_a%row_p(row + 1), matrix_a%col_i, matrix_a%blk_p, blk, found) + lb_row_blk(2) = blk + 1 + ! get index of a-block lb_a whether found (from matrix_a) or not (from workspace array) + IF (found) THEN + my_flop = my_flop + nze*2 + lb_a = ABS(matrix_a%blk_p(blk)) + ELSE + lb_a = matrix_a%wms(iw)%datasize + 1 + lb_a_val = lb_a + IF (tr) lb_a_val = -lb_a + matrix_a%wms(iw)%lastblk = matrix_a%wms(iw)%lastblk + 1 + matrix_a%wms(iw)%row_i(matrix_a%wms(iw)%lastblk) = row + matrix_a%wms(iw)%col_i(matrix_a%wms(iw)%lastblk) = col + matrix_a%wms(iw)%blk_p(matrix_a%wms(iw)%lastblk) = lb_a_val + matrix_a%wms(iw)%datasize = matrix_a%wms(iw)%datasize + nze + ENDIF + ! at the first iteration we skip this and go directly to initialization after + IF (first_lb_b .NE. 0) THEN + ! if found status is the same as before then probably we are in contiguous blocks + IF ((found .EQV. was_found) .AND. & + (first_lb_b + tot_nze .EQ. lb_b) .AND. & + (first_lb_a + tot_nze) .EQ. lb_a) THEN + tot_nze = tot_nze + nze + CYCLE + ENDIF + ! save block chunk + CALL dbcsr_update_contiguous_blocks_${nametype1}$ (matrix_a, matrix_b, first_lb_a, first_lb_b, tot_nze, & + do_scale, my_beta_scalar, was_found, iw) + ENDIF + ! + first_lb_a = lb_a + first_lb_b = lb_b + tot_nze = nze + was_found = found + ENDDO + + ! save the last block or chunk of blocks + IF (first_lb_b .NE. 0) THEN + call dbcsr_update_contiguous_blocks_${nametype1}$ (matrix_a, matrix_b, first_lb_a, first_lb_b, tot_nze, & + do_scale, my_beta_scalar, was_found, iw) + ENDIF + + END SUBROUTINE dbcsr_add_anytype_${nametype1}$ #:endfor END MODULE dbcsr_operations From 44efc3eaf2d911fcd75dc223d60c8ba8e98987f8 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Tue, 7 May 2019 16:38:09 +0200 Subject: [PATCH 43/88] README: warn about wrong results using OpenBLAS 0.3.6 on Power9 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 25fbc27d024..9b1c9d39def 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ You absolutely need: * GNU make * a Fortran compiler which supports at least Fortran 2003 (respectively 2008+TS when using the C-bindings) -* a LAPACK implementation (reference, OpenBLAS-bundled and MKL have been tested) +* a LAPACK implementation (reference, OpenBLAS-bundled and MKL have been tested. Note: DBCSR linked to OpenBLAS 0.3.6 gives wrong results on Power9 architectures.) * a BLAS implementation (reference, OpenBLAS-bundled and MKL have been tested) * a Python version installed (2.7 or 3.6+ have been tested) with Numpy From 1d22614ce130048c6b781f52c215060992091875 Mon Sep 17 00:00:00 2001 From: Alfio Lazzaro Date: Wed, 8 May 2019 16:54:18 +0200 Subject: [PATCH 44/88] Cantering logo --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b1c9d39def..04b43d6f21a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ DBCSR is a library designed to efficiently perform sparse matrix matrix multiplication, among other operations. It is MPI and OpenMP parallel and can exploit GPUs via CUDA. -![DBCSR logo](tools/logo/logo.png) +

+ +

## Prerequisites From bdd769bf5c83468021748b576474db86e3ca0d15 Mon Sep 17 00:00:00 2001 From: alazzaro Date: Wed, 8 May 2019 16:57:28 +0200 Subject: [PATCH 45/88] Move logo png to docs --- README.md | 2 +- {tools => docs}/logo/logo.png | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename {tools => docs}/logo/logo.png (100%) diff --git a/README.md b/README.md index 04b43d6f21a..3d0252f7f3b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ DBCSR is a library designed to efficiently perform sparse matrix matrix multipli It is MPI and OpenMP parallel and can exploit GPUs via CUDA.

- +

## Prerequisites diff --git a/tools/logo/logo.png b/docs/logo/logo.png similarity index 100% rename from tools/logo/logo.png rename to docs/logo/logo.png From 47edbbfdf8105319e57a516eaf3773534bec6e4e Mon Sep 17 00:00:00 2001 From: Alfio Lazzaro Date: Wed, 8 May 2019 19:05:07 +0200 Subject: [PATCH 46/88] Add badges for release and license --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3d0252f7f3b..5dc5160d164 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # DBCSR: Distributed Block Compressed Sparse Row matrix library [![Build Status](https://travis-ci.org/cp2k/dbcsr.svg?branch=develop)](https://travis-ci.org/cp2k/dbcsr) [![codecov](https://codecov.io/gh/cp2k/dbcsr/branch/develop/graph/badge.svg)](https://codecov.io/gh/cp2k/dbcsr) +[![Licence](https://img.shields.io/badge/license-GPL%20v2.0-blue.svg)](./LICENSE) +[![GitHub Releases](https://img.shields.io/github/release-pre/cp2k/dbcsr.svg)](https://github.com/cp2k/dbcsr/releases) DBCSR is a library designed to efficiently perform sparse matrix matrix multiplication, among other operations. It is MPI and OpenMP parallel and can exploit GPUs via CUDA. From 1f836bf376f112781c3a69b39e804ce7fe0df299 Mon Sep 17 00:00:00 2001 From: Juerg Hutter Date: Fri, 10 May 2019 09:31:53 +0200 Subject: [PATCH 47/88] mp_minloc/mp_maxloc had been hardcoded to message length 1 (cp2k/cp2k#346) --- src/mpi/dbcsr_mpiwrap.F | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mpi/dbcsr_mpiwrap.F b/src/mpi/dbcsr_mpiwrap.F index cbc92aaef22..7853f6e6984 100644 --- a/src/mpi/dbcsr_mpiwrap.F +++ b/src/mpi/dbcsr_mpiwrap.F @@ -2647,7 +2647,7 @@ SUBROUTINE mp_minloc_dv(msg, gid) ALLOCATE (res(1:msglen), STAT=ierr) IF (ierr /= 0) & DBCSR_ABORT("allocate @ "//routineN) - CALL mpi_allreduce(msg, res, 1, MPI_2DOUBLE_PRECISION, MPI_MINLOC, gid, ierr) + CALL mpi_allreduce(msg, res, msglen/2, MPI_2DOUBLE_PRECISION, MPI_MINLOC, gid, ierr) IF (ierr /= 0) CALL mp_stop(ierr, "mpi_allreduce @ "//routineN) msg = res DEALLOCATE (res) @@ -2691,7 +2691,7 @@ SUBROUTINE mp_minloc_iv(msg, gid) #if defined(__parallel) msglen = SIZE(msg) ALLOCATE (res(1:msglen)) - CALL mpi_allreduce(msg, res, 1, MPI_2INTEGER, MPI_MINLOC, gid, ierr) + CALL mpi_allreduce(msg, res, msglen/2, MPI_2INTEGER, MPI_MINLOC, gid, ierr) IF (ierr /= 0) CALL mp_stop(ierr, "mpi_allreduce @ "//routineN) msg = res DEALLOCATE (res) @@ -2735,7 +2735,7 @@ SUBROUTINE mp_minloc_lv(msg, gid) #if defined(__parallel) msglen = SIZE(msg) ALLOCATE (res(1:msglen)) - CALL mpi_allreduce(msg, res, 1, MPI_INTEGER8, MPI_MINLOC, gid, ierr) + CALL mpi_allreduce(msg, res, msglen/2, MPI_INTEGER8, MPI_MINLOC, gid, ierr) IF (ierr /= 0) CALL mp_stop(ierr, "mpi_allreduce @ "//routineN) msg = res DEALLOCATE (res) @@ -2779,7 +2779,7 @@ SUBROUTINE mp_minloc_rv(msg, gid) #if defined(__parallel) msglen = SIZE(msg) ALLOCATE (res(1:msglen)) - CALL mpi_allreduce(msg, res, 1, MPI_2REAL, MPI_MINLOC, gid, ierr) + CALL mpi_allreduce(msg, res, msglen/2, MPI_2REAL, MPI_MINLOC, gid, ierr) IF (ierr /= 0) CALL mp_stop(ierr, "mpi_allreduce @ "//routineN) msg = res DEALLOCATE (res) @@ -2823,7 +2823,7 @@ SUBROUTINE mp_maxloc_dv(msg, gid) #if defined(__parallel) msglen = SIZE(msg) ALLOCATE (res(1:msglen)) - CALL mpi_allreduce(msg, res, 1, MPI_2DOUBLE_PRECISION, MPI_MAXLOC, gid, ierr) + CALL mpi_allreduce(msg, res, msglen/2, MPI_2DOUBLE_PRECISION, MPI_MAXLOC, gid, ierr) IF (ierr /= 0) CALL mp_stop(ierr, "mpi_allreduce @ "//routineN) msg = res DEALLOCATE (res) @@ -2867,7 +2867,7 @@ SUBROUTINE mp_maxloc_iv(msg, gid) #if defined(__parallel) msglen = SIZE(msg) ALLOCATE (res(1:msglen)) - CALL mpi_allreduce(msg, res, 1, MPI_2INTEGER, MPI_MAXLOC, gid, ierr) + CALL mpi_allreduce(msg, res, msglen/2, MPI_2INTEGER, MPI_MAXLOC, gid, ierr) IF (ierr /= 0) CALL mp_stop(ierr, "mpi_allreduce @ "//routineN) msg = res DEALLOCATE (res) @@ -2911,7 +2911,7 @@ SUBROUTINE mp_maxloc_lv(msg, gid) #if defined(__parallel) msglen = SIZE(msg) ALLOCATE (res(1:msglen)) - CALL mpi_allreduce(msg, res, 1, MPI_INTEGER8, MPI_MAXLOC, gid, ierr) + CALL mpi_allreduce(msg, res, msglen/2, MPI_INTEGER8, MPI_MAXLOC, gid, ierr) IF (ierr /= 0) CALL mp_stop(ierr, "mpi_allreduce @ "//routineN) msg = res DEALLOCATE (res) @@ -2955,7 +2955,7 @@ SUBROUTINE mp_maxloc_rv(msg, gid) #if defined(__parallel) msglen = SIZE(msg) ALLOCATE (res(1:msglen)) - CALL mpi_allreduce(msg, res, 1, MPI_2REAL, MPI_MAXLOC, gid, ierr) + CALL mpi_allreduce(msg, res, msglen/2, MPI_2REAL, MPI_MAXLOC, gid, ierr) IF (ierr /= 0) CALL mp_stop(ierr, "mpi_allreduce @ "//routineN) msg = res DEALLOCATE (res) From caa98e3af72c7a402bb6c9a1d683c6a2549ba388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Fri, 10 May 2019 10:28:51 +0200 Subject: [PATCH 48/88] gitignore: un-ignore src/dist/ --- src/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/.gitignore diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 00000000000..54d3b402cdd --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +!/dist/ From 550f330746fdc31cde40b79ada4d996ec2a7db31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Sun, 12 May 2019 13:39:29 +0200 Subject: [PATCH 49/88] Makefile: make sure the shared library gets LDFLAGS and LIBS --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e0c9109278e..2db3a2bf1d6 100644 --- a/Makefile +++ b/Makefile @@ -508,7 +508,7 @@ libcusmm_benchmark.o: libcusmm_benchmark.cu parameters.h $(LIBDIR)/%: ifneq ($(LD_SHARED),) @echo "Creating shared library $@" - @$(LD_SHARED) -o $(@:.a=.so) $^ + @$(LD_SHARED) $(LDFLAGS) -o $(@:.a=.so) $^ $(LIBS) else @echo "Updating archive $@" @$(AR) $@ $? From f80d16fd34f012841b49ea23b4c2bd104f880296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Sun, 12 May 2019 13:41:32 +0200 Subject: [PATCH 50/88] makedep: ensure paths are fully normalized --- tools/build_utils/makedep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/build_utils/makedep.py b/tools/build_utils/makedep.py index dfc884975a9..5c08bb3bdc2 100755 --- a/tools/build_utils/makedep.py +++ b/tools/build_utils/makedep.py @@ -23,7 +23,7 @@ def main(out_fn, project_name, mod_format, mode, archive_ext, src_dir, src_files): messages = [] # process arguments - src_files = [path.join(src_dir, f) for f in src_files] + src_files = [normpath(path.join(src_dir, f)) for f in src_files] if mod_format not in ("lower", "upper", "no"): error('Module filename format must be eighter of "lower", "upper", or "no".') From 32270a66a0a0f60a89d2b6ff5ca4740baf84b962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Sun, 12 May 2019 13:45:07 +0200 Subject: [PATCH 51/88] makedep: use multiline strings/new-style format to improve readability --- tools/build_utils/makedep.py | 87 ++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/tools/build_utils/makedep.py b/tools/build_utils/makedep.py index 5c08bb3bdc2..9061c3b341b 100755 --- a/tools/build_utils/makedep.py +++ b/tools/build_utils/makedep.py @@ -100,24 +100,33 @@ def main(out_fn, project_name, mod_format, mode, archive_ext, src_dir, src_files find_cycles(parsed_files, mod2fn, fn, src_dir) # write messages as comments - makefile = "".join(["#makedep: %s\n" % m for m in messages]) - makefile += "\n" + makefile = "\n".join("#makedep: {}".format(m) for m in messages) + makefile += "\n\n" # write rules for archives - for p in packages.keys(): - if packages[p]["objects"]: - makefile += "# Package %s\n" % p - makefile += "$(LIBDIR)/%s : " % (packages[p]["archive"] + archive_ext) - makefile += " ".join(packages[p]["objects"]) + "\n\n" + for pkg in packages.keys(): + if packages[pkg]["objects"]: + makefile += """\ +# Package {pkg} +$(LIBDIR)/{archive}{ext} : {objs} + +""".format( + pkg=pkg, + archive=packages[pkg]["archive"], + ext=archive_ext, + objs=" ".join(packages[pkg]["objects"]), + ) # write rules for public files - for p in packages.keys(): - if "public" in packages[p].keys(): - makefile += "# Public modules for package %s\n" % p - makefile += "install: PUBLICFILES += " - for mod in packages[p]["public"]: - makefile += "%s " % mod - makefile += "\n\n" + for pkg in packages.keys(): + if "public" in packages[pkg].keys(): + makefile += """\ +# Public modules for package {pkg} +install: PUBLICFILES += {pubfiles} + +""".format( + pkg=pkg, pubfiles=" ".join(mod for mod in packages[pkg]["public"]) + ) # write rules for executables archive_postfix = archive_ext.rsplit(".", 1)[0] @@ -126,39 +135,51 @@ def main(out_fn, project_name, mod_format, mode, archive_ext, src_dir, src_files continue bfn = basename(fn).rsplit(".", 1)[0] - makefile += "# Program %s\n" % fn - makefile += "$(EXEDIR)/%s.$(ONEVERSION) : %s.o " % (bfn, bfn) p = normpath(dirname(fn)) + deps = collect_pkg_deps(packages, p) - makefile += " ".join(["$(LIBDIR)/" + a + archive_ext for a in deps]) + "\n" - makefile += "\t" + "$(LD) $(LDFLAGS)" - if fn.endswith(".c") or fn.endswith(".cu"): - makefile += " $(LDFLAGS_C)" - makefile += " -L$(LIBDIR) -o $@ %s.o " % bfn - makefile += "$(EXTERNAL_OBJECTS) " - assert all([a.startswith("lib") for a in deps]) - makefile += " ".join(["-l" + a[3:] + archive_postfix for a in deps]) - makefile += " $(LIBS)\n\n" + assert all(a.startswith("lib") for a in deps) + cflagsvar = " $(LDFLAGS_C)" if fn.endswith(".c") or fn.endswith(".cu") else "" + makefile += """\ +# Program {fn} +$(EXEDIR)/{bfn}.$(ONEVERSION) : {bfn}.o {deps} +\t$(LD) $(LDFLAGS) {cflagsvar} -L$(LIBDIR) -o $@ {bfn}.o $(EXTERNAL_OBJECTS) {linkerdeps} $(LIBS) + +""".format( + fn=fn, + bfn=bfn, + deps=" ".join(["$(LIBDIR)/" + a + archive_ext for a in deps]), + cflagsvar=cflagsvar, + linkerdeps=" ".join("-l{}{}".format(a[3:], archive_postfix) for a in deps), + ) # write rules for objects for fn in src_files: - deps = " ".join(collect_include_deps(parsed_files, fn, src_dir)) + deps = collect_include_deps(parsed_files, fn, src_dir) + mods = collect_use_deps(parsed_files, fn, src_dir) mods.sort(key=cmp_mods) # sort mods to speedup compilation - for m in mods: - if m in mod2fn.keys(): - deps += " " + mod2modfile(m, mod_format) + deps += [mod2modfile(m, mod_format) for m in mods if m in mod2fn.keys()] + if mode == "hackdep": - deps = "" + deps = [] + + deps = " ".join(deps) bfn = basename(fn) - makefile += "# Object %s\n" % bfn provides = [mod2modfile(m, mod_format) for m in parsed_files[fn]["module"]] + + makefile += "# Object {bfn}\n".format(bfn=bfn) for mfn in provides: - makefile += "%s : %s " % (mfn, bfn) + deps + "\n" - makefile += "%s : %s " % (src2obj(bfn), bfn) + deps + makefile += "{mfn} : {bfn} {deps}\n".format(mfn=mfn, bfn=bfn, deps=deps) + + makefile += "{bfnobj} : {bfn} {deps}".format( + bfnobj=src2obj(bfn), bfn=bfn, deps=deps + ) + if mode == "mod_compiler": makefile += " " + " ".join(provides) + makefile += "\n\n" with open(out_fn, "w") as fhandle: From 9dba5a414afdecc92020862c5465044e65bb3f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Sun, 12 May 2019 13:46:00 +0200 Subject: [PATCH 52/88] makedep: re-initialize opt. vars only if None The recursive calls rely on the fact that the optional variables reference the same datastructure. This fixes a regression from earlier refactoring which was not triggered in DBCSR but would occur in CP2K. --- tools/build_utils/makedep.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/build_utils/makedep.py b/tools/build_utils/makedep.py index 9061c3b341b..66a69715bd0 100755 --- a/tools/build_utils/makedep.py +++ b/tools/build_utils/makedep.py @@ -347,7 +347,7 @@ def find_cycles(parsed_files, mod2fn, fn, src_dir, S=None): if "visited" in pf.keys(): return - if not S: + if S is None: S = [] for m in pf["module"]: @@ -368,10 +368,10 @@ def find_cycles(parsed_files, mod2fn, fn, src_dir, S=None): # ============================================================================ def collect_pkg_deps(packages, p, archives=None, S=None): - if not archives: + if archives is None: archives = [] - if not S: + if S is None: S = [] a = packages[p]["archive"] From 394e06be27b5bd6a4e2ef4dc4962d344ab55e39c Mon Sep 17 00:00:00 2001 From: Alfio Lazzaro Date: Mon, 13 May 2019 08:47:28 +0200 Subject: [PATCH 53/88] Add ppt of the logo --- docs/logo/logo.ppt | Bin 0 -> 326144 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/logo/logo.ppt diff --git a/docs/logo/logo.ppt b/docs/logo/logo.ppt new file mode 100644 index 0000000000000000000000000000000000000000..fb5c599bc1992a8df92b03612703ef6f9c94ba71 GIT binary patch literal 326144 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 z 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
nlZOrTI7;I^QgR_p+9 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

tTBr5npE64a5h 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%0Au9hlUHS`kfR6nbHLl#ZBC_ zp?c_2U>!DrAqbr4(#4K$x{)1Ngw7((3Nx%FphZCHyU%e9H=T5G1{wV#}Mg=itas%MQ2v{Gy|6XmvKh5SZ;cQ!x z$+Eb2N`;&xko_P(s|mr`7cT~#oV=3yvqHfBR#};V#5-;gmI29c))CwY5AfUWq_G5E zo@#*F8@!{Qw5`Q^GjvRP<_b&dehE=ugSX|K|JqgZButGq9bqx;WwZ|hBl8Orf`n@e zr7?0Zc`lIWqx_vRiO&^N$QEPIkq&<=`_jz9*F%_dl%iFZ>sK#_7c$l)1woB@jB&+D zF~v7L(7q!$X>(u2tifdTS@@42_~OM&8CS>Y6YO|@`CtFvw3XX2iMUoR=pX;|&nt8? z`Jhp?ITf!MvuR#k%iT7V=BfcGMoOj?*R#1+ z+xav{GG?P*`1bA6@=mnG+Lfbe3A5_XuUg+cO+@e@I^)6iHSsqY&>tJ~QGp2tyeSvZ zg88PsK1YuoYnwTiO^GIyHbfJSpMUOHPG;E~jr-6#?+a}?g0{2anT6@ohRGZgkFgR^ zDJ%3Es~b7oAoNbVdlp&iS@vOUJj|KWu}}Z>g)?X0-?g^ujkR4n-+27s;k!rn@BHNc z{TpBZ^rt^%Kb6a;O^d!;ZkxFC-9S8z29K|O@x>Q=a({cT0MGY@bjp|ZtgT(xdvgB^ zIr!&rDr5K4r>mI_jUFKVdJbDpCz?)tS7w?7$k%`P2WjXrwvXKuJ0crTcJIkxpB%!E zVTd7!p^T1$-~6fReD+&7$1iQVFVf~*_%M3D^9Q1eZocZo zGsZM;@A)+?Sotkueg&{`qg0lPSC*;`{E1Va6KFCmoTr)LFZpGHpU8xQ5ee?%r2P#% z<1H>Y6V~s4R}N98xbz*HZo6>W^a)X(^Z|VaWD+cJr~i_AGq3ow8y_{==;`e8ncsE? zE_%a_+pM#|SDsm3b*hYTp7EF@i_dN58#wSIFbkXM`6=Tv_7p8?)JNkjFy!GI9h=6V zINu4WC$I6Hmwfn^W~9r@@UG^i8G#r3)cQAaA@zG+4?gh+LzK=gV=*t~LSN^WYX41INuv2HwWRQ(LrGn)D5Z3a# z`JMU*T?jifObtxa4jM3KX9sif2>WirFjE;9VAwR~9BKkSA%kWu?0Ri9b&pSVJT(3l98>m= zl<_X#2VeM?htj6nE@y|6vuL8*T;JVU7eIFm3YLhnSNrh%n>R!)2Z z4?Kw~ZO^HX=VMYj83-sGCcUbmtSeQTQ#X$wBf7_;N^bf6$2L{ht}GiJrdd_ zA=za0tJ^nP9>PBDF4HBKDY0kP*NlsHaxqLa*0V1{Kf*t{&=QY+1|uY^q`@#uJfxqJ8C z-e?_mWM;cM?1%POWAx#Nzv~8t>*@GuPAunXE_U&Lefz6)Gz1Mx9z(ETpH%HpX3ex} z-58V|(T;@EXN3Rjmi}jSX5k|JlTIsq6owm`$Hs}@7&tK9h^KLBd*Vyi@jUONVFrBS zVN7tq@B_E;M)qx={Iq>SoW4zZPmR`2VHuch>MVPdR2s5nQ5rcLCJmTJpO0Bb;DDiH zHWue|Ah6^G7jP%OGapDy+#6N?fzx=SzaQQUUi{%a-Nbz}zKr4~eB;LUGQ&?A@wYqi z(dhTQx8Q8c7fq)F3ry*xmpwmm{P=IPAQ&{7;R$T|#1W4yG#z}JUO-%`Be>A~WMB(i z-(3YrLBx}{L5N-HlB8uEbvn`|Y4KC`k)CFEAlWqegZ2hPf+WYk0)vf%L3Y{^mVUtz zU+DNdV2fWs>N>uOKm7s1!zQH4Q33ZG@fZI^s2*@^Ok6b0v3aUr{(v)t2W20cwJA^y z0XVeF5_!!5l5e6HcW>3eB&?qC=irau(R^HdvXle91o9w12e{ek8x)eNgF0A4kOysO zAIE05VY6DqOGx>Iz=>n|lk-h;OW?GW`th|Z)u=u}qx2^SVkSoDb0xNTggMvAvzKIR z>3Zkkl5gm3*%}87x;N7V4?5*BTwqs_$#*r8p(%XO2{q8d@~$v6S6})un$tV$IbkCm zj*X5zl_+#jR==J+lKydMaKRb%OcRE<)!xUiGzfd`wIMHp)ByrzGNI4M!EGobDGf~pCoM_dMK?|!8 z`_ErmIoC8}ZsFMy)GJ$f$q4C)y<^Iuoy&y<_k8*6}*psrha`AgMzKt6; zX{aBD7YLIyv8}_5;Wipx{^0$#gb7c2k6|M#6&PH(He^M{qmxXj)MF~NCw3%1eC4P;6N6rpo^Qv@;ZJ42*IjuljdJCB z`a8U7GKa6UW$x*-(GTi>3Cw5C?d(c8LH6V8S1Z`stZM_}7dfgC?eVV4)GwGcR&EIo z?b@kN3!V|8@d8>;N7$F6a;S5Ht<@YtyY>b*?MIv8$_hzih3VJ7`sFwcCX27)#)P)ruk3ll9j2|Y<3JvYrCF3eawy{=W4Fp z&jfHi#s%vK4js6gNyDSy`S_Q={AGs2PBwM0-x;@U$p22@yqa0d&M?#d5dHc5Y%sj& zckT20XRm(r;n8>AdF#M?@4US$+K{yzqQB}WvtzMox_0eykCJhe({&~tY4m2$!oSY) z&h2PfPDg`cMoXw9WPYAox4yb{yEa-F$>u`Ws_r~`B+NMaAbk`;IDERP$wf(mjIov+ z6JF}OjyCP8PMH4Gw{Y>p+wIKSPYTaZXE=Z2{l3%QIl>jW!fNQ_oHTJG8z*Q6?qxvK zaVrzefSIn{&MjNQUp)U(8^sTQ{D-maOuW4s{(!mpw=HsfrN5|4n;Fl95xDu!^1xRs zZn@>%}k1gl+d%ez+?$m|&pi{7%p}=riH8yffbgzC6K*0<>_{4f@Lyy}60^q+4k# zK)U>*ple5veW@*%APy{U!kg*gvE5Dh?U@oVXE``2&}(9!=E6YLEkTa`ZOjEK8D?Dgx2`O&49#Bfx931LqHR* z)5TadVTgYxnc(mjFN86e&gETw9pwf$^F2CgG?{GA0cFrXWIM(&B_;yelZ=$lcpgnMFXy7_NoauNOVr&sXi-GC?A++^B( zL|NRd*YOEYx1)X4W2(mfw$Nc^AFiv_^SApr*B^c{`aw)8CF~HSr)i2Ojh-cJ$9$cLK;}AV0@cav7a{n+7y+>DEBT#1b3Qc{o=(|1 zQN#%OZkq`1{MCtaF%!|AVcjMnZh6Tg|H871V30WBC;w6}Cb=_S3)egvp%cuS-RU?J z37zopw3BA;GnVlqxTyOOXZ8(HA<&_PHb%G^`3095ullIuZzh`r6#@gNFd8=e6WDNZ zq#fy(z9IPIEj*_D!k(m_Opd^juL%sHt7mm5mU=)b<&bBNL<|1UR(|;7KPqjJVA4lh zR;OUw4C}{~ho;#PZTbzi)34q6;mR*_?lQY)+G4Pog4htZ?@0Cp`JmUwQQh(Wc)@TinyM`ZtrwFCGqa5czWD zL494|l4;OhJJrVEE8U!MX|KZJx^JILZ*mxT=wdRjKA7a7-{Z%3SDg5P?`Zs)z2YVI z?fNGd6`hK``p^I6zph67?AaGv&!r9eX?*~ce7{p^!NZ@u}eUFpo%KL7mFRZMgwv%BBE`Ky(8-+il^A~RRa(;cd5 zG&+N?GUIfPCI&p38J5~Nxag2|;E4qu=Z@(f5j{0BfWzsF)`tVJ9f-^ejk#E2K$^W{<^;)lF=DF#F zErjC@pE~}DXyJzaG!38p#hD%NH{is7YS~0mejTaqPkecZ5B^Nwe5A`ye4X{AMZ@^z zMQ6?Z7|#)X1GkuX@C%~ zdlu5SjHBDWN*j$}kO{7%*DhT8UN^bzOIm{KpckSHVR(;S9(iXbM#ENNM5O#Dw9xMH zr|pYZXg35abbz=Yy5$Alf1O6y0oX6e{jVYS$9EEo9 zVYJE-Tn*-j(jT9EA!pxb!2hE^{?nA-WFu)p+w}<4HX;f$*uzsD5qi=6>r7nK<<8vD z`eZrMlGfZQDo!&{9<(K()|Hs!wG)9%QPzU+>P}b-#7p6mbn2UBjW2pkhmZYJUE7^J z7<+Kqn0QaO)mQm`NmKfan8gt-&pzANBDM(@JH1A(h=QT|Dz*=LX^ z<_1g34#IC&P{P|sqT!}}e(9B0qrkhh@}oca<4h7i@0IB;X0ZX#N|wH3J(CDEh`;5p z?d;5f2aa#tyE-ndfe>1uyMCf(Ur&}=U(Kpa@-pdyJ3RFb$^aJa_Mx;P?H^v}&tJ@Y zR&X-0wwH{YbTsrg8Nx%-qd$1wwLN&1Uz7jRUxo}(mF#@BFYWfZjVDje>{?sB`{c>m zoz-1ycXz}>C4%%u%sWUqsitS&8GGq%oQnm%oh}50ryg$4&6_t}dVema9j}H#ew+$= z{gay;-^)_S@%P_*`(Sq7(pax%7P+djHSjmGRAIx(fBu{QSPeq%gYV#V@#6P-1d^ld zEYr~1S^}X_(fMG6&Bl-pZ+EnR;g2xy@NWYOGU9A^gsQ~1JM6JoV5E!X};(N zc>aKmhG5U~3F5?2B>u%?!Q0?C9a^Ee^nTNAk6*myvj8layvLPpdG>ShTcVB37r!FX z6yuLPX-0=uW-9<44Gd#I8vgRDfBxv0x7WwS1sz6*DGj)U)=@U_hU0IblWO|px!|ZQ zq8j*yfKC%N+}1hMNl(IE0>CzaYD-{qg66>6{8DM+8N7;D;u+`+YzCh>5H)T5+cAe= z@GShK6KBE~Z#VRru zJq?iXq0s{iUDV5@*LMGwXNjl8;?hEiYdL!eZlivbrS+5Y%X43robysz$X_|p)o+kv z(r2Wvz?5`d#+NQ>`~zXQAvh`{hhOAJfF68BKoc?sX4*RM75)@8c)^j4DMw&}7omWF zLeosRu+b4e;eDH=nA^Zzzeo6HTT0VtVt=d=8F$U z=SX|H-N8rN+G8h+!VRt!rV_FJMV`Oku9{!`x3C&-@a49`<3A%MXoL?X4_Xog+3m7* zw*t~KW%B9-m1=-PD>yk2zzWLk+=GBe@C~i)OE14x6A$Hjl(M}0+u!s=oFftRoSppI z>)HR5y0v0q1>^d)u`#tPWI+sF@W5~1{7tUjzOnLU%qaM|y;Iv_lEqx;FaG;~QzhHE z^B23pwnD$UT^!y>p*ta(e!k%;Ons{te1SG5==p5f#`oa0NoV{TCXZ6jB}I>VID%EA z_hZiVWlr<>w5DN00B##kbZz9bte(4#a`wk`;gh(G8#u)DyA3OM5`q(fbh8f>Wv_|IdCmvIQ=`@h( zFD$D%Xjp*Sx&xO{TCeOFfWcD{L_Al_e>`*@GU2;?PZ%b z!u8=`!Al(P+|NJkG%I8~raJv4GxL}p$ zOFs5c^7EVZLxAQs^IhOX?K8>+xj1=n;xAtPE*{c=D^1=x&z5P)?;CiFpE%0TA3XKx zrD^gyGwB$ac)y=70T>MN!2oO0XP)E%#>88B2_6#<@h2VDFk8Yoe&F;bw7gaz-^Ps9 ztR@n{#Rf!95`UyE{J_Ga1g6=^qsPwJIEy>$OEU1IX&C9zIBB6mK(oosfT4po@DJGX zB?Kf%3Zbn|!9YSCutA8Xb-SwoC*#C_`c!#`z+ynKNe2uAtF#0za0v?IJA!%efrD>p zgiisrg5i=`(^-1;FSrbW+3{dQV3$Vu;LndRWZ)s-5s+wf?ZGe*(jc)pCg9z?@kLC` zhV3*VfrjV!_3TfGhM2vyQzTB>*p@O7#N=ID^ziG>tQNstoh8$ODSYC`jyLyDv`j-l zMVFy5tl*aP6=eg@8@R&+X3&{;rW_e$$1QJgGuWB{ZA2&;!aT)71i%PAw5aTt@dW`l z2=zatdZXRuH;rTH9RF(R@>Jh&n0CF&^eh_3F)a{OsX6Wb_WE>_k#UHVG7eBB)oM;;isoIuP7-iA%jel>^&sc?5-ehznXpl}>QEYG!=Wyz z0SpZUpaD2j*v3=*Isu9Q;A2T3kR(ed1;?h0zkfrZmg`cN{QZ&#*i*=v`6ko4(ro|= zRYuzx{7O(BF!Bftbut9^neV_`extpK*EHdsv1cM-{6>`Wz2SG3pCl@CILUhWdj zwYKBxcbSxqHB3GDQkP3C!URG@QXqqxR4LPRaU2>Xk&l>cfV+Qd7r=VQordC&DZDo~}JzfBI-+JwpGz zjUD?R?OuC4?WMmHCLW>Sx8AM3AG_!%zZhPA){NW$}tXJ`xVg2Y2eiBUt?ei#q#tpnPTxwmv znZ7#>do-D)AJsR7A-3W!{bZPMB=tSxF3|eWRTB;|SOqSOmOMHREfK-yfDaGX6_0qx zlRxCYvz&k;-C$_~+OBCoLL~Kk?@# zUgfKL)H`qna>XfLt;+x#9cfy|!V@&g!;P2~SmQAU1|8wH(7Z=}iIZjoOd8PQl+KIC zl58n&de8_=+<+IMWMT3tl+aY^l;vw<)Eua%P(f%hK*>f%gIP5M zlru8roknbG42NJSKmWK2$lw&s2yr2p_y&v8aw)F?26XsM!5!|saW8}R$Jej+6bmO< zu-`Q}(=Zv-v!a$kEjGS1q8Xrilw34CE=;gn{-=NX^A7Y*pimDqN6p9JoPpF1dV}{6 zP$T^5KvJ&SrUt8OLKUV?S%`zdHu%9?JOrGBY2Sq>FE*ziW&;*mWE-#y-hc2%e^Skq z0hEAkX`S$V>V=%+oC(1G1LKynkzf8I-XLeFHR^j|i%~U8pn;xiowq^<-N(Qjja@oq zI9b|DBm+n9(D(4Equ1kol24^5@C%sYPDp%M}{c#1tTo=vPJO-fe06}Rc? zk2K*O{OLPCWmGTTl^smZw|q=`$AvcauD>Wr*0ka$>hNz`@ZdJuQ3dk?)bAyq1a;yo zE;zyhjPRyT&{~~vCO?U?7->S`W1Q4g?5NI9y z=;lJU+k?NE$qYMw`=8vPXb*!^Io$lI9e5JN)>Er`xH` zdY=B$ia*mNKgunD@4WBR?7I@+L{+dj~Ouyx4QjlV7rV$>~*K}6d zXc3z-of)l{xM@GlYr;-@?ziz-QCsf3v~NhB(o81}95`^eP23i`7XIQdtmVlQd@ipQ zqR|2<@cE$;?KFRG!WSrM$BKka{~7Na_$fnhr&%qHeaj>;d~bL0+Qb8`l)E^fYXZ?i z9{joTO*~s(-^+XJI^Sf|!u72-D@AA2_A8{Fl5o{gq=|JOk|%NTi$2;I7ke z8NpL#PJRJk(CceHH`9TeA21em`cI@dwv607A9~4C*e{B5tC+$2*q&OyQ zE*B3iraCm(=3f#exNhW|f-m0%C?pYd&|C+sq*YGsl&i4205O>HhbQ6KyWaqr9O5?y zewvdJ1`3`ej1z1OWT-)atAG>O1plcSO|pjjYr~S|<7?4)+2m`Ge<#{AH-R$*7v`PdGHp=%RfZvV4ARevhz6jb*_F#6fvLCUAu%iqFF%C`94otXB z+9q;BA)DtWeVj0olK|q_RH{E3Ld(|9hJLswc?Kr}g1&;ugX_!D%~EkeW>VD6tnn*- z04q)MOjd(8&|JO(m*9oobG4+w!An_09K1Q|WNu__;`|w9qk$V&n!C8dr5g6KxLVqI zzWQ%MIN^tY#{QHxx&muD6f}<2Uj-#v$qMp891W6d^T2220T>0XsjY1>LSNTm557VB zk+ygz-F7EW8eKfJkRY)~oe`)B-oYrD48o!Bbl$Z`&O@D60Ly#R1+l~sve^!s5C0`$ zCn1e5k3qjgA8_&K_C1}+W;vViT`z8v?6Kp=>gW9M{mVrot*20m z(qYgwgKWhU^;wGK8~wb87OfivMyjPh&d1=Z#7G}SK~TH=OpsTGth-5rr@`4RoF)Kl zrPnh-XGbr6Cw^b)M{Z05vZ~H2KC)=Px-b0te1?)UA@AL&1Y$3}>+bElj~|>kc5EXP7?jCS z7aD$xh3RjtARWS?`s!z&eYQKDa%fBBn>pIras{QAB1Q@OF~NX}*5 zn`OuyfBe%wTQz{5zqq4~!mf07Lsvh#TusX>ul+ECLT)8W$7$2K@Squzd5EpyeLk`8!?;$DhS5$@#^bWS#T7#;MaI56EwHG<^gUzz5C5NQpOoB0)O$8 z5#D*!)jt9Ci>Ey2yExL!XUn%F9R51)0monIMtO(OYU0!ZIhpd;8P(-!aPfs6ymf4a z#dE$d8IL@+GV(1N%x0P;S&_AQaMOW3@rrj+jI{Beg0XT*KhfJvyS$h%aJBc$b851O z0JoKw@>y1#g1fP7<|meS_-+km5gzidG)fqAhNn+sx*%>M5+Vs3jjy8O2R6Mq0c`#5 z*APJ_J0~*@AuV|)Ke*J+GjyMm zF~XY`@YP zqiu-Q&TOuAVT%d2%Wr7qUFHHWlK>lTpUcTWjs|?`F1z;O z#31mscNfh#jm%_)DGL*uIa#?8C8YfX=wf0{S@6k&>ye$ltkS?{Ww?#|HzH4D!m7Wx zaOr#Pub7*Vqk6GR`Z*hheDp-HEv9g{zzkB!b(;WML z@A3QZzyFNAbSpf6zK!l%DM+Wm>LJ)g$6ROEf&7mC;un8+_AmeP|G4zQ2bV8K3wXJH@;Y3dE?iA*Ci3hbe4@=St2$}iTSAE@ zt^jq*i?p1l@M?bZ1ViLy97^LJanh!~;e<;koReN0zlRT=R7lpa4CI!o9Hpg^7%&ay zG|?IYMkfR~zGe!*MK-{T%K+atK84QdU)&R~xCM{mvfv{b8gQBiZ$CIU{)E3Rp#BM; zG{|cmIMPj86i~h=jli#!z>Z_c!>kl zW_iI04*Zn?xcM!fbmDsVXW}>E)w_Y-M*QNiyiY&R{}zp8JkyEG71#V3f=uuTp=3o9 zGdgKqJiaFc>A=(R2icZwTY7-8rk1F)9%UT^Vat)QE;$D{L088NuHRPtp@o=X22_Kw z0i$sdjYFgSYrF)2FF$_%cfgZw7;QI9G6N_E#8teh z87CB0FwTq|9tQOThYn`oJ679h8dU?nf!p9oqx$u|ui8&p`ZnpG8dGf;yj|TSIL`I$ zkW``#`lAhcaOjMn%9TIJS*Nzpi*{>AE(}2MZmzpTA$Z)dk3x7fmn@w(T)exWu z-RnK_Nv2={AkE;65k4@AKN!IamJ=s28n+!tL=t7nuXWbWXtn*C!5#0mUecE-n|jno zqq`gL4Ax8sXb)|Y>e97b124si3*RTLvobsJN*KCMCu`VnVuslB{a{x1vs4jnzrBKq05Y!L~)Im=M=U88*MvnDDCo_{(+zk1m5shOQWMQ(UBLr zY3!>*k_!)n263al`dSJ|*;k=IyRn&wVV zEAUPF;1y-B#yoj}`y}bK5plp$S1mibz#~7de>dNQclb~|V{VUqNt{u(qz`ZEla0eO zZ*c4S$cD$@59fW~f31@UlZ1yUpVh5vS1*L_M??cG4+6} zi(Mj+#?JY_uDtce-?u+;jNpm%b1z07BKV%p1iHd>`bcNpJ0kdIR`qY){Gt<6W>i*x z^nJtbI>%u~=r&PbUH!7Or3JUb_4Zr8j&SZGmxGbNPIdF=_g?uym6#VV!U8?+$$qr|c)A6~C9GJlF5v-?+PD*V_HH{YP`A;`+DVUiw=pNaM%MLTVSCnaOkS zzWeT}zy9n0>%zP5zke}jAitOjJRasekb2mirJJ?Ps8%t>E3dxZ8IR2hVbj|7V9?G! z4PY6hG!BEu?OR{RKiAtv^YWXV>fyMXs~>&X*%u-7tFLaB0T}wT$+IcNF)$kDvCN8d zI8)w(Xc~bw(tbxP(ylZNg;<{Y2EXw6aNFLsLqAR*rwuNj1p=<{jw6X9Ea-0z)^H<0 zf0$$a3?Nspd=xmjc`36)ji?=B)8`u141yS$5Opz)N1rQCKqn3}e_{NTVLlVHY5F~q z6<>v2#_fJ9rZ`KVDA_0%cnb|mlbOHt6Nfw-KYyA}!X~_}fQ02^JFea*?8cKm@Hu5% zz&4IDaohc-hdk?K8ix+0Mc!w+B9!l0H>IKP{94CJE6sH9Dh`RTSQsd1)1BbEm|<=rQl3tTbLKIrBj z9vhzvU!;H4?{xAI+K=F^`fS~|vcpx|(tp#g8gId~g$3Pl!pH2&HW{|fktV~c##H|G(rI3!Na0870NT8dNurjNin zWWH^^{)@K%$kbb$28VBn|>{w?wITD+Gn{VDfs_k#}x zJnwi9a$M3YBRZrQZ~{-C(mWGay1~QBKH@W?Ra*XNmVjk)4~f9hapj2zz@d32pvhah z3c`uXK35qA921bFYaZ$?ZAcr5QlGq#Ri^yazm=)x5I?-==(&$Be^AArzTKV-edT8{d1Br{ zvG~HNv+eJ+2ge5rcUjcupMF|J0UL4l;7%|TpM3M9AO9qR`SqTb;`lv%oC}bw?q9oh zIsM+vQCGolDo2zReVKp|=IycAmz9LwyWXkY^!4jkYhO(e|6%s0p$mCTA;Xa&{OaCJ zu8ureeR67bb?4n@JD#qm3%#5E=ziioN5%T*X)pb)6Qn~ZT)pPJ=rokW*`0gx zum0+|gxFDm$pxbD@X>_s20(4uaRQt)wGe zui$LAc{i|x6&(%pB+UJIV`rBr2-aU`IS}KrtHMk=Ud{mPh9R@7ox9d+2h^Eccp&3j zm?x2vk4E8Ln+&hkwLzEjK^}1gEMCPAj%{;^u=(T{7*V`54yA#3#zuqTtz4*bKZ21N zqoaWc(O+i9Xj#~xPZOiO)2`S;oyqLeQtrf~cq#`R;yL}{Gx38z|F-WX zf-rrO0C8u0aY~B>>1Rbz7V(m8pPO)#PTV<0bzn%8^b7Tu9!}`t9WB83-PK6%+wzWY zUGa>6`m2VcowVZ4`Vp4o{iFDB5Z?Yd0qLLP(f@_hN)(?x%ygd6w7M=IQJ= z*4V!4+!ZDo$OzYPt41=|uv07&2^#}kqophv6jP`vj8u?LQ0)if4{x32cm|IkS)Pob z27k7OzU!9qq0y#;Ps=|7n|GL_gKe0D=8=$O^~L2vZ~>TL`5@Y43`Ljg}j#j%zfE8-Wcp5BVM(-@dKyA!a^JQfB|^CHfbiF^5de01t?mo z52qwJKX@JyMv(HulW=#sf~&-t`IsCzu>n#hMkWd66&{MT;7LUDF5ihq88zSjDF-?z z9U9bpsbN7cu2?30`Ez&`mNH1w$w;y!5S3;+wWdPDMV~K`74NLmKE`1VbYO>+6CHyUX z5Kz@D7ZqhvhJ}Wuk?+Biqf8R;8@$y%hnMg8mj0e1_iNxZ`;UQNg0lLi{$-n*Y&{J; zN7RWiRgqE`PLL9W$Dw9XXFxDY2XRf`jrWJYEwwIa)|B&Vdui5{+Wzi4XVy zTYU4f#pBZEmqAJ#{`g2;)h+py?+_tBw0d+&6`hJ7+YR$mj_h_vys zf54#&|I`2czpeb;FaKs`{iQnp4u~?wyCb?#D{t@!`XV!?@U~v{OOf8n@MBehW|vfkN`yMd$XL z9K3Ys(DDE9AO6FOzx&{Wi??rmbs+=SsWj#zr(Za|H#UPie)jW!St0sNHt4(%?M^qy zWb&8_NQX5x-5ol7F!hvr4wGsqqfu+bJ8LV%E{@jc`3TUKH_l{fh+PkRBbtwSlad|k z7||@y?Q?`^HJLia*JTz0+A@9(AuM6~HNpOfPf;Ze9HsMYg!mQKj2oQ6Ynxx5+Agh> zrEA))<9QN@j~@G~K!lm)5ts?nOj+z)$J{&9IJ(0oXFEds0|LYPmhdMoQ$I# z+u*{r0EDExZ^NB%O4tkun{EoZXwdRWoA@r(o9Rl2d;_Vr!E9SY5AoxabNcH`#KWlZ z9&wj+;ug<*qn+P&H{mr54sji460bb5E&gU*jA(=Ic?V;Ld7pW-Uc}u7OW0;v^4@%x za8cn}Sfi&OJQ9EO=Fo3s*v~<4uSv(hxGcQTs0+Ga!HQo&AnAP9|Bod{16R9lxROozGH3|7MwyRTwmeDDX3oKnCe{E#dt}EufOeqkADm@tC;X>bKK(MM za5%0DT{j}kp=r#_lGoD+YL0_Sn@pbC92oxDm*LFlm%smmnwFh8cP>XrT?(#yD`e}F z+Sg=ItbJm%#^7q_1ZK3+wh4e5>ulu7txP*e@=$DW@Np?S^7372rzl_&g6t_-NN4jL`-ihDkW zi4gl$^$3Fz)=g1q{3d;PkbNuD7m5J%U>c(yB|)Q@`i1|UGT`d)jZP9wQpQSRCJg$2 zywXx8nmKXI)gu*SFFb9y{QJMn=TmH}? zxG<&k4cCG~bfRkxd2G7zJ^F|l4o7tj z?x5AgdBkg7wT|;0nsu@uP5uofM#>s~Wy76TSXvY&c+n3IoSj-R$}s9eydfmORD3*I zs0tI$%Np|R001uSNklldXkU$ZYL!ZS^Yv&eTlvhZTCdEqTe7}yz|>P^L{v+so&}u-M4cJkvz0R z{Np4Xw4*#wPW}oZumAYhf4TB+{+IuK<^6ZwuF}(OsQQ=0&;HbB4$j-1_k)Qai-TwP zqs6})x~}ipv+M4o$A=#sSi8UR)?05qef{;<2crh^x5X_(_&+^5VP1T+cKPzsAHM2=p(^!@#>zW$?~XU?8qJ9qv*e1VfvD`q0!80C*v1M4WVT$ptF?esI&}I@2C*S$s{AXV#DSrZ~c*@Z4(U#Kq zMtnl9vh2!nQ#mrbWwLYG5)C2epZLZ5G$MmFJR{H{A49<6XxHjtCi4?k|~-;QHwZGd4CK@%(CMn@}fKNN?iIZDdwp zU}z^3{5Ep-u8vP9*@0jpoVa~W+UeQ}22qfdhZBcK=@|@!HYk74%{w}Zr~Z9UC6z%^ za)OAbKk{Iw>o_Q<)z~m{B`Xy!CU_hkHi#ZSaUufR*ij8Gp7(n-=c?-j?Ob;}mCp8F z&S(XnK*C>p(>@G%9l$aeLTU`?u=5*ul&<-#ZyU-O)Pr#c2l0mH#dBf!gei;Oz*8eD z9lcK%ViA-od`5V(!bH=!BUg;lB3o7Rt&R*f>V-!4W|k=pG;YCTNBPyQ+c_upqYAIX zRye!XzrM5b-jTOkPUp|!1y_C(q<{DB+tKvgSh<-=3E_K|m1ZSkcm;M7lSAo`XnO2g zcR7U3Zf;?_5VI;brx5^;9y?l^(Nv;=vbf6FBtRYdXW%+`@JMYFs{v0ZFeU~r8wv9z zj&yj(M$8HTXp~0M>wrVcIZLiaH1P?g>Y8cKmK!|oTRjZnuGCL@6_KBMl#x$x7gtPY zBaFXqD*)(*F1@u<-dHztq8s4fG)U+ra+&!iy}CbTqo zSA!q;vYEHo5)@j7_$rdXrAUoOfB=JM{S(Owjkpz(;SxSBw$kUV+TNrd3L5EJqV%IW zx3pz|@_(e)pAaGmy2Hf8_(RC3rng&u{rU*b?m)mP@_@PHi!-GGPn)y?mlR6m%$wd zcXuD$-EDApcXt@v-CYA@c|Y0xp8dVQw%$~wx{|tc>ZJR+`y^d`UAR!{c#hucejY?EK z+aq>At3O1_e%Z(R)3ry$pTm#q#S{24+SEEG;9hz~BtR%>Q0~|Bd-Ji6XXY1i3sZ@; zY-42FP2!IS z1&uA=`(=S6wlD$bA#-XRThqyKBo|8Cak?4-zQ9a=WlEpx8qjAd!*k8&RZr#n#WAQn zv+7ZD4BOK6YhwL+B0q{bzXvIn3s2Uz%bEA7 zc+t-*KgX6g--`m#zdnX3$9Mcq#?POT$4tJU`8nfKy(pTITLK%mo26ynR=yE}s%$|w zTMoA-?9;wRNIx5?^PE!E(oKHp4XV{GQ2i<~R1HX4pM0MI_ap=zb8T zh+%fw4P)GEv@MFjX!)ce z)%?TS`pfpre^F1AF@U(YVr-Pb&O+3nQL8^Vg0L=TLAege>`QvA{3t#p{%L`AX>A3F4YG<3jF%@d>#o32j8_cfP?+sA^WhKnE+*qiBVgT|uEx=+Khmz-t85INSds8Z zqEU2QlKK7Y+;G6kG6vwEeZ&6k0)(TElzh; zBTrDm2OT1mgxW5<6kIOMa#aBafN!nb|2-9~n~I%+`v7|W4o@VzBxH&W$D%}@D<=j) zM80StjO_BOsSa2Cob=uDK2jbCz=PciJ>Cfl+bm+M?vC*>9Ovi!J2Xl5vem=K3F+!D zg`*Pbz~R~Wh{g=p<=&eIuYreu$o<`>t0J)K1Zqj1^T0dnveeY3qx9}yTZ>hdty&Z=XC zl8pd!|0m~o*nIQU2>EfPeA>?-P_8o=+p41e%aB<3fO#+iS8t!?OZj?0kYioRy^w7w$)7lbFY>-kv_Cvo0bW&d!JlDRJ|&GMI_WK3y1 z(4cDW^uP2kwNN7rPiO5Lm6=aA303uHt1~@QY{Hnw)~^cwDz3FC z>F`3PdE}0M45vmSaU9Z&v32~OSKv6s8YNM~_GAugus9umNv>kU$&_=9joVYlqccit z7!1bZ+e*6U2KJg@DtYNbz(!<>x4;K*)5ptA$o;iHLNea4+vRCKh;MOHC||~Oz<4B8 z-?xnk%bk7Fp0P*c9ypgRSt0at_VkhJIIw)1<={yow&`-SQ%z)8j`nuSr;08|w{`N2 z9=bY&8e#}R)gv-8}>xP7VTRQVEutbOv%ep3VX71uaF^HfBo+Sg9UaAJA zU1hU;}tdDd_4H9%}qIBs!q<;)#ba`VQZO zsVHhB;i=z4VUFZn^PM~EO z7^b#F(@Qqk1su(u!7jkzX$Or|$v#;pao`Ttp#Hc?n>~HF4&9neu^v^?Xjw4Kr0-75 zlt!X`Nh5i=qpN9FX^F7c8AO|Mo+osuwp{xbh=gW=xhmG5SJMei?55v>7EA2;K6S%vz4F5${#-Eq^$VAI4rdnNE?nH<=4!<~bjdk#)>OB&>LuiXSr*Z26w@%C)Jo$wKtyC8W=P#@anq5$dNMupejY$3xFO>l3MA_6WkK^dO!62PxLyWg)zncMB>Y2*-u^Hc-u~4?wT)VEF@5fAE z-GRS>GOg=g#{2jbKd;GP1&!6LqmYIKTB#A_s@{mX7p7wngt`G-s*20S@)Ie+nU{zJ z{MFt@{(Z>`sdnsAQ4~2Zkt7|nRB7z|ew+^3KE@&AdkC@L!UiE7tzzk+@Xq0#k|_9IaX14sPxR&ev{z zue(1aJQiLit5>_3DUGf33B_BuGAt7t=V|?1#bIl}GI^;zQtFX@(SUD>j2Ie0YOpJD zX=4QN@I-0Jo>a?4x8rj$2~HPtW2(0VZJ%IAjq!+3{gm~V4`E+CT zac#e-&8+lfEGduNg?henF~iGS*5jiTV73xw=qdPK6LHsGM-R*pdz_EX6%xO%D)`9boNPu~CooCu;B4o?9rTIyJ!Ba&h3 zuyB-PM#-we46@`+W=-aka#`tMgdUSeEZ7kQFq~waz=d<*D7zAIV|n`juuAQ<_k9u6l3_aIA&~cG}Tms0RI_>r2s^ zWv~#1#^JEg)BXI3A{lKcNHOwA{nJOoDrfyBldru%2xA5I)(uw9F9zl<+7APK7v1i( z;E3%?w?nxe{*y=G=RB3HKzNNWY=2U=*Hihjsh=<_b8X&!C9!kPS`k165>f{dg4dvQ zO-eK^z`bHH*BjryaRd3upOT@S7*5W0;Ut$wqY8d^mAtB73J?6nT1(E`cK9NgS%5P} zz=8nJP?96PbvEiR9U+UDtoI^*-_K(o15nIw`=xAfaiS$}s%~#MA*jK+GYE|*Ut_jt zH&tB(+YvM4V4ZiQO6YHarJCK(Lp>!>uhpVyemkM&Alp!kyq`6_RyhHXCAKw2mhW_m zVZQxH{j_6z?O#;R>n#j31!?|&yuao}`na~ftqR9zVrgM0dQHh!T6E7lP&fsB)*=ue zeZx{j=h`Y}>x&`oGvE~=%knR{D&#?U?q5Ez;I4!4J4NIQ0nKu;tp8gy*7ZM)H7EyL zGejte0@S=GG6J87WqTe-d|YIWSV@C=BAD)RFENF@aT~IqE&&Rt6c*Qd(PQh66-y?5 zXno>*`E`_en%So~Wu`vahwF1Gcm^U-N$bB8#BsaoR-8#*c86CIdQIWRu#>16$W zg}qXE11K~CGA2~0OIz}U$`l(wib=#C2@#R}Yqy#g;gz7=h)o!OQKj0v*`==22ggU# zZ*F*n_a^=p@hat+3zg=Xx>ngYIoBlPMzrw|!fY75Q&vdiq}bLs$w@r;_z88+9b3U{ z=9A(2mdT&-sS<-67J?`db*6T@p^chC?^KNcsP3ri>SVLsd6iI8B^WOooc5k7 z?!_3tmX1ri1wDJJTXvtVTBFxNEx2=M5{vacpO!-r9Sl0_n{`U)1V-Q&xn2APjh^6K zaJ}@AjFP=*%vn=psI=_6hH|Ak7haBGY=cs3HVOxeys6?-d_44}RNQN%8u6XQdlj-a z7c~m>y-RbX?QBarV+aIGTd96yb)#GDdT>@?eorqB9n{VdXSIxzMnO&*#Qn9gzNJ@v z^y`4EgBQvb;dPC^AKhAB5%B$$s|<4M`yeM*e=}%3Z1N9Q{K6M)I=d5`HZSgdw8Eyp zIdvH=!}h$KJL;yd$D;vpy;=RdrXQu-lCPO~Vhzulko~}VMv3T%LWgb6+*6$c;*1LM$j!myrc_-U9hJ=Ni%(P8zQy=UEbb8C`Wl?C}X?&cg z63P1!R}BIcDzW`lN1>JsbNoFX(eKQ@L%n62)C?;gij!T{(yzXei>qLSCKMRg?`LMt zX(-*Vyu98h*tBJOMM6{It-jC7z(Xz{H zp=-!gm0G3WIr{0hXdg}(Jv)rOQsH8`wADOpUL>G5TC?&Z<~vF3KGYf{dS8P+Gbjo& zLJ~q|dxOIBaKiTiLvDQLS3N(_JcEA09bIRQZAnfs(4}dT{?d`n zE?6hH7`+fC@0p&ZAZI+48NDP=%Hgq~>C*t{B!Qhw1O5=YT=a=)%v-{I4-*>=d!3vH z^c*contlN2LqzwA=Jovk!EHB8bF74GCvML;t4I zL*jm@_yO8*!`Y!@ji!Ese}mP zhS(`$yXnB^{X+BCab7Ax?%%o2710A>A-OokRj?D7HpG9yQjJ47wYcLb(B}rWK^7dY zC0#{?2^Y01E%)~apmPnTx5WYy)5Lv;s*5A3A+F8PLY3srTH~efFsnPf4X&V;*|mi> z!rBTkyCOT=Y{`Edc%rJKi1GG04vFoZa}ol%fiyt>P*d%YI>xr9frs-EK@~vdN2aos zA64(0N9M#IAIKk4{DkY+Dq=Tm$kd_OP7%g$Fj%2zYOP4}#}UdAyu2c`#RB zQA(+fR~Dakhd`tD3!#Sh?4-u)fdS`~{3nUfd)NsnB(h&72Ge|p`@xIs9hJ75*j67M zP?y8Ui^QR_uXCcLlBevO11Jg@=v`l=<9~oGEBXTH`jhH zBZGrP+A|c~9!;_AL{z-hX=@S;OzS0Xm|;naPZL7G14=ntcifcMD9@i6RoBr60Oj?Z zt;&&5tFH>0sn9C#Fs_Ewt0W0D_HJZ?i;UB`?|=@lnz8^_uYwQhoRWuAmmgZvJz@O9 zZBRr9sSmE%kQe%|3?mX9CCy@fO?-Vj{Lb$p3;_*EOwZNlRy_C9ABO|(7iV{cyDfE| z|9oxYZVD$Y$&?f>g4P&#A^Ge`gA}MC7|nB&Vt#>Cpc|!X&A){Q3>XC0kX@;#*Hu z^r)trD<=Gwdt4cR`R{9x$+=eYGY{XL*HwQE9O)&JL!(JXL9Uj`3M#+9vZAABPA7AcAs1b<`EgGE06o;@JL zpVJ?(3_pBxL}DW?dn3#LVY;OD^AglI+8Y?=dPjjT`oPDQ^sQ77xydT?5cFcV1|^$) z2iMZ?&;I0Xc``0P8U3~Xr@5TZs@$+bQg=WQmbm>W!{x)^C6&KK8q^NgqbcR+&Bgdp zR+{ab%WwO<==qTn6bzF>-sgAU(nAUd;{a+xPw!Lb<2fhUXD!ADH3Yf9X~RX~CX3NJ z#DVb&Ba$-568m6#LFo#%U?h=9*H+z#Hut45z6JC~EV_86>fNE5r1HYnEY%6>1_WbZvyjjRaBN1(NEKC4UA)F{ z+qg|P)>(Ow+i@>4lEZ`~k?`u&r8 zmL7>cLmmF^vs+99OQGeI=T=YP5TM2vw?WBLvgDm^d&`k4LjGcVby?Qo&6bd!@`7Bj zJZ~cc#W-l1@osBU5@JPACGTQ7`03`2PHc`MRT@sC8qS}{q%qeyOut5rBW0h}sLA!N z6dQ)pLlKIG?7I7;Rs|Uzbs3`dq=yA3zs!?8uR2LGhGC5yX zdmvRJvDu4K1;1=lg}h-Wk_M&76=X6kRIh`5=>uClGgN8W0xO5hWhVqN(1V^M;1G9d zI{z@lSEBO;uqnWo>%i4+X{~0PQ156B7IN91a8Wu$5qHbb+f@A1-@i5QXDGxeT(Nb( zukoFV#sy*dG%N8x8HRasWe1%lbdsR!JU`ZytRFe_6F^e83b*N7{<)7k|#5- zslzH04>o!6UqfX>cIZ1uZyG<{Vdt9>+KUpGtE>3NIHom=`xx!952mYZL=6`wLZ)_@ za>TuyZkp+iVYZpUSqMuMOwyeRE+&zWPk}Q*V~^>puT7_2q^xt;=ZiB{GlD?tmoci` ztcZ{KQi&q)N!GMoRUiSBd3wM-M2H*iePkNbJe&i{$HOpvGJKOnamHafoZr`9W=R;s z>S@Rs_#uC;sskin!0s$8jRQVf1q3^k_|mNqt*C1FbgoMYN4jWN1D<6&i)s6UT~R$y zqG3zym+VaIeAkg+_p@4t)e*2zQpbKepQUEkiR68v#cr@Zd_IW_2UX|n^X*eNg{w^i z>n9+xKtwbK*S+~cbl+jSe8Ox?w!m4NCg0F&R2KbmOPz0MB@$*UHMNFxsFVexgiQx- zkKb#+hEAHFkbLiUGq}K?i`AxEEwn}&IGMWrGxw=IA7_tm11E-wyQpip!t|uY!9tuE z&Gj5#TZM0OI#<;DBOZ<~N-kyMBI=^Ed+%Se-Al4e%R}qX(8AIl>1sWjy+J%b9hQr| zE)+2gYD<*W+UU!A_E4cXraX>6_V=O9K(FmV;1vw$LSO5%GPX zFfyU!X2I^kVEc)QU8W~(KrKHxc!G5?w(WqueSD*KuC87R&e|Mab!?EA40~|@!!Rc$ zGNEz{#kOkbXoZ)Eww`X_Bcow)^A*n!-=Sd+l>8<)Ui{JK`>kY8L^h75W`xH}U(KNb zP7Dj}l{36371L?Us>PVG&hG%4AZ+lne|A7RmT)@(w4|7&hw?wHXM0hfNL~Q{C*)z| zK_e2qW^k0p5KQh-vcU;fQsd|>7vAn5FiAuPALP)`GWOZ1byo;X4Acl_?B|mEv*ST#k@jP!c&6&HW{esx*_FnfS7-rSGsry8# zIxd=ntSexGb33f5;#Vr2iZ$IVqvo<-+Hh1#M`7W6hnBgs$f1Bzpy9=XS9zv~2_sk6@mi857MdYu!A5^hexGdvYC{9ulg zS}*2XYP`fbr#fA)I>bpVtT-Qq4%{X0)rK_ADJhz?{?M=(Z=36(r2$US7%LYDon6g3 zV{a+T65T~HQrjX4U4!hqX@qHb#YcUK2F;CQ)Tld83+VI=0ZjhrK_0gu@a^D`jOfCE z1Im%wE1z^6nyovdpO?Qr&FYgrWT9*5vgXvV#w*#Ibv>)sSw*R#5ZgaRe5JbUk6n3d z4xP094Va?!s*q5^OIR;E{XhKi(%(?b^u`)rl}UDa0t}28jH_}azH65hZoD*mw`ZY4 zudtPc+wY?9;Xa~n+M{S_4HCG))83%03Wg8Zw6bl-H28r~K<+ohxuEmKj_WP{9p+o` zM3aJDC|A|!Ac0}913VbWwXvtkE5@E|Hn)I<@YJ%7nX8SqhA&ZWeMF(p7h{BsB8k=ZKQOy*9|TC7T4Mw=)@Nx!=U4Q$ zr9Rl~+dg*!&YvsErzG^VZ8@N1B?XPwVm7l1l%6(XnPYTrKiPhP2_kD?iok*^b)D*0 zMfXAgr%hsi1J_ZG$a=zO+Roq@{M$9Et(V(Oon%I?S~2T78i3sGI|)9bgx(B?q-1|I zmx=<>hk^l>K~;qi->l7o$;oqR=(|`udRG@x8g2B3{EGa~A5N!x$tUg_qb)r~r%wUq z+jPa&6T#A9U4o;1bm7EExlYJZX*_v@i1eeVVuQF(;~wFppBI=KJspIE6P&j)7A-CK zGFw{-wUr7sRlYzf;nn^IwYcf-9=+w(mO)gwuHQz6R{re@?pX&wR`J!P1d zbZcu{e&nAPqAd*Fn#UTgwZIHudNSq6mh?}j^zA;yvQXpv^>Bj8$3v3RBr~-79WWyg zKotEmB(yAr$R|V=h`?Sz2WxP{6e&q&t;F&sf$oy|N5mY>U*K;&+4a`nqIZUE;<0i$ zl7<|$m-&q4B(%rT>wpm8DwlKXhT>HmrT=z`f z0n0k;-Xv_|&lX%ygB5!$Bbx zBdr@-BJ#q4*cfp}B%J-SVuRqX=@`or6)Y%?s7kj0LQ)G{Go-eGyIQi7ivcHM?J6(TN&dEIEkySECfQU+RFiO1&wwwGv_+{d z%-GJx)$or6cO8M#vyr( ze;px7ss?P<1Mkx#{2o5pAVQ!!(%ItJ{rk%koN{y$NjOI~Y#7S38O1zd01?`0g!$he z{P(cj!q-1v_T&#wZZZ#8swsl0fAWu%MF{lD^3j)hr7PGuUZ6Vg=sWB^W>JwsYyh&wuVnO^9M_YB*Y&53XAg3gc zA7gVhfJu`=N+-Ph@I)kv7kM2RDcWpGtwu@9PkCe*y z-8BhAf_-`K3o9j?Ez=zi+=jzF#2!YD!oK9;B%$N=qDA>e-7Hv2W^+lyZ}V&x$p}n* zoDiZaO1{U4oNB!5zCPk9QrSCLU}|66{8J;Vv30R((bvqyDzce{#tdxB$V=y1Y5jHf z$KR{2Y*&~6-=jnfq}kp^@%s_A%DHoE-1`!$&Li)kvZ1K`RHin`m`}|co>4@ z`|A1e_IQpTcPtkiwhn9Rwv#j+h+H9ueMsK8@MC_M@1qZfcv85)+-{?l<*U}}^ejCi znuD!5%ssN18hM6#E*{>HIgL#^g<#+tV-B8o>=Ae8<;+W2k4Cv&np_G<(il>L#e@hE zCRO||(@Pf#Hg5uVeX^SgTq$w$a1G=aEg+o{<=zD&J@*%QhNK)`=hyZ|iJx^cF{=&w zxZ3P+bF0zqXpU9=)t+N0kD0mfBYXH9lhS3Zwt!-J3=_%Ivu}j%I;}sNXv#TJA+wn< zG)OJDOs^c38tSXnJMVv*9c$9Kgnja!GwGzw)TG!RrfKwv69*n~Hsb5?h3s{nhh-G1 zfnj%6=^wn76dv*r4C$Om`(o`1B^2|3JPu;2Ilw5JPF51_|FL8qDt8d<=yHs*&pr9z z86e5$T1Ya4#i0R!aZ28C=7$`SA}mv+qFhydz&qv^nz(DE_W#-vA{mL@dBv^W<(o;n zLf*(W&=zPYf8diK#yeKsPR<+Q>?b3skIoSBG)GS{*Dm)XJHf(bNhxI3`poWs`tpCf zR7-Scq;*|^bfGkX3vf!l#GNib=9qAr#82fR_(%A>lsM)TR2P_LLPS^nzy0w)-2%b~ zZ_5FJb*sDZfyFm}A^F+7W9wEj^vh~Rg&wz^m9_LNoBP3CfC`;9L zbshQ7FIV`v@2W=us`F3Le+J2%^hQPMn%j~HbVapO8od7e+_0sPAw|_DcehS8*OpBN z=Y(!iJZG7rZ1I5=@u?-Qlk@+C_$wZXutl7cb07vw*#YF4U4nZ&s$P$XS&+q>?IRER5$btn3j!c{!_>aLP61S`L@f9s? zGHq_NRcS{ww;#Puosa!ZB%e1E1k~d4z(+xedwnUwLoG@QD<67=W*!w||Hooq3b7Ak z&gcgRK}Yl+1z9X@#*TnT^0_wvdUh7e!0_42;ul(EEzLG$A9LBS&6QE16xi6Z4Q}y# zmsE;--Wj|8(?3f;4YCeOcG!9HJ8Kmxr*v~gpV|(;V$-tkVDSl3rvenQIRIQ$6z(S# z1B=r1xH1sb(JjDFgNDq@{xk6U)DVt+-Uksr)ztbIApTZV^3n(0ss_*-~Sa`n8tqO8K7lMy)Q(1=9k{|tGL0aOB4Nsh@cLo0Mkmo^jW3YWwy52R!h z-bZ$!?;l0sjS*~QK6mFWxQmlfz?{6I2Q6KoV8qdVZ2xVC>VJZm=?ja6v||qFhvd^| zfXYOTlFXZxk1_iS(T@ZJt}-7t|D?H&RLf`l#q$-U6x@!TXLDLBQ?}E8Z23>Wx`^i~lwbr2Y7zM@ z%Wczt5LEcin>2m3L0tQ=v2lL-RHj$;{+~djhSKwZN>DYIxZ`t~@;eduAc*wE8|2uR z{m^7p4)}kP-GAY!vl*7$LH?uZTLG)ye{A-Dq{IJp3;%y&FvNEJz<=~W$|A00L4kce z0OImu)jtga!EA|cYCFNeBo|#{{#45Z^`X;c3c~fwgGDIHOCrJJ!GB$g1dtL_1_Oim zx(u8h7W(U*>AD@OF9YtREcp|xdYa($>jj*>l$H}17&iKUJ;BX$=f6H84EA3Q_U_;p z*)VCQ;!VxKhXX{>=_zLBgOjf*pJ*besBzQ~(9C*WZ<&5rk7w5${?Fy%f5Ra(YD`c? zpa>}d(^>|?ih&8x(3DT0?o5?|{L|A@`?D;cb`61Sth=M`!dzgwEb;h`V` zgh^0g|J(ex3;r7g|9vC;cP{w<^w!XESFkPuU*Zq*J~}wb=e$Q5CB7{)eu5<#S$!N$qGl+e)ZuCX=h?MCenW z`}gACg52RK+=Vf>@AF{@uYd>L`!@z@MivMWcbN?yrKbOZskgx1C|tHs(LWOEw8@4u zLZEl%g;t0CP|?s(7gyI9B0=w$aPi3R(f-uY2ZHTyo-gmO_s2@p1l??QeSzQZC8Kay zuD?olSuJtwU&F_pb5T|k6dC+A8BS!n_BuAnz{&_&G8!jYClE`SLf7pKgaSV`L#wp| z^rQDOdaZzqR$AK)xi6m$&aoeyse+j!<_h&_gda2~t3Gt^A}ErY zZ{%Gbk1H~5MLAA*j|oP_ir8+x?c$7J3QjP0onD zpGilrWl&FEJ97}gQ&cHXSeVFMf`ozVTTjh%Wj#Ah`)wtG*UsG?yeqE$&nC5cA)wS_ zKPqY)@6y(Jt7`*&orj@4z7K==hPE8aSo1beGJnoNd-e>`&Ktg|5SQ!4N*n0?SV%vI z)pUG=ZH9jVPPRzcK#F8mI@oRHs^eNPqfZE(V!OJ8(F$c(K#SdcI!hi?g-S1B@5<_I zD`L60we*lWwIIj)ZcKc#TaDk4Ib)&ha`a!CdK?4n*iX|k_2vW}xtuRPOK9bS>2zGh z5v=l&a@gh9v}a>dp0hr*q$Wz%Ui`Z|9n{?Mv8B7l2t z>h`ZcCJ2jE@=|Do>xxv)YTKM_%U{LbyfPe6;C3f_UwdA)9p@AYI+;ynJl^Sp-j>_M zzlFXKQ&Nzf*3|8=L076n$8g!(fSF}35HIvvVNufBL>(8*oYWNLUR112_{xCR{hpFd zmVN7TJH4~F_P5fv2EI2LhOM;<4O|l)K65$g4!B*BY{R@>nEC?mIF@zpSzYi|i3|iz zf`g;-HwP@wnb)*_PY-A=u<4(;g~`v~pEkKsGO(M^L8yQGzGM#rv{8J_wyAQXbvZ5_ z{;%C)090^$`J8MI#=DG%Ik$357^di zKZtJ!lirNdc7hX%2rqu5u2q^Te^%&G$Kyy)F_?L+W{EortT~}U5ju49lBP)v=c=Pp zYB}(tYRPnm=a%~S=@XgbYYm^@O;D zVKF#CQPlvttv_2U-_tMih)Y@Pa2e6|{A&KVZTU@6a6R$8``!|!pL|C>C?-)!C>6^i zZ%fdtq1{d9_rNk)d3F~FyWUpV{1o2OC|I^Fn#4}q>4A#(EL-n~KFv(1G)>iYrRTJw z*R&R8bAychnpb1_uuKi?6Esp*b}$MYX=?Sh(J%H*fO<~4s7UJla5+d7q<%vPOoFvz z(Yp0{2=FlrAJ-q-m%eMy`0U822Z{Sl_ig=N$j?)9pk zpX7)NnJw{nFy4K#i?^N7o4RV!douFU@c(06)%5Qd+~JK#QXh*>;z#lLdPODNE`5}Q z&4;Q~cWmDGKQtdTcelz?SUTPJP|sekRZuSudL7O$ZfiiyZ8lCTqM#M-4o!*5Z>_O4 zy2!%~br90GIJF;O4l#7ALgUHMBfhl8(vHT2J~s7RlqSV-#cRu>V+7sLjr9Z1SNnVl zhO<}uU14MUf>AlSQ{p_+;?qfGigDjETM+X~s`Cg87=^mGk$sDL0~>_Mm6GVkRU@r5Tyv<8N@)SN|Bwz*LcWPx3C& z)<6ZQ%Pla~uTPZ?l;}esl;JG-Re8IQXb~kZz21E} z`#&+4br0wa+d}xS_CC2GKI&Pta4=Gfe$f8_-yZnzheTe15O?1z0wgwTIZbiU5fT!H zu>73Eo`@89Z!9A&yHS>|NtDfB_(LIgNUJ;kMR;6g?HmSmC9x&#-5)%{vO2SQY!2Jm z9<*a49=-1qgWON2`K}is3CcuZTi>H4o%sj<2C;m6uihk)UpHzTsij|U9>%j$Ro8V{ z;-s_;xt0IOy+SHCQ;IV6B;|`_GCljsV;bui3jA}!rzkcGLLZH!I^*j>qbDIJ72CIO zl)hYE%pN{)VR&JTEJV+Q56k%v>zuM;YnP=>%X%Gw3njA*ojqx_?DaLjC!~Fj1Wu7$ z2KHlxww_K3W7piWpGZA-l-`~Q-Y&d-{qVm+K34lwymFY!(>mhGF^LXsouX?f-DmdqZW1!`K^OYwS9J9OLZO`_$Gk)J3 zxCZg6x_I+!YCa&2`*C1?XvM|bxULw6jXO>-w77F#M3pWjKv>;fB_oPoT5YSq70-BA z&EokLqYB+mvTgAtYJA&uRp*Ml-iD&oxO?M;?%hT^`!Ib)(6&)tuAS)~-JEHErQ|b7_+mcl zmdynl=(NWoLU0?6(Oko3;n}sqj*)`@O4rJ5J$9t(9uHf{Y%WC*WuRC~@3@`N^>J&EqTNqag_vh;8!r=#o9x1I)&`7Wjz znpce0zElHr9h!|sngR=EIBMtf)Y1|a$tpuc=Cn&myhhwHC;k}?pLgKi2uafLWwNCl z4@EL{uS~&;^0K7A3FK^Y*)&XXVzFRWs&1Zn8TTn*CY(Vt>*;|{&x zS5q0IFM&vdCEd7*HcjK(AaN4IcDpu}5}>rorLtChbc1bZ5})EV=q5_*Lnbj3T|^1V zl}0t{%Kfc%BW-!%HXrfr-K#J3D7)Gp7N8ejG3XPCt@W!K=cTc#jRF(iDnau~F<-5a z3nfL&SOOsrxtaP3`}Z|>tH!2(zQ{cn{bKu$!y3DWsf$RDNv%KU$?m8h+uwA(Pec_G z?gX(vK-vdyI#;P*Q;fZsa~hSU`w>XBa&Qu1p4Y5u;D&_+zMwZ$Y5EtzZuyCdR6+`& z@UK!p8YYA;)>&XB$>a>#33fq|Z;<)*jC*2e}Y&aSfKbwoOxW&&Q=5skU05 z^th78{0_z8U!%npQb}5AE~U@lT%F)subt*~TZCA<<$$&W5FPNcWt*J8OedeZ2m~ORdE;T znqvCnz~_0h#VC8}@?pu~JOR9EgypZTf}4w{S`YTY@)#1q9>m)RJNKaM=*H=cOpUOHXVxyK$*prutwke(t$DN5rdv z!WoSQhK{4lII+ZD#c(r##`b>p1G5V`nI z)7;gPz6|pF@lu}>uA5Kbb;}HI#BCRJzw>NW%hsDF?i%v$$DUcHjVnP+{hvgaG3}dB zif7q9gr_QFC93qd?1(@NyqPt@#B;=#ezmBPHh7oPUV`Pj&qVU_J5`DBZ{CJ$;>Uo{o z4inv29nX}#OjJhK14Qs?6Y5M;tNjsi2MPuc$_D*$q)l<%IK1?T!^45{(CVZm?ysmP z)&xDr@Lj$1$kKlTWK-}rpq*HCN;FFrtWL2e^klAWO#vG#$$DAe^EA+3%XSV|;bME6 zaWaT8oIuH$cHOV~2b?dPNit!THqdqL!E}{iwz;N=zAIBL3A+0Ngpp@0`4VaE{UyGR zL8)nJv*PI6h3Rn$B00!Eb8^n!G9^B0G;+GVs1n<191AAyh@ve82ob7t0O+B=>Mz$Y zvcK6@5BgqAL)ST&!rBCS*A(-wa-BWen?1-Zrb)#1-kyouUBRwiA3Fh$K~GNY8-1r}(Qsa}PSZ7+2c} zMy#9|!t(^vEY+XH2TSVOY{L)Ag9owrR<6IjUobE%tG^27->xNkJm^N%g#*`Yl1=V7I&yxAFf4#y8>IG(>TZE z{$+#upWRz0+S7O62yxFINjiN7s-M=MDm=%Rut&&@Mdr^xqo3K*S5W7HN;h2vT%W^W{`p>1R23=@-r+^#lQ(#v zziU6w6C2e-)D#^)2Pe$G1Px!^U;T8e$>DH~GE|mvONDdsBNedZ0j_4&8!=mYW;Qm@ zn{L~{TSx0Lp#I1@5T>&WNnhQ&Ddo{eykpHB^1{S^{IMiE#2kgZ$WQU63R0e{r3AI+yrcI?+`ze1Pe z4Nnw{-bjUouz9y4NJx<~_g7xv=)6J0EAI@&jbiSimT)zj2a`W^McmJ+S~m|5tTlg7 zzmg$0OtDd}vUG6}b%6Q|h)2AHI!CK|MThvpizLD2ISX zNQJidx_a(G96DdZ`_?*KWrX$Y-}XU}!&8|)nC)A-T}kp#=vvaxw!Xx6OC)*7j*lTo zenY_lX_MKn+|!rV26SrUPi&sD3lPk`W0md-AOl$ZVQFT!@2^@oLQmfBhKilu^1%H_)=OSr@h z@UPkk3Y%P7q})*H9*y!tNer1!Zr%6U=RZ^AgGny?gB&*oHQcF_8`Azh*K|Bg7mq=# zRL}M3c!KDrZFRqIy7K39pVM@#5erO4tUL;q=HJTQIyj}V%F~Kp`1=$eug-R^G^QvZ zFWt%?ZH@v~=6FZ@Ze{OKLcq>9s9k6oc3p4*`7VY23J?*Cnotk*!kT|@7=kuGDM7ji}jb_ki%)UTLERIe9BOo4Dh5MAyi6S90iMC*T3aU zAEo4o4BI`V{9iPkg}O~Ny$mq0BP6= zDQQMGjPCgD`yR*pH$3-q-`91XpXjl&$x$;G(!;$&6{TfFjnDxF*Gxak zB}Aq3h$y~zY*V?>-mw*2geES|obgo!Nu;MAHY+*?=$O7Pw@hIwUZnq_v%7vaZvZ~` z-OntOTB(xCypWAdYNUo)s5`!z+c<6!+z2Zj?adRr1aC{><+hPq_EjE@lGBSxP-8#7 zeeNQe^NIP}1yO(yNY%5+21ns)yru}TI^LnY$$LhZ3YN!49 z%f7UFHa+PL567lVwyFNIVuW=y<4<*WOEXv{Y~06vi5q*v%)ju0dn&rH46lT+#C7|@ zQyV>Xa&TNPS!;UV4cb^h!JT0N3_U|qZUdv35LVR3n;b4b>)00`5|LeQvG>a644w78{ zbJbZDj$m}vsW$7qHztXYA^!HMz6+!Zn10CwMgFD=poRs$09lX%t_4z0@H}UpS`$Bq zr2#rW)mp$Zk5_k!!ldrdGG{5UJM$ctnZx5cBgS^EwK7H9qs{taC`b$PQD%>6EUMWX z@kme~Xf5@=M-lVUw_)rR{2uA>JZO>JXa*D;zlm4>(;+) zKz-`mf0*~~TEtMce&aDInRbd>ELE?Lw%%yn{QbRUSxPeZH6SZ>G6-sms1C5;^4M;@ z|CbMI_t^We{|A?~sOR8U+eTF_db}-AU>ePz`Yw+-$;p;(7AM(wAk%Jl@_gP;tC*cO zrtA3ji7X_N*Q9@)JL_P_{6j^)_UKUv`M!BqBj=JZHCvMQC0C~tGZV;~^l@c}r^DaV zOTT1NyU6BL)}Uh3+#dHi8>Z{4pR4mhq*%xk%J+h~sm)X#L54RurwfAfUdklRhW#)> zBw;lg{gB2V-vV#M;)EX9j$!NTH$-d{>gUyIDIBK=DJ0;2Bg_ zy`Af3;a%ld1#3?B4L5EBx{;=U5s!e8^$O3^RP5f4?Y#+2rxNX*=yqC4v0j5XV>a4h zqdHSL{OWj0E;ibM&xKQ<5)sGksJhby>ir${$uywb-??wZmHhE2O_>qa1i4d|T*1IM z2}{2HU0{jbleh4ArQF#O$`0XPh=C3S;_n=TLT+M`#fLONB#zuKR=Z`twPH!h2ztl6E(Z z8Sk^-HUnjHsuG8SeqwTcX{$qS;L}#LUm@E6;-Q+^E0UB`tjaCofpx3b=GUB0qnZ%0 znsHkCp=9DKQ@l_FJ)Ux@Z|ec{QIaV8FJJ7eM>9{esI=8zmikEfr{uc3Qj1eH2RuDe zxg5%EQY(G|{+CW6^eGnmhA4DbCHu6=L4k(Fjd=AF+q&s${(%Pg>09?aX~Q@Vw(3UQ zW2DiWEBnXo<2G9lWb3h_ARuf}h20tMYE(bgjhF(!+l@z9B+A|p#_P@<#bgjs>B9o_ zWos{(deO7sWb#j>ZDd#YdESiu)n?})H@cLZys1ONgPnDEh+EkKAzPWV&eeqFNx(6bI0Dz zV3Jte7or`rrQnki*w&UX$kE+874~kFI*5?C|6S(#!`Ud(zeHuc@g%rSZ@Thy?tbDY zpk24f%lUVn&g0|}k`1pTjC|^E)_M#xlEu`gB|@Ye<@hSCv#sKEWcA%of$IU-2PydT zD?~RJ@+5g3I`k?0{yqaG#vr{P#J2hjgP7`k%~SgcbT<3ou1qU1SSjZ@FzT+;X|9P7 zadx?1KX=gYRN1rvP@|f@)(R|}b8%g9klOQ~OTw}|5mSv4U)+-Z)QSa?=JcWo$;;^u z6juHuu_OI@kL9sVljw4mTBehAzcP~?$g}t7SLLnI(MH{%*VfuiGOSfz-wNIkC=}o< z@$@4;*seoqgN@sWSQ>$Lf=4DRUR+@guWStSv#y&>43vR9tj&}+o*e$>&jtGV-aXyW zooed;D-;k+c*m$Z>hx>aA29Z9(S1WK=7C@^d(giPkmjT`D~?CQqD$G8LGg?*f7EE7 zJs|7Oo+705J@HkvS?Nvk&WD5x9+kcr zE_K@69^Z_}JHITve!78&VDjSj_RE#;H=BaxhKGYfBLOks;8H-_+;ElCIQs<;d@-{v zdpINYGG0k8);$2E`gJmz^$5)oVi5vXj1>NHef~kqQnqIJ{HMzojToR{q`eB z5%X>y?eVgL#s;e(gRdCbmAFwZt5XvfrYz*z_UNTglpssW3H#feeX5O^53Ro5SEYx^ ze<79)H+m)$ee)&EhXTOYwQKkCz@U-!7{qC(`OlYqkR6)Do|7~mxlND~6&=#Kx8zd) zb_zca3o!Cr+oTG5YnD0ezC={4oqJ!2(I)SKbts-yr?j+cmU`9)n9o>QI=qj4(G4R? zUj?4==8LvdP*;R$RK$?{%NI&Z``+3pssQaJ&J{PbDbh{J7MA4TOJlV*%Q$^;ZF$d0@OUVbrKG9mN?GRFp(O|H9G+b9h>6iNntlp4#kX++hp$B z+YKV!>`)hMS5m>S`RlBDRqUN~@|zX~-l;{5;QxK~4GUj-(hSq(TUHycGChu@h;CHaUba8B~&})78Hq{ZNS&$RoBybqnL#ovzFXWkQ^8vzXAEIuMxEt!{i}dk3-Y9=DeJX_< zBLPTh{q#CyDV#zZ@-9c>M|4`-TZywW8Eel6oD2=e*RaXFAWxd)@X$v9AR#rx>ddeSIlWl@Qr zF~_s8JOz^`m%-XhM-KG+{|1L5gYUMrgG~piJ<=>8=9G7#z>>$;tZz)A+MD=0B1>diM>OW4Cc%^p@hD zKp6f2J2PA&8$La;_2I5*8kMa~wMoQJE+IjLtCng54+yz&C?ZBo7D8@=$@ayo1|id_ z95)sejD)0MN$Qu@yjM3=d~x_mK$)&mJ@~*uJQSej_4O{3YsP+-zh9M#Ep;m- zm{R7-DhdtrNSi?hNClrjP=jkNLM*yVzv)Cb*$BC+OzS#2$#ROmQ)!F+F~Ez5R#ds0^WrN1XWMb`tJ!WxJ9No0I5H5trpVl zJE$dVpQU67bDBT}CNu(`+mk%B)8R}LA=e&pi7cn3ve!1@!aA3J42kexbAC2RzT|Je ziJo$8*%y5l4wu3Q8A)SFEe&KowIavi;FXujw&S$r{Gp8R?b4!mMJckgexz@i2WU9V zk(&&HX~25a!dV=?L|4Hg8pT{xA#BWSLt5l@W=D=RA-4gKU`uSDFLvf5TTbmCqE%e4 zs;Jpi9GYI4pJRc?NCCKr=JHPg2m$--Qpo|?I+-{vN>iEUWMQ~_?^o|Us@y^?Fhf`0 zD#z(Et){9k0lHvG8~q>ik-0}veQfBt3rT8<4*MK;gy%^qFY~390*J`(?m1@^J26iC zMa9%3bU}DIkJZ#sW5XmjRlOG)je69aDJT(8fak>y4kOeEH^zs6c&}FAe4H%*R6`rs&Zas=h!VuoY1}B1bENb4Xqh%MdFIWr)9tWvYb&od*T<$; z=VLp3-n=qu&<{Z`n(cnAf>MjQ{ ziY;}6Q~Gi7M{>~~(!Z4qPGNh@61o6}M6KBReA!9{u%o^Lk9Qxn#~|*De*46I4(^lE zKX2&XO}x*sG8yPs6P(`*BlJz$rGp1QIg`w+`nQ`gKP&q0Mr4_EBxRGYRUGDv-M!R0 z;V1S!HRWt{hntOX=SAN_axc`y+j?1iB}?0EcT%>yHPDicU#pFI@*jgy@GLNy&&GSZ z!LR@B4MU4uh1kIv0zL0B5||8^P3XH7#vg#6^afw5CKlc zRb7_0rdK}Nm(ybBX)%c(nzoA|pOzM`aNV-{zyez1*OKHS%}sDG%BKU-v=xFZ@(!-b z5wh}8T_yd4uUf?r2@w)LdZIb6woMc`e7<-N;Zs|jC?2@qK;#td%wF97gd%=pT#q-{ zZ4}6ibi8FQX(R6MK7#kT5ze>x(%RN0m{oc8d>3d8`T);N74Se|OtQG^wl_J4tfv)= z#Yv#&Ik#H^d%PT6AfGbY9zSxl-^HRo(8qFr+&}Rf*{J@mUqtn_npBEYY^nX}Q%4(m z#vFJXVbg;;I!F!moj3s4>f&DgR&F6&e`lN3;KZw^ zbUek}q>ozF0`IjFH8B4624XJ~yD#25R~R;Q`)WmkAX53L-=iatNGmZ% zhW+HbIhJI44d9GdWZB@01vaT7j9S%NkHt}<8s-lj{If9RAGertgK4_R!yJjeO@bG! zzw^Ybb~3kudOViv3b0L}cZE2Qx;9J#EI`mJV}dPGtz3)l_{GBzJy|YDCCl3s7z| z*`FD|kEN8UEVV63WEu7Z^pl*Rl4eHUd1~`P%y~*=F(Zffb^+cL5tnaIJ8nja%xVTc zJ*m3??qz6Xv!^&wxV^)C|BUt4E1ICQsfr@ZMOv!=xkVvZ&?t8nd;j|~+sguQ$5K0D z>yfcu`;}};I!a0B3YCJKTDYf zeeD4_H-n@SAW9mX&z1)0r}HX^~Up=6rNximOoHdVVanFz`M8)sAi$)f)N3 zd2aJFB5;D)WQ+gIf8v%Q(#y22v;4(2;6Y5Z;Vv;PVZi|fIvu}r4!Z+n&aRP97WuC; z!qcr5<0fzddoK5CM%rR0V@F8TPAsV*qDlG&_$0GYl2XtKGzfGgm{7py{p0?fum6r> z91H;{M34Q(XI2pe4rrILMj?rgvgZ-b2j3ItX)?VM#%&_A$qE`q_ub!`h%yk8y+C@x zhtLIts7*vWbPU;RszQ)-HdtrXLs5x6>2E@cy}OzCi|HuBnftW?NmgBRuaIPYPNG23 z!r#Ny@@MBv9>eX_k@G==;@WO*UAkHHXG|?YcZyBAViYhW2l0t-ppg5GZvR2u9pX2$ zoaAb(Q0&niw?N+JcY&v#+O}Wv0Kr{($!}zOT0tsW%y2K&wYtoDMIsu-+QuoCRo+bF zh%lOjfOnOw(nZNCk3SMUY-aglJ(`BUmEvW z@G?4jrFG`YlzjKm1T=fZjaJMo7R{S4Ow%lHAEK9(BOVY*=2qM2L{ zk+y#F8g!2SN=oGr$a>*Tz8pjQR=|*G?LFfA^@wZ%!AYjLMQVO_|B1m<>ayc!(UldGGH!UkJ-CSepKA0c;c>DTNQ?&Mv_@pwMj0mWg+?h;T*h&9B$(;7BMa&ITtv z68{&29p5zk2Q`?d-UnK=&&58R@vmrdvSM*oP=^`WJx}7^?~Mq%!DQe_}F8Wvc+W>yNV-lt@QlSif`Zms@^P;$AcJ_ay%8W!<$C zl;7Uo52lv7oP(e+j2<`t)j8mG-h)=`i6ZXm`vVU@G|rT{$B>*;I|?=R@~^7%O35WN zyLHWT`Ba-dGr7KYTEGrUm7?a_Z1d6r_402ztbW$<*?p6>?_>ffAx_sN{Autt#j*%G zzmiIPW}LSy(u38jC`C45H6Kpau;4vsfY+CmRx`M`_Md6_(OXUMkGEdDEd^y(M{368 zT`GYp)3nZ^SD34bkHYTur2ho_YK4!&7;-#zmL661mRc6Kmu{2u*|c?%Em!2IekEn} z#oIM-m6rte_4`#N89OGnWv_CMPz26;Jkz`#^D?hy?$viX^*x(kQ&yF+;;J94b_oUw z{T**KC{pQFJ@G6Kbvs5F<#M~;o15IUc3gutxa_@yp~X+`DPMR$K8gGMK(qDxq0$JF z)Us}2J^7rS!C=@UHgK1VK`4YtDT~q%RE&qQ&XMJ>ANh`24S|&T+U5qXo!txw4O~S{(-S z_7sG;U48jo_#a}<#<12!PimPTm@*#@I}}NtSAYHC`UQUihg59-*EVD{NljK<9sc+A z6>C+*{nV;4y5OgNkVv_mwlDERtQd%}*dcaz3zN@g9~agq%<+bO-YvBPQt&G54NVOj zbkhMkxD>%Eb1D%hUtt~<3AGRf*A8Wg?6Sc1A08jcSuWI^i+FBh#M zDAziRUK5?c$x9R{e2`N?|H}XCj+05i%pcxMJVV0CmO3)%Tjl7Bd?bF~zEW!tZ+*9{ zFW|ebm2TJnRb7Kyo0sVEVx-{iM%6a(t^%w$@h%tJHu%JJ49$F#KH87ho4iw9dl7u} z-BQ|pElFkZ`?uLK9irX+OJrKN#Z^9?vd4u3%g6?2S;JLz!2$1v!s%YKn_B3$)~Yhg zs)0xzHR)9JyJqrg|A2f1E$2Etu$ScZs_YDy+GR>2e~(!&BzDsZAi;_1^edgJTen81 zA!n8fR9#nE4M@1C#C{sl+L5%UOAcRe)@sH-cKN7jTDjph4#1d;YN@e(0zpO{dKn(f zQays$PK-qtT-2J?*tQ$*5aHfo>z!d&Bq@9{Ol-Sb^V5N^lY*0@T!CgH zs^Ey2REE(PJ9qg^jHBUIqj26`948ha{fe|~SR2nwT-cljxzt112B0&-n zWz+4F2zO$@x-y?b*_OB6%7;$MrN>hYXeY`(*C?B;#Pwtfi3lsxS)q%mZmwRU6rbYiJ@H-|gZr_9+@9rvdX`Y9M|{Axu~)Sbl( zessLDwuTj$1^j*XllZLRG>{!$48u28Ya8}*SaU+E*%1~=^*u;Oyt|usX;?V_=4Quh zS1{wiYp&mh-xAZo1U=18BE)Q4auYhLb1gyX%XrLtyxcZ#{F!)PS*$x!wpc>#n_;g; zNq`Xk)3m)9L<^A&X_Jgn)#+Kdq5P&i5UCF91)Y5TrnRC5v}XkAb+R9vDLwL|f*sTV z{c<#W(>(eTx!!ARai%s%^7t8C?#`9>S73)sz$E&Bh?b)1bl+&6pxNlUVcxlqh{DYw zqxfa=U+D%hOgvd?(x7A#du&(3@wv6uf5zvhEp0(XRaLPa&|%44ZaNl!#)Q*ba(Ow& zGy_8pxG&|`c@Dpnqa6eCr#D;Mg@eu1tf<^(Gdi)##(C?9;to_UJpGiaRSHpoUqnU% z?b&38Q6t%G$|%bjf`YV)NF@=$)GZa*A=UJ>;kn)cf#3CDej-iu^`U^cfe~8 zW@4y-6a6M6P3cws?ncS345#Xz$4=mxBc;L?c%YnGW5c+28lJ9uNnNu;CdN3|&?F6a znBPFiy-^gWxG&LkS5ifnSy43krn&s)n{G+5&e?4jg44alm|&bAC_jP?J-lNVvke(o z?zT`t2c6CmwV@-az?B2jaFhQeP>Fd#b9;vR)E~&)M`FAgnJ{4=#pSc#nwG&7!|Uv1Rd=tuQnn~ViovqN+DE`?#TKI5i8gD3YT{0zo`=|2h9ID2%BF~1yd zIMwE6Ap^-DYSppgxj=Si+D!=@tOpyo?;HtI2oy>n%Kpi+Ni{QY)6O|fNF&{q=n)X? z3aKA5$V@&2jlA1SzTHTk10lJ{xp$2{^cXq%$);QvtwTq@dU(p}qP;~sW$IXB>%l1d%&4r059@jdCEqrNc=M3WZ7 zHz)jO`Pe3Qq@^!`h3!5ehX6g4p^|GI?sPF!k@H$&rr55iE$V z^Ism%FW2RZ7X=Dl^dIzneMw0c7DO8HQ=eq(3lwTfE=*eSxB1Mw_3<#KU#Q?dP+^j- z_VuTk;RCCwWkZy&wUu=!-U+k4<*qL-FM0{swQgsdQ46hEbTJC@pqv2xdjq_19KIg+ zeF*`vxutJWjM2}mI+_}**SV0fN(0v#+d7|bt`!q6S|E=)a3ZwddcQdg_VwB2ab8%9 zyd6p}XZJs^L5?-`w)IGzPnKaO0Dh*RlKSuMMHf8Bxu&JFf&Kwv`?PAnCP%O*c$Hg4 zZJs{Q=I{S!0TfJ1!#oHgyGtRmXpM?XN?vg|DY6}qJ+st3K7+XTmz zD(qluK`7Uh#&2ewwn|Fr#1&^+;V;b4#a4lHvNU&BJ{T6#mo_b|8-E?B0TKA_S39AEY>eRjyGDOx@Rw4Y_&~;1=PpZAT zdW$n1YQ#2QS5OOe(LEn0uaT|{{wMTszhPk7S*Ej%r}vp`$y~L=EAd6$(@MEsWGxP4 zT3LLK6g|(8g!d^6+qNCAM~=7$by!G9mLH*A_^X(w&sNQDnlsD?1MANv&IBzlE79WX zm3AJJaG@7iy)b`KiziclWzAovCPrS9W@Jc+S)NU!LKQGLY2{bZE`J#F& zAel3LH@ceOh+cS)zn);$}KVPvcNc-S^ob zk|HW+WtUnN4XA{BOf&%ejvG-(olBynA+z8duYVk!Dz65!Rm(A$D@1&2YLXo&g>drK zfZVxQr1HS1?|}CR7v6Bak)XVIsX0DcgH2|ksf@oh+s|gUy63(u69O| z_KZ`+)}=mLcl2=~GuLzzWS+6^6ah=#HlLsP<`6x`-OT@ZFD}^F4CVa|j8Z|dc#G|2 zkvr`Z$zCq`B!);}^yT(!gKx!J8w&6$iL$=QhWJ&__0o_lGsEwNA}`jD#!Qls=h4wu zQUbrUBmu16joS$c4S@gZ8fG8O;^BdJYER#;rdS8v2LCD`)e>JW>>`r8y}%Gh%gbD} zBJ8i5zPvU%5v?>BTa6I-G|+P5t&a{Aw-5S)w`0wGNZhd9X zmK7Xnl<9UhENIHX5b1Q2Dw1u{)8%!lvpMEBJGr9uOGwYbOM?Oj~|- z*IJc&{oW2T@MOMchMkW-H(lEuX|S0+W*H_g^rs?6-DAvq=aPo;$9r%4GlkDd==viP z=FQ_&n&LakrvDm??k;^5<}j0Z0actuR;y*#o}xR(YxicQuQPGzU!^30ri=naUb;rC zau{*82|;KY)Y_fnUJ#z>c5?jVj-;Nj-hOJ<=~T{dH@x{E9#j7M2R6J21+*3fUEW=w zP#dEf@uSKt>+1fj+Y1<{_o_iKNxm(=BAa$Wj^&}_wS|K4J3n^4+zE*|09iWZ5J_;2pnpt4Kq?IaQjPc(8>_# zVg_r=rS+IC<0Go(%kkUDd`={>mgvF2Xts2OA^CeOda{KiaI^1phf955PemLqrq#eJ z{nX{tcx*EE9;lI$5s{7WkQntYupJ-!^1U>K?4~>JgG6ehpPF~H#fO=;rw5uB&Ut$c z^$#MkvW|u{DGG#+Hvp5b6R$|0$8PM4EqLlHthd$kDx8U=T%!c9uCYlglg@pZ@L(Bb zW-J;cD9n&6(m&kI=c&LNU40nZ44zNKG^~hx6PiI;Hjp%HGGIIDW zzsf(K3uB#}sQ#dH0w)qjFIjSmX0P$LJAe_?f6bO!c8IUhA9wQn)ZpPvEg2~;6+mK= zhi;>#t)ESpdVqBjDG^y4V(|-^Dxc8tnS+r6LA#eurLZ)@Fg|rnj4d6jiuQ^+;M`n9{yLu)qf1!=RBNDC*5<7;@yr;DXNY{3=Rc%(ELd!_A zgVjy0%(*3~#yb7}i{iT=>GOY%?dL=%YORN}mUzR`QNZH?i~;?oY&No5X_c6Ah)u|a zZ)*atiF0KFFkWh}U#t3)D#%3&ph2$HJR{7<29k1UsWU3$YByYp-_~4 zx%KuaZ{9xB7GUyP2XiUMm5IFWD?HCe^s^++SNYp*?;Y&8VJ7WR(Zy&$>R~U*VgYya zjjPOJ4IfK0$vf}uU8}k504fk+i5Gu_F+~ln#G3l?mSCF;kC}Vd>oX9S`gLyimeC7) zovU?n98M#d1?aa&qvIZ4@-;`|*IW7E;ybftO|F%h?0^z`cUC)Y&Dlwb{)Hh4cXxJ^hyr$wzvYY*0n;k%D>AEwlaIe3;0 zKC*lS42O9nk_e1liqFv^R9Bz(QdLElF}}7=7EX#iJWQ4D9t%-8k`KZG8{u-*S_~JXH4V!-*{{6XkOdC27 z7wfeFJ}gYW$h|C9P@jWzZ4@pF6ez}KylVVrP#TDjd76e_W~)kIy@s1(55PT|&Ql(x z-VxRtDY&DyNy6|IIw8-2RqsP9n%!KUFCJ%vNmo64J>sUKK%0nBeyL!E8xe z-#^+==-j94({0()fC|d%Cg4BU3Q>iip*iYBy&FW#7h7`6sDDB^lJO2qmw*2(VX6r!0f-Q10m_ zkcRbZ#j1J8zdyC~`gL|>+TAJ8v8Od?>fv1L`rTFNCQRL1G!O?%<#2v z0*}G<_au%@(e>5cc;mAN126iTlS^d{QQL0^OLo@?%Q1cd+j{Q6Rf~2SUOq-VRE_>t zC~ywtlF&z59ePZPskHfu-^Xk&p`Ce6Q#3W6EpjfJ4?Jeu;PiTR!JZENS6CiF3_uzc zIp^QyDm?yNt4GoFWzVi{KXC!C)(htqs=@eR?(6zEV>(w{6<}k07q%^srszKW%l3jL zbUBsFtm)m0-Bg(s1mF%S~JqqgMbf-6aS@gKIJ4Zh%tQ%Zd~{rJ7jn97dqNatd0O>zUn2t@ZU*t`iA`s+P$bMaq? zTqoJ$)uj6FT&)C}!^%-r{xe%k=4=9-AifeMMgGU>^3jvDa;yT;$rbbs);5*`u6d`2 zM+mKQ>QzjgoybZZ#DH0bljl?3_wRe^EzQ=|;-rB0*UaLYvC9=dPFeNtvud|LcdEpH z^q6%wPxD;5OpXF*@9)GUpw3!7jY>*S&C`AYH&;y>zy6JQDDNqnfo-F`dj70Cck!l% z-WOH+dV&+CClKV4o5KffZ_pxykK=6vZhwRlUW?p4;CU|cuXnVWn+6wEjn1KG7~3I1 zVAIB0)D%YgTMvsKFj3;8v2o%oV?%v=6dZGO$8{`=<^lR4D||&QsdqLn5pQa_W<9-h zF(}To&xIK(MQBUFCC}ATZ!)00`r-Ztrg78WM;pG68Pv{+(c<3sThWubYvjxIN^>|1 zB7P=yZ^vT8eiDVigj2ZgM1|DCzh(Pp3Y@9@>ex@Sq9stUFVYh@^?f3E?!jO{7nL^A;=q~PMN=1#o0;7Jr)(SqW{?1Wpo<(RPdd9jb}F3iQfOnEXwWJ?7ug)jY6)$<9C^qRBAq zHt4C$iR|nbEftkMi7hnO)xqA+<_=A>N{i8ml28w3iBK*DJCD@mBf4iL78G;WblJC6 zbJ$eQI&L*t=bAVgT1|%Ss--!JRyeOe5^0Tu+`T>F=2@<@B6KGx`M9yeebSKq6Z+@U zDy@W;564|=P~TDwL=(HzZk~VCfO3IdP^;d;HEMyxieaZ*HTW?VG~tW!-(Wum zXE|(yVKRYM{?=6ucFB!+*Z~B?fO6-mp4YBn9Prq>vr?578Yuusn)wo=6+}WN0M;d! zoW~E-X-0sn_cDkQBig0kPZaG<2=cLYNv!TXSl2adek2v3X~Scsp*8US79ZSf?^y>T znlFDI_tiYT(G-o2o5x_1&rSLZNkK+sEWzp)+iD?NcscKU(etY-C4@Hg?U6rQh*qV9 zlD5YeFMGuR-{O6tH34288)SzpMok>KzgG9U3W9en@J`MV6=K(VK6^nH*}{mpoP zuOY;x-lPWylHsYy44DH<``=icq;F9ozKqCRc&==>Qc&C~0fSNfXpBVX7(14v%$u;W z!!Oq3P#y`Amza;+{NaoL9r0lW^4z1UKG?!N`Gt;_f&wM?cn8PwIW#|qg!(8|r-|Yv z@Hps9=MQ=9`jN8c+nIQkrnptv$4|u4MV&kBcQH>YS%-(o@>WHsQ=v+y?<_U`?72ix z>KnQ#L~r>UzdN&?qa#eDl&opJn-U5};F@3FaF)}PGU9$3?9$R>DbdC5d3+rtixfhZ z@(2Si@E`Y%Y14nlp~sS9eDgiQZjgtxwRB#utw7dlucc||FgvC`cKV898cCt?4IPns zXNyUlx#9sWyRo+OUD^&6+Rlv8m7&ZH-{7EUGpJSP*ICu^Gy8Qnl8@&xg+U)f87bbw zt@!Z`F{=WT$pDuYiWuk2{GcGnBeZ^VCw~=CMAznz=?T)@RV*=hPJyBr+~=7X_(}B( zDd%5o*vQZqOImPGr58Z0!5-5=1D(kri~OWZv!bvGN%5N=`iWNGlZL?FXcDb2u57T}@LTBPHp_2& zvKBr%yNL{q75IkX^A4|AcCFKdqEkqp*nRuJncy?*-5|f{>t`vAVWHb`ROfFoGtHU5 zN0q6_Y}BuY?L2nkzOQDhdg%gLTgbc!K>=8{2U6vnw^A+=kb#TLa=`_qnNaUg$Q_I2 z7r39poCmX4>A5b2gR(WSm;d^;fn1P@=gi~j%s@B2{jGvNUyCz{3*E2QZOu7ug~)<< ztjh4|tmiX7Zjp<(^%SUqvTdBdj5t+`oKePB?82vKS|lU{P`ejrVGSpEVHYO+R(tT{ zsO4-dNzKoH78jzpL@0yp)QMm*43W;NuFm;=!6gB^>n^`5NrchK06xpPbVbjeu{Qm- z-84M_r?|k0-sn5evtRJAcY)u~*^jnmqBDaW>=Ty+HQj%_nYW$)#1s{G#~fi^C|Oht zI37h@W&an5AK=CY8w?RFZ~f&YY|+8A4b~mX?_eD-=kiF9V2#U|*W31Dpj__Qs|S=-9NJ6QDgg_xFMvAXNkmX8bgSfMo$;UiLC zE#Jb*Z-~82fsfTeAtN4#3{W5Xq= zQ?eCRHcc7-o*9Coue&SLNdXtg$nfu{Y0!d|2-;AKQ<%^GDA@QRvM`NFENT6qSU4h?!8)La^xyTgecYwwQN_vhpL3Hn z5anWxX67q0Tlp(sPwJX!LN4KpcqV6vh?8MBK_RJerk|Y6749i|JNC$OAJa{VVe)+p zTx|!WZk!$^&Yl!Zu@p%pgLLN#bJi#cn-*VZ-|TBCtGr+u8F)D@FmF^qu-fnO1a3=m z?jST<^SElmrq+POOySL*HtQ!PnN;1g?`LXv(ENouzfviNrfmk&8k3$|LfebfS#;`= zmqSIh@;{T*qe$#1wk}=mmt-#q{3}CSXeifcGV%BRrk*hk{0U%<<3%j)yzr~hlbT%^Mmc`cR+$q@N`XRk8 z5X823GjC(rx^Aqu*qu_%6wy$?=#FV@gUadsxwFpSeTAP;QE>R*`S+8Z_)$hRX*8_2U9NDMu< zS@C|b;TYoX=_6pG2z04XeE*Zg{F(93{v`Kr^Nu~tj|4dA2dDa-Zl2>Gz(mj{c? zRZstKR3=m|bEz_E^%z@djGqIzg4YzqG~kXU`}KAN13;?Jbf2@}F1sZb4fLKA{g%Wt zC@WG7U~%HT@|WeR$|V~{LZkXFxDva3-c`o`H@`Px#dv9+IXryud?VSs&s(eIVC@|s z&-rr;w+DR+<9@=T*+3=up&0p4rw@>%C5Z;6bDQFv75R&$5`VcT^OB2~b&M`dj7W-Y z!>vGR$}c&qBzn9Ag!dNWg>&dZLeHP6CPe=BmzZ{EMZOG1I20%Jt#Ow*Eh5s?dMP{( zyuEo^oFCZo$^=g9Ry)57OyP<03CZ$4fyw>O_GXE>r?Bqrz4TrAzc?F*iry!zqnl70 z^L{9Z)km+lEX+n0yDYWuY_0c4+Oa+9+yyQ$W5oE9QqoNj=kg6y*N!EU#!*<5#8(yO zh5!X-CK_Z+XodG@zo#PF2tc7v?&-u8ZcKLm39iny=@ysG(V&`=L1W`Dxw*F0&OQaF zoEg@Gl3}H?2S8%1YYWGG!uMjF{@pBn+QW7va-=th5}j72pVDBp8rnZyx{E|)q)A}! zl`=Zy$XspOva6b(LWE2rVxmK2V`V>QaF-f?xIAz1I(+z<<=l>#Z5g;1E1B?vbp{Hn zJK!jQhdzCS)k00WdW` z;9gqb^VpUtIHZY6_~|hle3|(@S~~r%T@}UCaJ2z>BPe8?&6LdD*L)0{`isY1Fh%fQ zinQ-mx%V;wL$gf*mj?XdY4BD_e(JedG8#LHspzZDR^4RN- zKZ)`#%p6HwyWT5XhAZ?iGPsFmA-kM%xD{+(m5AfzA0Db<8+;qya%Ulna5L0$&R{Xr zfnRyEdOL*KvCUKyDk%ARg)DnLuvT+Du55}S7i^{I?_vIQ3H&Gx;$V8P&IVvDw_>gO zzJ3_7j@}JVj+Bc1M?U-D3P_pW)UeKiYxd6s{6_ekst!lI=zue{7XKHpaN_rNpuuFp zSl6OZ<%3>YuUc7G-hoHCI?2LQr6`5)#&s*-qxkS8XC?m!I&HSwI}G@3wz92pp0sMp z)~n-fJE6`?O7xaDhQRUV;wCPIM4Z538b$`sTaP!!RP|mP4%x9-W4_tu1&W>HQQ~bN zypO{HIstr?DE7r<3v^TRox_8q|Eb8`0>8(l;8cQto$4aIBk+ z-j3j_Txa;>&&1|06fcW=B}1j;D@!}ke%LZYt)ITBtFmC^Q-VI$2}$aoCHMMX?NXlW zvg@L!)sxRRfG)E+J(jPun_6DpfBseT{y<_g+hcYPL*8FuU{B2AxUXFF3iH zIv9Tbuj1!eCRId9Z(7?`ghIseyRKBOPcUgV#g^Yj{hGNENz>iBwN|Mq)M6HmVGu}d z6j-uXkOP^qNe|}{WgnKgn76D-+9yuGmLtlX#Y;f@+BzJ$Ue60od~Rfxsw}VOD0?zM z6Y7KB)tL2OzI#||-9=EZ{NQzmvy9Ds-ZO2LrqOYK#I`+a@5KzFF}6pAW(O0q^|DmV z_hM=xyri&h0;NR{V!o4+G@mj&Y~xpE|9a+Tg0$v)q?dU)ew5$Nt(VbQH`VhDq{DD* z)GjwZLy)KE5TlrAyY2CD8MwES>VX=0w?V+U3tTCfy{i`+KG9V&hpUjGhuUQ1vZ!jH z{+)%a(k32DRG=5y98j(=yO^=hRY=T*t;G7V4SyX-T#Q+^j1RxxN;IV?0aIsfzC97- zrgym)JXHbmb#D%d|c*0=3yc=r3iO}Wk&3~Bjr#euJy1Flor!2vMFZF;?h5IkIt>MqyOnOJ+vjQuG zZX7FFEZBFtYpp$F@H3LJhUX0Q1mB7E(_>BaGufWa%t`)_qjPMGt81fh5jSYm*ha&~ zMq{I~-Pn_f+9XXHTa9how(W^+PMr5V?+?g_nVdQM-uGJTS_4r1&|mlJL%Qhc*%t5E zl$5QEDcwOAUGNQCeCtU7(T>gr>{*-)Aw%Ze;sobX_I(aty=)=R^O-WC{w)l-y|@z$ z+^SO=@RgU?@cYE%pxQHl;Mhp)bOF2LBfn!AR#lM>BE59w2i1XPwwFJ@=d9&PU`TCh zhTDF?dVPD>3-zo1!x-GOi7WVX!870+qZSh8g9caAzjDb{by^Mgso3ku%?a}D#LG5J z9_uxfpZ?^)y;7!r`jQSmTvtZ?V;W~XatJYzFa>E^4(0E|xWH7?g`dT8rxtMWlJ7Ve zIY-^3!Ewwc3C<19@ARfsgj~ap=#+w+YZ1E2NEJ;GNVp>=a|IT(t;^0?lABGvs8 zc?BZO8i}oFX69KF^`(gl&Zd)S_j4DCQhr%&@{yrh2!rkWKHYh^ETl}4VKzlx6w=d7 zHxa>x2P&|g!v>dTG9(&JgW1>woLWxn`ATeZHG;yM_4vNHwRuV_(?~m|eeSdlorG;| z3iM+X)|d#qhlKI_CYl^Fod^cadjrZ2mupDCH(ULY_ceB2C@#5Mae>Or*ZNoy zoKAJJZFeAnohArZuW7nO2sftsxY4NlsJcNCTcLBs6C=Rt1|Y1tN@%5z=j*%ANu-=% z6GIkcGQf(uv!4237YhJO7;LijUDQ=Sz55VyqBu| zglJ8%+g~+!_Kp{oHMUO6CiOM!+V_*pDOMCy@QT$!8G6gYD?^P%OK@i5FcX)nM{brm z3uK9G)_E=yjte6fqxSqM_~WGqdSJQ9eR}paGGpQEpuk7xa!nA8TcO<@?~wbLRnYva z$1*X%iEgaiOcs1HRFmDEDl|JJZrWhFoNV~4utvHuAYSmgIx@L|G#IqG;vy?P6M*R71{0w1M2dh84EE@!@ebCv7&mF#$pn5&0}r@^nV zR|ggumt0me|N7Yy9i2!C3Wh1}xJDj>l!BT9xV&1GH&JeeyiC2w z$iAEg>bL}Hq3WLait*8U!UPNwRioV6>|3htJb+;koEzia*BgL&xD8SC(vIKU-uMti zo>J(AWF2M)5~OxJBNMPT02Ruy85{8T71pSV{H> zKe*iJAJ~Ped6lnjAGG_{_tvrTwqDizSdfLQ4LCy>hFs~ewl#4_bp4L2+b(}ZFW(4< zsgsoqB6AD|hkl&KWf-FNuQig4S(?BnOs7`Ur4Jwsq>;yE9I4k<5Kv)D*xIu`Ka|N> zB3@_DtIzWkIGSAN*WJ2x(w*6l_XHTvQr+=;0A8pHuJ8SL&gvgI7YW+!+oRa@i_h*T z6;~1&$YK?To8jO7qH~|OUNaSVByL~cg4&@{z*mGB-N0&RQ)2gyfj{32H_`hP>9}-3 zTP4`8gmj%iIJ3PT;87CJoNU9p8W$SW>WOg#F1$wH|8y^|FUS_~lYPj{jI8lBvNd%; z_6EVmUM%$&(EWwC9_YTbu?{pCR7(Uvlg!g%RnvZK^dPw4IwduatidM^w$1!#k$`Hx7&VZ@YFb#_4O(&3i(@ zh@>hn7S^l=cNZ5z#Z;dePJSzm7`q9p4(+Ut6N+0PXM$%kt#^qa;IgrT*s?^x2jZ=V z;-uI$AEE#jcb#^kBdQN2z?;lJ4ngdUrmV??RVBqSyoCLI;XM}p$2WDtrWi~VE@Z#9 z*CbW5wcstim*0Yf@Xi%P81_;6T)|?%(5JBe zez{qwWCd~l$P{1@tEI{6C6MK#uvg-DS6;=e~BEaLz=S%Llt9vU7OX@N{Uf6W_ot)?W6BE@w|CrYwQK?>FFM!!Kr4h ziS999u}uNTYW3}3!t4An>Ctq8c6MLj2}}`xur}mxfB{6?-d6PP25Q-;ZPJwOo6*MT z(Y!ec0?jAA?&C#&m>&QEI1n$u*DKZeXGgju^T30$=8;7pyP*VPF9Wu|1Km-HbdiW6J+O@;~1U`Itlqv~UM2%t>f%7V>Q|xca zmu0^f?2}-mM%xHen=oX2Sci7#ISinYq=zi* zrpH?q@q_!=HUJRO1r3q~@iPZUDHWQiJ@dp?=e;`ds|&b~>@zZ7*%84o0hDsdLVy~3 z8R7nnn++npsuH^5POIV}wzqf8O(qkHKsTd_&aJMS1mm&gF>!bWjTL766ZC%k4(QX~ zJ&`RciQMzEEr2Oc0i$PgHa=nVr==p@ZAx<|*kdV!_qO$Ued1aF(N6JUZkv5Xnb~7G zP=VepC{@`r?F)p@>}d37~y3=wHz zg;gp-dX)G-{t;c}TO@IGG!fpg$RA70+Xt;Pyzi2SXR;vvzj-k}^5I?a4KK9=$a#+4 z@|FkdQL2oYe0k^V5vi~Ncs^dSr3vo#8^i zg|XjL8!O28@|yIAW#phi%{VsR^>;IcjUmb@4M>A;vw44{<|fzbG!0lND>1)nqV?cY zD%cFp=rrOtzYsm&r0BLe``%@zLcFM!u3gT@>>nSARv4NJ($cu?_Sm%yj>_GS{qbuw&6=@RP%aKn1{W=o}ZdTZtni3+mNrwQp zK@?@zAkRzja@`Lb{qcEYlaIWl+LuooEpFtza4*}AnsQ!FwF7ugW9I2kPH|Pa{CY9n zQCKf_L@F(bH!rWP=T6Z^Wc~IDta=Vzu)cZv8GXl|Q~&+4eZN)m^SY3e!TvECf%q6{ZxCZi3X8H8#^I<1b*}(_gMx$^3Qy^42CU+gu-tN{*517qk0UP5np{bzAWpAXZkz#3p2m{M5C$OQZ7_jdZwT4?EA|44}Y8vOTmPy`fj z&|}tHGm2Po{{O+{7WeN&cIUcSM@2|O-5NKyD#ek#ucf%!-5tE?jpbixACIvI-7#x; z7M!eqHsOv!x%uB5V*M~kf73|lvkuAcP^RXCpW2Qbz3vSB?UL*w>nSsG2`0K-3gybz`-H+fWH^5q4{xQ@518ttiMCdkXh8 z%P$=w4_Dnq&u!mVrAl-ta!xTYIPAMO`lq^k~0-{ZDR?y)C3Cy)B}Pd z&qUVbk}F9Hzv+r$CTA1TdWJKFfq12Q%8rZ_;lyotFF*nrm5H*xA?A z*v!7ay=uok$-n;7Orjb{>>;scyxt;7ny6o7n|i7e#9B`u*Fv7r1lzI7p0y0udz?@9 z?y@bq+ny5U(DQSX$&|*EXzdTbzgWZut$u%Aeg+USqre<*G_r{#%wC@Fd!3SS#xPoq z)EOO>sU_ykwNzyA{Z==)P5j%O_=_#g%2Q9Njs8EHirV8n?&JNtzc(Q_r`)3AA=?DD z8fU@bF9mB0oYVD4W)TDa-u#cpR!2!@2CxsD-~OYL)lBu3E3@sAq9R50if7kOX53`f?>F`rpH^q!RZ!tw1v%Gv%gnRHbZ~Y8^APepTOwx+drgF zh&ycFUZ6t%uA_Y;wo?4M%Rn(=6y0oU^~0%I)Ve($s|}DW!T_k%2%GuXV%?6&(30ek z(qUJ>XWR4_9h<6YfOP*1O*NAM+AH3RMxEt-C)sCQC&|>LaNO539 zjt}4#bH3Q2G#F2H`kYSq@vBBUsP*QI$s{a^QcVt6phvkhdmwZ_IE+^}kF1E;62t>0 z>ewQQjYYi`xJSr;8$xVZy9~0xV;VUmYS+rze975hk#W-u?yh)4)p`PhJBrfHn%i{b zDdtFP1P=ToY#RNhSlb%L1h}@yZqFJvGq}w$lxNb>j2bVJOf8a~L)*7doZFs*Nj*}D zkxFENQKb)UDS)M|SpkzKme8Gwbv5zvTi8W$$?WOTx-PG*rx6~{NuW2hEj4CApepxLovh%xv)hE-Gwb))`7&Sm-*gk@&mSce?Rba9I3Ol zD@=wb4w32N3t0RNX943qIdp2V=XmZ#z|#ldA~$Of(cS%Bq`U z19U)`xN3iky`euo5*%=t``FC2c8y)0mu|{3X{RP+HA1uy(U3|EF%<&nlj%N+BnCZ4 z$E-ExV~=P+k;Yxvap!xo^5yu{-i21Gw)EC6@nK0xUtF7ty!MVQdW}htBB|R$X>GuQ z2nF3VY16kAYOu%1entF@pk^YXxS1P2;-Kb**VZPqW0}4G z^SCqW9hiexE5G@J78JKU9OSnJ<`=mvmzWhJ9}Z^!^L6AqDE7rcZeO9}d6)T|%;(AS zX13wK$|sWFr-T%U@nM|Z$;Lv>M-mUGi^CPhpUTN=v&mdNq}0A$V$K3R3o^4$9?Ln zc|9VEuJWU5BG5=r#5J4kr-*L{=!JrvTLQh%ald4}N zB~A!4DB#VjOJxJ2WenmL2O2rOqgkm0rn&~lB2zk^_txApOVyV8vq=5xd5 zbjZNVQV~0pOR^PcbiO|tU5UbuoBq&WK0=8iPmg3V6hj2e{C!ke3nt~=mUH5aONWd_ zL}63Q&OInxrIwvHy8DEo(!a&r7)n3bbYiB=7eg^qqRu-+g}EvO^(-%U7%YUAn9Sab zZe1T2*a0@plC}R=R$w^djRE5v!Z5)fy?px+IzF_8f`<5rRIoq+;u zim6ympQ+_Hr1|ZkBVLNUS9g{1-wAmO>gU!>FENk()!!5_-tX8UOZ2QAi-0hN9VQ&DU`Z-1))-X27^y{syI{$_tJ*LlKN+Op zm-FL?kQ2UY1YT5s^01HWk7Hq zSyam6@Eq!S7?POZGSlRJeCHXfI&ppHnb=n3_t(vobAk4k#!>L&YnJh|JtL(<>1Rb(1vnG!`-b!6h&RG~)<1 zdx;)MD<|e&NIJlpt%g=QI&d&a5k(>*7e_&~jhea#k2KBmNLmBE*=WqcUsp4GqWiL-k!v6Y*;gnho$nmb)auwnhPxm-z__*blejzZM}+-1-;i`{QX(pZ*TR zN7a#)TNXj_;|Y6?kap6t)RTpqh$mYpEgqIx1S%pd2DHmxFclu_NFW4hnyxL$D18Q- zKqvTl^Km))f3*s3%)qTeNZ>47y&WlHaIF)nB>4J0XGLi83NXQAYa%AWb%#?mA;+X7 zeDkq3HvS?=ELy`y{4P-|6mSj3XNxbJF^y|}jl)1XD(~uCR-^2U{Q09>QNv;`T1r^N)T<6 zI8v&IS-M6ylegb`mMrm&ahR6q10gerAQuWU{_5MG>tmweKs&B!PbocP$8de{aNDqC zp3>2HJFi1Bf)Zzf$EL~62E?ufikBmG$uJpu4gjSj2|&XHoqGbVX_Bpg?)atE2!z|V z)KPU=BGcNJP#Z(O3=|s=R6cz?1P-77n30|MfjfsoMuutV+7hVUswTeQZY*k_K67%0 zx!hGaeGW?1{M&$}@x3d|k;fZsEk>}at{L-nrPyoNvPMREX_%}bygv-F8=JP}C3SZ{ zEL`?f<+o>3n8Y%_w|)iR%L}qN)U|vH17s$=bdr9w=Fa5b+*+C#z%EnpIir>5a>(6{ znF|8{;a2$Fv?mdybAr98<(=Q%_)shj&Lb@sPxV;@kr)P1Pguj3{)IrV)c6bSd<_o! zLih-QUDn0sG{I+1Hy5rulD1IEE}X|{pRu9=-A#5m*mE~ni}8d-*tS-S+WY(`;}w~EIF*(Jjdgg+5(|J0rGF2r*Uy^{b0>zE-VMw0eRo+#ga ze6CkC?S4xwRE|DT4JWDZz1+XntuFRMW8(=WP2!r4FC4HpU5AjpYRpU%n}ty{7jmP`SIaNh-w^WJRug|?#9KG8w^*Z~De#u5ugrY)X-_vcC`M>IQS4iEw%C7-y;y zD0qzO!gL>?4daWaz60o0II$r^yk(RR7?-I-ZYw@6nt!e%xCWTKVy?zVwaf*s$0^WV z3$rqLX{n_9BX&rse$4FNN{oVz-$}1kJJjG)?tM9h zdylLh=8F@@i*s(!EML<+L7eKV>aK0dZWUOaa5!?x>3X^J#(VYSZ!ksY5Q?I6?i{n$ z>uUrms&wHab3<#%TkW>q_OZaNod)zd22yEzRH#d=zha8lG-L+7bM^HFjDBs|&~)sh z^D1K=xl{sW8i7_p@0klmhgVTg&!-!Y>%HAH5m$S!@bmvHA$NMno)s z3Q36B7-n|^tLPKnmLol(y$b&nQ=*d;t`r*;&RN*NO6A|B%e=!DiS@G3>fPy9Sv#KA zN`-97dyIxT(!*%qzHrmw(8gWrtGh_ENsThv%jSVUnTq8-eAABmY3``d+(3!jSjwKN ze5m%8$jaUO6|IurVd9uFVeg#W(M_P%Xn=ncp{FU%{noHNH`Tg@D!pA|fU~#9+QW(h z%#=dCsV)@Xr5s4ihm!&(k0=R0l^wFQTPt0aDqTB4Mb#1Y5rW%n<4$CpV_ygMDSYxu zn2onKF0T4Ww&TAI^nT#!AFr8F-}4NfH8c5hJKQ$zDJU4KiAUurU~SlpuY0q(C%5fZ zd{C&4=MQdsu`kO=v8T_O=a;!_*|$=q(fIzy{>h2zX@ITOxlS1Q*pAU+H0hGou@!2> zD0tw=cSJB^BkpunZsc^0&!G8)0l848_Lr|)X+r1zj(01zkqAtrx~;7>Qynez?nn8l zSNdfpErdv}Bt2_W(qOTa^zySe)kx^vYA*2u3MCq`!1HPr5a19TkM|W%M8OmC@p2<) z8b4xzYFf=Cn-a=1o}d5S9V1X={hZnC16=#9b&CQAxus0ZV0a{BPT2T{qFw2(#fNPB zZ7wv^BlWEI4}XGB!#szR2??!CqH63C(tR>&TH|-i81cL+!jhc^6JXXUoG3XM0>j8&Fp0({!T|Zn}~C z8Rk9`1!EcKRmuameV+8|l^AX8-vdnQPlqj6PMD~spt?MTyLb^A-KL#2FQNg36R+?H z+d()p+a?pO7t<#vO4hiPLFM5n%`z|{^SMVYRQ$xoY^yi;kKTwD*VF6}W~^(Pc@OOq zdz?GGMmu*h@3%tW_SKawasf=@x3x(f0XFhdy>{{Z&&3M#f&OV(G0gY+k>4#5SVyDVLq(v2)(7B%aoeoOAf1YAY`1FA# zg-yTj6G#7$G~0Ce0ekSTSf;j--sp4W%{BOLz8Z8hNjz)D4~k9M`}a_^>gkVGA&;nIXU0BY*Cp_pKrWSJl^3_Pg={0mgy+yo4oNBO{UoWMW*>h_|0rM z?!21DSRZxtINk#IzkP(AkgQ@fN-cV~FGc@Lm%WSdwv1x}aGgWbXq#e#FY!*17v1*t z6BJxsO-NIy7@c`w_DXLa4A<0Q>{?^~wvegV>$Jt1j!_Bhf?TC&C!vMndgzYWZbdck z-tjX^i;Jl7?`bn(F`$GH>+i<08KeW&)PxP~E&Pnlo8Xnbv6Kda+~#ce!D@o&S&Jj0 zL{gNs>@u1wuSeOG%ZHorPKIGz?hglT19Q?5Up3fgM|cn`z~nsYlT{O_t+3wvnG|Ae zxwfi`yVJ@*4oZ4FDym)Q@y_03=fXdA(c#quu*)qR)NCDEA64tN(#;7!Os$L^t%DDa zULKx>X57tM6aQVvNMULJVHoIi`Nhrh14m^#UAfA;8~3bA({-ZRHqBP%an*0SK9Guq_?b4h$pEM#fQZfbcM4z z_u-};uAs9Ta0D4}pX41eVeC*dse5{1ACWt)wrwAep7maiOy#Xi0IOz@_(9QuEFM&< zt(BTbfyvvv{m4>dU^eZdc6+gvvuqSC8(8bl>7B)7$oldGCll5G8@NCHC6rrc6X?ZRRuY;XsW?+)>EJ=-J!KAhbV+UWdMqP&285eVq`pEwN>) z1rk{gl{{Fp%JEw)l5(a($<@anKFGDNX8JQNghQ5N(4$$q#I(KSTwjLIjv{eIU$V`j zR$v2|Su~vdhMec#a4LZFlk`_1>8%W?5wWpW6VYnR`Dr<9Vl#U*hosU?7w(CPu9!?+56WR$XF?R#TH}WMm5BI0)Lp0We%tVpu8A^m5j^V0IegPcfW#i#a>*f|N-Cwhm`x>?omtQV?!FiiOR_XoxR zse?BBo1J&Acv`#6Q-fqW#*9k_^3zDW2ktAb1@=SEcGZesJ06RS*a+o>9!J^931Y^u{y}R01hx^W8KXrmGb+~ft zfa-EIt#^*fyGJ?6(Jh;jsqN)%Wq*&ULFj=-3j=<4qy$1Y#CX5JcPz0FuN&{ZsHP4o z)f<+z9Z^RYpw-7`yzr#f;Gr{Ec1Ab7-Uh`p2wrg&{FXAyZ>gq5L^tS{`Ex%E4DgNb zyGs%&GxB7x|32aSzjJCYkA1GaRAY-{;PJV=0J8=f{0ZPN{g8Om#5>F&sUk#45JU0y zd&dLfv9C7)^D4o)b>>H0RH$#pL#=&uS$AQkT|{2g>$&eAkz8JpU0m zunuzd1eGC})OidB9--?OV8A!kWln6p>E2Tc+bhG#jD~a*CixtxW`J>!uD45Tu`XK_44vqeP!gPu9@389 znclV&o)(YCzP^1;pToI8E<3NXuCJKF#5Pl!NQrzhU-fbZp5vC=y%hU&4ZR7Di#qlx z(ihhQ5D7Poa!MwbLd#Jg2Tes&%;N-WnD?!SnEO~7AJnc`ABlIbmDb=O0?2k7AT zqS_^v)?r|RUH0YK$!@5pjaBHqiD7Z${4UtR>v6T?OIi-yh4|F`^AR+yXuH!+X}ACQ zc1!wYVmw`7rSXkp`()57tr`8%t~k=)Y^jMfXIM94r!m?=21FG|IbmTdRjvErhv_`v z5V6A@&$UB7`>~9j_u4oW&S2oReWEp0PG?_t=+zA)Q^1TQ;PF^kA@u>3W}H91WK{ba zSOnI^hDAF*d_T`M+yh)b&Q>&%X-~td)gu@^TrA5p7_zFDLz?x9TXThO^w0Dk5lKRf zSiK)=+K*Nb&IvQ6>%*$l=a13=zR_R{#P#&6&{l_J7JsBe@;ZthSdfT$E0IAfaCqrn zNM9(DP$0Ra^vG+9ojv)U7`dj7&` zPa$_mPMnH8zc`3lxJVR2ox)A7)!+CM>R`k6dGP+-)HBZDsz_?tF|Aoc5;j3!O?cm) zuQRAhH;sR=zI~ZCFq)%%g3;$cu_v(&^D2v66Z&l9qsi&@AB2TPpnrDltSQF7w}6Pl z(`hJ#vlNB(;mb&M#)?%zS_nvdWx8G%}78+ERC6LQ_OvcvaiZx z$16xe?H)`Jw@odNjj&!Nmx{UwNLviVEKG>5LGI8vhr`#0VTYqbcjJ5-yk{4owb8vG zuj6ucZ1d~oyxvxWAPY-((NFZlf06h#j*3ow%vW1v{h_PQiht@!&;p70PAYpe?19zd ziEMY%a&xs;!GCFZl@`p~ZBq0MGn>~w2?AR+Bdz-D@|Lq@l%CU>3k7X0L2vI!A1@rt zSLR>)lny>o1@e&J>8+?QwZBZ_@Ec^SDrC$bBgTfm)_N!2-QBr^@cMV@B@BwR1vNM$ zJclLsLp)f5bpzAevI^dYE|X7<<8%LpDl#<8*;b8eFRSWl-1;TP23AwwEPQhrs*>Ye z#+8NLbp4RQ5L~rs*nizDO*HX(Ik z^sKKc)(9OTpW6<+(0x~kX!(>v&6Gr)qDn5j$h4oacQ~(CWc77f!R#apJiyrA9e$nI zMXU@nm+w8Z>%4#Bi)f%Tmc4DJR|HOnmo1FKSlD2a!(oZ7Ag-PL!E;H30$fxwdQ;QP zen<7=3VyH_6G#ogiaQu_M>eYa96*DVknQ-YAL|~!@ktkSESM*IyZ-}YV1{?7*gLAP zT20&+6-u&Xda4AD8Kb{ z-3PUT^{VLxj)*8dt|imh5OI{!nuu+21gcbBMb==OMOM!vr(5v2W65*jit|!9*o7}; z;S~gQUyPc2or-!h@Vb|}kLN-Fq!g0YAEmCb3&wK}dr0di=Y*zWkHmySJYN&Sh&0pG zy#9K~kcbgu0cDxA>K{-8Yg7~k?k^g0!xVZO@TQGMjzG&e>FpT=n0o9qbporO5Fu;F zBY|Z*@=GzAho`{#P479P^uVR#beP!A539IMJsg`dh)*Va7h2V#SLI9e`D}zBn27R; z{P6^a--mCbsL*n@G!`animuE-IRmNrLhYzNsj+b;IFvoV;V?Lv4Xz%OtYu(kVn9?} zfTESnZnbgifbX^g70t1etO|V})4uni7j14DZHEuhLol^9RI<3m2z#49tjhP>g2-Z$ z6>tyO_GLHE_a@tQyZj>h%tr0t@11U2$!Sga zuu=pijdBB*C=F`v>)>-mjSWA4^Y&8v^pJTv_DQb;mr&y{XubklOf{lA$X8)rB>t!5x-II z&~9IGo)8EUGaVo`@C`lVMcrYuGj0wK24Uk;b>6WC&uK*8X4<2KI7RHEk4p~D^*Ur167p~M^mhagm z7QK0d@Ht3hZX9epQK{SqD5im9i%0kG*;@t(w1Qp}@@#@5#Wj|$yxU?yv>H5D?O`=B$lmYUtH{uyE%_HJ4O83AfJR2wYM9&{@&MHHIPfS4tJ;T1J#s?xWfaN?I^?Bbj(dVF|a2NukRt1B>r6p?nDJu@dWTp(KE5Q zW{-Q6{|VaX;$n#kX5iw0SIS_%IX(zm4H@c+{9xC}Rhw~IXviqRQWDe;evbdOqLrZsN3c##<*Ivhx=n_GHv7OA{*j!)lO0{aPEK_)AwE?j$o}q#; zU_sDvazAQ?SGbb=6C$G1t>EC9kYAq#YlIm0TixPrDVMOKCPhri$Nn=>e*VH&92@d~ z>wld*=}iEMzoV1A8mD*PP($%sX3S-mOw2c$x;t1?`75@^Xi4G+LD};l*`I=sQ<1M< zy16^VP3#abZMbz252Ol=pV_hluZN z6>y8!$DhdD*fwZ_C*o&40_Fdbco#^XZ|R!dCRBKr>PkyF*QwiO+f5j8fvq*lGFm)N z3v(ixqld1_?aXmfR`vBqCbM=-Z-F9mU*^;e(xbQay85j24nUwdy}H3N9Z$;C?Y17B zFvPDp8h0^|h|2Vr4D0L+-)t$Wed?htPW$Wp^y~Kqh8}j3jCPlL*AC(8-0ksU9gt`? zTrKQ>;Xq#G0=j1EOFSRftG%f8F&NMPQ{(ayd)S;9617?TSn7>7Bt@uECFT_j5q~=W zRTDYu?Hdb%5JVFm+vO*7*{8o;;-630iZ^-3i*$GsOxF->+Yp7P1iG+1sk~SSgfYx- zOxt5iEpp0X)OTg-4nSwPLoV%RDc0W*uDyX~ptfq5+TwT%g_aDgyYbg#1y3y5#PTso=|LE_k z(jCa8D9<9^KWTqD(}-Dk2J&{I6<`xMDaH8HP4=ypg}_Gp?|kj=$5%u^p>slQ<>0km zQj2Ousur+dRf*#C8@qGDf`I+goOFy+>`Xb09b3|4rsv8N7>%jJPeZOx5KX<^c|nKA zd6dluNs(`Rm11~YxC5;j6Yx`LU=;5wb*W45;+O6TAaw1EX)+N7Ues;1S#`auWl_>W z?{$odN<)W4vdBJ?iko`Jv&fWYXW|F#GOj(!plrBqCY~!!UTUO~&P|W31v$f>ky>!~(S&`q$2=%cOQH=^(>Tz#I7 zFj!JSZ6mD6{fEHJ2I)Qzk`5p|4%9DW)DjwiG0C|oEb*PfZUJX`o(J*2h9&I?u@rfK zc)H=`RTL8h=9VFb^f%YTH%v0TjGVKa%X}PyBqE(~(iY?Vude^mn7`Y+Q%8eBhKI_fS5rb>G_taj`0?^->YXvz79+|Ma;8A&C+q=wQCe0K*+dq> zL%FQmNw6jwCLV$6OtzymQ5%2m-_`<=^!rul=;<>MBighk%fdX{lL>#sGp%1xiyZRr zH^}qDk)vuLq;lqSeIA$DpzB+xH((^Z8P@F1OOIza*rk_*yp&q}*D{MLCq{>jWG4K2CsH6@COnOD&?yZ=?5t*av%{ zmQNq332QnxhxOc+cELwaYB&$r`w|F4pKanLBk(w{(2pG1K)y-MKQ8ji7Htlb6u3jJ z6f4$G!hgTG8fPPm{&X2^m1>%SPuMPgTdt>SD`~wgsL*J`GUq(Fb(y<*l6cz#DiA!< zKbNgh>E=2-SvP(w)Pss@Sxw%rW1qVHil-`)N*z1qaFkG7-BrbH^Zid-*EkcT z6QSr;M~mp!y4p}br{u)##E!+@Vzf>uiJwE+)|D&8Wyc;S3eokL^kHE8_S>4lr-N=` z&uh=_R=FIj(Q&Yaa-4NzP71FEjd4N=PJ@vYVUI|z> zd$(8Gq*yNR+vnu!uN(S?3O>H$-^?J9eBKM9L_u22ZKD`V>eA8*eT(wlhU$*linK%8 zv$iBs7lKinu!hbYoI5i83${CN-%j;M_hmsHN!w?-dw7K(Dk|w0MTr9aWf>moRU2L` zCm>-@bmc(Ut_XLgUYY3B3_uHAHxP>95e^KlVF*6GR~#gWTWh|8RfITvP&6>JYOXfa zsUsPSIf!MCTA3gvu#Hg~*cI+K_gw}W1<+0My17?Vt^~4AB3_?47vAE4kmfy>c6^-v z7~w~ao;(%x=&IZ&JAV0f*Qi-~gn4umr6~9?kt|*BiuW7d=q%;$a%ENQ`C8hU^`H2d zV{b>M&VD1rik`^n+r#hlKFMy}atxlye;jIv?x<5t^~Q%U+xl-XfBE@}E7Dd-aWG?J zf#LtwJQ$40kQlv?Ef9khI5EhI4-S6^R}CT!j^!JOW3wcRXWjK?*^!$lPh|BCYoRH0 z<_!CJY9LB5F7SNO12v3Yq)Dbkc5mp|R$U#AD~$})j7-PGi-?;UJV>_rbE9CW(p+^u zmp9B0QCR9U?stZN9B4EUh^?4}*LH-Wg#{ji&Fu(I=W=x9AaNuE5m%H`>g{s|`mc%t zu8Fo=2L(mFeXax= zm>Cw9H!2Z3*8iOgc0PHNG3BfxSaQc;f>vl>hu^<7AkogfRwS4tg>ZI7Q670QJ6*&s zI}xyifC{-F|@- zHK6Y)E*+M}fY``m(dyp?Z+Q4?hF<313yC1%PA#)idwbVv-?T+brFiD^TZN&P4Xt|6 z4C*ukrO&N`^mrWIPHDCr5RMbmMCxO-QWEi8Af=7O_o)EJ%?b}jU;H-|WlgD!XFgWn zNYo$RkMuZtH;PgJ90T5;e1M4EkS4=r(>&5w%%8`>G7lB7ZRGY3h38`NEIf7iVX@mu zy6AxqAUO~*K>;!`Kj7tMMyDs8KgvE7QxRVicU0Cwm@==`gFMgCbX*8!VO55dS_{|& zKky_>-(#3iCt&aTJ>Pb=lQ42tz3RuEnuNZI6Qu8hV_5IWJz58k`wS1PD2ZAZ*q5b+ zN+lsV4|Z7V;n|83Z2AowTqM6HBdQpm7O+L2t}K_dKWI-$sipB!z#)dJyTw^-7W|p*^^j%@zZE&%};9EwZ*zF zEP7B?r$9<KSmQNb7R2)zvGhZF?j^m9Lkr)2R0PESe8>SKb@F z!Z${->aDejm$XeY|9BS+4^gzc%-B2qac(=%iQUf@O{uNfQ#;&ojx6_8>rc+R8Oy5Y ze79{$7k(AO!dxzS60P`IJ%pGMXgh8n`!|2r9@l~!$~qnZH}f(&QUw$|I{frO{5x)u zJIpt3d}8yUzAN?-jf%`p6cgOqyb_3Y%a#2%YI=qzJv2EER^5k+CnyO3l~eim{a9-( zzIaFq$8kt~_SZGGGfE6ZuT^v;DzByMa2+nyaSs{6wlPhW;5B(JJR>`HKITS-oWO|1 ziSsM^*AXcv%J7k)=!Bjmh!P%0CkWDN5d+z9V_+`R6!kh54tUiPX})Y3lawsf z>F~q=eqjl{6}iLgP$qDCWuUAhr&s#iQb;aW1M(8ha6^`BjJ`{UWZVn#i@^+tm=*#& zT}(oVQTm9m)-3RM!XkEi>m}T3q~n$zKQ(FmcfZ38b%?sjI!3vaEgq3MN~`20`CrUy zSIj-Q<0A>!+U<#Le%-$n!@Xi2O&55&G`EBT?WqpL>5#EPC>=;f!ZFG-D^cwr`UuF^ z>Bkhjo=?AUly!*yTkl+CGV5L{7Ep#Gu$853)z5cT%d2m38g7+B2Dgn zXj?5&3+om|k9%F^CA@2uXsS%z3?x&;KZ352-CCY>1gqWPOkXOdx@`OB^AXK_eGNw5 zc+Ln%4&;0AIce^^&~DzS>UKCL*MWnLzHwYQ>@6xPPU}5#E#~mOXM80)C^2wVTJhN! zzUF@5;h7+6O+=@@82$%XemZQ%4Q{gxzSw&|D|(V3cfoEvm4~TE@Z$qFIt5S5xk19Q z4u6}${_|25Xe~lGP~tx};CJ0xC#)hL1a4}MnC17F_w!hhAE!7t-4J`5=EC*LYsC#v zN;7Vdcq8R>1KvlJ^{llB?4he;gv>>TSN-7f4 z0!lYXcXvohhjfF4baywRba!`md~4(J9jQ^Q??3vG;YtCoZZ#^?s zSU44uX;|NVou%&Zy*#d9?t{+F!sVK@hDYU;6`MQVP9A}7F`&7+b0w#9ud=TiTXqQaQ1zsOlrTByVv+g&r!?3deb~ zHJd(ZXs9#Wz_P0wMZV$k949Kcc--tWbqLeLd=kFOBMsN5cJ1Pg;F^ zGJ*X`kqISGS~^dn`0Wn6U$Bf25$NqYEOKkz+ud>6OKLJYxxJakaJ746`j{xZMpS9s z;_cTySqCTbVNxg^(Xjb;{G{|136p1B3-;$Mxv;Av4Xy1One`08<0B5)~V!2`=)W=XB7(cwx%NecQ9tr=7D4N{_shal|y2F&oQws_GQVkBIY(;(E*vP$V`;R4U zj5;OrNcS@oxD%(!mzES)wm*#w=0@LZc0|MwJ+}>d=A2)BYtaCF7xt-3&$t`NfZfvO zrAbInd;=>NHJ+*i7n+v(%FUytQ2VRa1HUG?-zR*#!foK& zyx_=(D`oKc6z~DN#Jq)+1|N!q;mUVM4HVYZbdR5hkVjL`4omqF+Z{D%QeO6}U2iDZ zJ^+qgjB4JVe&V*b*iU~x8Nu2$@5CP9^wkficCiH5NXBD1lJ^!(i3g}uym%{-R~~yK z!ng@-*?Y%{m0O;tuVsqk-*VB(7by3g-g+yX`dmcF&nQ#Wb-$_ZRvJ7Q6;I7Amdkt+ znAqbdpyVOm_W>lSrFVrP_1@E#(?u7NvF=EwF4(n$qt5c}!Sw`igz|Fdam7?ISiu38 z{FoGqT3jM(r~cAJC)@K>uaIn``(}0T>IO4icsFd)=1E`Kt91Ja+ z>Ew2;B{UHSkACOkmGin=i@k>Ik!_Cm;dUY=me?Dv&ou;VQYt=J$SDf8d(GGe(`w1c z@Rw)uRZo69p&PWywaLfqSMLm#J7im=cOeidmB*Mt8>%z^R^(^pg^7-hJ z3(ltWLKz#yuo+Km-mknRmoKareoNjSOxmDX#dWc`&9kvITyMs0+cs;9xpD&5x)$o( z$*Z_cy`MxQ?8itWT*V!=!M&YDm(~}r-$7}XS1=c^|LjUIDC3lK^%Ilnc0On5@Ni1) za{|;+qh=HuF-l$JNL6y(sJC1xG2Z0`)nXR8e)%4m58d_{I^r%LsRrM}yg0->v1B*d zN~u41r&2FS%b+; zIhh5H($;t7s3!`B%QmT;$~f2&+DX2D^}vaNpSD$lrutYJ!}N_Jm0ogJ99@tqfm-C4 z45}aTfK%~s)zxhQlG^9>%B0j}sw)__e$Khtz*81^ZtdLNAaRRGD#>pA@C*uBY!#$# z=$l=my=xAZW4>9A{FzzoQLYd8BsIWCVO;&qZ6CJVJ`}S{?_Hf}*f9w<~ zClp0BHb+a?r_a`Iq14P*_nh|y?>|K8e*S)&mnU+kN2=hGQ`n-|l(4VX;PJe`Y@$Si zD}Kvxr`geg#=`aLJB~v9wR`V%DNR>cA6W5EX_%FO{-pD+kanJ|U*)wf}H4IN~p;+^gNK6pyZAB8Pd2f`rHF61kY zyzSHrs8m&-Qw!p|<;A_QHX?by)0hT@3?>84Sd$i_r#`uwcoDz4J0rzLL5Ng=q8WyV`^W zI*T8lG1$;=`eEC4nZA{In3er}#1t&zylgkvDPqCXA1M+}(ZlpfA*5toT)@@K1-l?` z{TXmp)|CXfkJ?20(h+@jHlVr|XLrObI3oN^)L}bd=bDTPrj5+7C5J`wdQ{f=`YUsO z-(J|71xe1=&=>ij0b$;@%&U-)^vp^9_m11VjN974S3@O&s(XZkiitw0w`2T*X3?&n z_aaEs;E1?5JmPg84L3SIx*bkBS8`nyK~tfo%vL;EvLJtL6Q&@Fu?u|bgGA@t03Yg* zbhWAn&-P>)duHP+LEfl{8@-ywD}m6?FS}0()(6aJBCxUaR#Y@l+IixX{bT!274|NP znKB#nM$ZB~hs%p29f7SO4V{4}kGKgxFt0uG>CO9E9JyBzRdf^stiJC)aa)*MqP2Ib z)}8Gh!(Q63UNXi?wdT6DL|R%Jy{~l7;tiTEX#tXDC3b=h4$g?v4)nJw4EGLZ@BhP&_^$a%Cc90ODo4O1t&FJ^|=BEpUy-w`MmI=tjf~D z=bRJ2NF|#ocR)$A(3_l+RKK*~VCjwa<1g3h-}u+4UClY+wX@mdbxR<_n(k|d@SPs(hWA3Q1;Y8p+TY1 zX?I#oFoo}Pa&3;9m`xQ0nS&V@xHjNC>BHIe7v)ptnmQ=SE+V|Rnnwqqm(eVZry6d- zGUZ3`pCXtqlgDLG+0su%+MSY?#FjHd_Ywoi4u+7(lvK4_DP4m=fDO!PQ(W`(bB6A=(^Owl7e~r3hejSk~(!wRzXn z=oE&FGn6yBaJ0LZdnl*W1T$3ylCI%Zota8Q_z?=kme+Ne6wGTdG|{^~$IDeTMmb&hC9$-k#F+p&UHmXKmBtlcJwB~m#nYoNB@CyEL}7#&X=JzIXs{O1+A#s6Zn4b(!MR`^niHh_k{K*c&*WwE?z#{uX${BSc;>T zq8$Uo3Qs&$?WieKr!TdbE}C7KGU{L1Zu>?rDru5^k4&Gxj=-(gwM~Y}Uv`a6T&wrv z+drig)ENP;;z%{U&pNya+)ItsZ{?TeAS+{h2Cgm+=T7?ks3At}<{66xt{y`HE3h>| zvpJ!#M7m6U^g$p++(y9rg!$F6nB(eO*Pv4vl-_RXv>a~xnM3wk310rHt=5$?X`>T@ zhx=c4d~;k$hoZAt2YIkCMSP-@vY=p6XgXisOBpVa4|(@|Ri{HHB4QN##+A0g(sH~6 zamsV)9MxlA*u|F7JUp=!Gy5fM5b%Kt>qbW+k}@8|5z;cqCF&I9UGhBeb|2qt9_n?6 zT5mGwbt{EE-R=C2EI?P3@ujDVagm`)#lK0(|Ni5b?u@C89bKmA#m9)}Qy*>3cOYL1 zuu8)7W8CxyiEU20UpFap2_kloN0W#4gc1YC+Cz%8I`a1}286wD4tFx>^Tg#9PMlAD zD8>&CDGb+HcU3nSj)C#3Z!|0?e=#pgAWwDm`eD7RyMt=qsZUeohh$plmGLn)cg|g5 zSEu`^Y#lp+Zf6ep7P&O`D8$*F7I-zWxdyM5?kZf9uRP5bQ_Oz^(%w1(w)mA1us%LgU=|U%9=K)*%2uoO z>_WEKw5^v^XddqJ)Wa#^tc3kA1+6`s^)+Gs_L<{B-g~OC4vBbn^K@cE6=Q5x+2*gK znvrIQ&XTlU8MA&VUHzr6HM46uIjRo7a&hfHV${AeYDe}gj(HjxyEkzF-1BEbk%Fd% zFc&Q7G>Y&EA9UX*j4+Bb4d2T_hNNsM++}b$M$XtiE1~-`0vBK9p^HgB<0^_O!)E$m z^!{x?mFjAV-uYLRZpp)GR;N=$&r=cKF4k5#5z&XkgixA~l$g=g$R_LdW&+#n%D!Pd z?ZR)!wcMWR&ChbW7Js{t54L_}DbjBD#bf{4qF%}0V!qr6E2(ewhK1K0*jivq%4UIK z>;j7&rt)1IO7CfJgxQDrqdwvF2A*%$T=UXSjKv%tj>Hr_X9HyIpEB78g-XwE@m_X~ z5>f2ZQo=;;tRGq_n8>7FEo8hzilJrTJuFKyC_KV<@QU0I!^~c02o6_%z{JyhyTKFh zomh`_eqiO5DZYXD>Z{rWX{ipvj?(<`19y`s5JpEFyHIfI(jyKniYBDnp?mAjrrbfmi;Mh-QccYcB49Gg5QnO)GTEZ#OEMkpE1VPrk0wj<(X5_*<-AYVxznY7rbiip^lboVX2=Bgd>U~~JO31BOYu4B2lZ7A!t8Hv zDrKn$p*i0c#ov&c;LCxaTQcK~D|y)Z?Q=ra-COg70MYs`GFsWiJzM z8BsZJYdlb?VInu&VbmYP4?Z__V1B7Q0&WxCwesepYrC~lZ=|TY4dAcwKJ|Ii z=VLZqU)?=H{d^xu3(fWg?-r=u6=`Hs5ZUK2G1?feXn^%)1MW3hQ!#JtT2FbNcBS&opp{f%6+0=+<~4Gq;>>)L((RcYX3^$s#3K}%VSf~$gfJGOC*S}(C&O-CNOOLCw>zuE$$h~my3m5 zahf6RT3#~+0@%?sOd2lT9O?(>BGf%dzTs6|eiWU|<#5HQ`?&Zo{Nos68y;3I1 zX`7nnZS2Vq150C1!_~U@a87;QB}sN7C3EIgoqD=cDM(GB&P{T^9Ioa|qYd^n6O_f| z!}BuoMnd0r{%^>4Y&!kB5E>rWYF*`5h70pox6ZokC#s6!pNSu|3HNZ;3R<8^ziZZ> zer_(=(WhLofAL&1y~Gq$T;CT4Hs9p)NZ>1%S)D&>dDm%1{3*ZXl8qs_ZGgEfuwQLi z6g?;UQzIb0H+41CpC^h2J~#G=Z4+2KB3BX&t+PFtQJGhO~cN?)3zoi@|p0b(zTGO_d7jH?2#;bD&#wW__-!)^%n*H1=E zYFz_QC7ZI<8rAE%H(&99`CfIaxs6sga)uVnBADP*Hhgg8N-BUkF+K_p$Ka`-LKY?` z#rU$v>tMDXw0t6F&QIch>ZGSQX*s3#FzJJxanjk1IbvI#f||4SeJn(pP-yphOvL8N zdz||!U`!y>xlqt{?|tSo2+=t#s%#_BH97}aqKu>WF02uw_h%jeUpd- zp?d4s+$~=!v7_mt5(Fn>IIV1(sTRQ5myx!canOb$I%>z^1hX`>K+jL z-|GJ}4q$-RU|3K=ASlo}3=QO|Bp}cn=8wM{KExjcg8n5pP#}1S3km~t0Fws2xI`#1X%$3|3<++#QY;I{<}0F>HTjN zEC~FU`2vH)f!yhV^g&kt(hw7z(5f3B3jVFd=pj-(6%#tlQh&JD*!1{Pq?bVuvsq4g>;2 z0Z}0eu=7Fxhyk&y!>GeRydicsSU2FAka|IZ9lA3?d%X}35d0n(&|!cAK!}lmKje{+ z1R+m_*dhLmAgDb>z|;gG>`?$FJUrR!Sl3rq^nA>hiZ($EuspY*@dD_(ct9|q3gG^v z)j8-v^PFb1MF?lx9#R?fy#-a){&)r?4un<TbOP#*)z?SPn&hmwMDV7VcYK-Vx&03DL4U;lPN(7QLT zAka?}G2n+A661%0-o*f2zzE+tZeV^?2H^OSoHbz62L3-yJO3Zi8~-pd|1fEQc%K0} zC=et$UmQsH!2tQ=&;lk1RE+^)h1%Uu3M2)R12KbG0G|LHNL7MBl(2VMhD2bx^Z2U> zkdOczNJ1bG8Z4wOgBpMQc|qfUwB?DrzKsTORq`+VFn;4bq^v+q-YEz#e<&K*W}s0? ziNOFohX?8t1R?{;=OeTLwFk-usBl1t&`N@)CRREoR%&wgW;&KC40gsw;8a*>n&jW> z^N%$DHvX^SfKy1HL<o`M$};^cMo3 z&5B2ASPu>4H$81NgJXRnegWFdC|hyX_sFyP(*Jp1k8EdMeD(`QRO%9Wv6k5_JqAy>* z3i}?4i_y`-ksBHkRsvrcvf;euk|CD``zE3X?IYXwH%%cIM$NV7TyyD;kB7A$k=U+>d`#*k^&MU1kVBNn1I(+6N`1U5ZAe zC(O4^>V`DSHt@ic6i6fbOFW34@0gih#V3b{NJm+rb!hz{Ak z&2GlOZ0lp!AA|FNkaFOU0sO5rG>vp*t?Z3-EFtp(YOe^o2NMC`e4U@I{*CivL1_xy zW-y)Y2GpYnvUNWFH0;G`o%4e*#y(!r)M&*m?I$p|b5PjTPp8un;Da4KwfIKNr_BvY z)eDhP?8|2}xXmT)Epu@dHos-+$C<&WSy~GC8VjdX@_c%`wtcKCJ!M>lnyz%_I2cLc zzWTs#Asl|lFR|%ajt4lkv@xq^+1KC|jIzU%c5}Ir;i~NGNAxay#k~J07!>ql(7&7Z z0HS|Ve=!M4iT!f||2z)-8k-52e191gevkM&Q~!*5AHem$u~_)Ioa)V8Fcn0og#SD=Q!&x%VI; zi&oIKGcyQIs|ge-X#r5AVLS(PsK3C(?*RWFiti5C{fUX8fpj5%dPp#k1xz3n8^HPl zS;PDjvVI3D1W;oL5c+#6Kk(%3?HwwF6oe848oMoo0t0qv5KuY*MSp{a#DWH@5#rjr z!`ILt0E+`IOt>Ei8~h(}J@9IEr~cj3Q6Zs_rvoX1@1Fg`&Ug3ZA9nS-=l-y}{ekQM z|9P8`0?I1$BO@Rv0B`<@|DpcjhVbt$LWNS)jz3(!FW?cpa9h1lKxA{E?UQo;Tu70h2!LH{Kc zbRb=FSZ$y${4WiE54`?PtEVrZ%@P z+~6*2TjyaTbR>~In`C*3jC6!AUE*u*a^eJ)tgTxqX0gHvjZRIz7v=f8rG6>uG;d-| zJM-`*~q2+3q=(X#K#|}H{h&pNCND1{L9g3hHl$mVS zZ~cr8MfH~epZ*oc1Z_OJs0Vn&4)6*EFn<}byn>q`^Ow-B$1N~_4LV?kX4d(B^St*R zLov*l*eE>YW2&oZ5I$^h3zMnt#>rF31(DD%w#YfDu>2B^TGPpMHax~BVD=PNc6(vu znbvP?n{S*`kYv&K^uTnmh+(WP&94}-=zpH%eC>Ad7;D^tEQ zhBVE8EvATn1vx9w7Mp;cPXCFyMew!=F5q6rcfuurjuo- zhME@Jn1tErp=xqvTs*N5#}#luVQDvVxyZNZxHcMrsE|)OpX386^o3zLlNy{^bG7VU zAKOMCq6jfzEy6C#j1(CMJfWt<*L(AtAn*&FswR@!dHYxP8~9YQ_l`lVMPkZK+9QXf zc?Bbz4=G7=tdy{+h{@um==q&P@0E>-<*Jc?SrKw0sJrkJwJfU>kV&;=Pn!QUBVD0t zfij3T=J$|}e3Q7g`X(_sd-D=~?4wvLScliCjp6LEBF5#IL+Vr}f8-N{g74dCOcL6yzA468mEx=EB;mK9L?_RD`F&c``A}{y;|N3A{Ci z_+%kIUijn9+EqCC+Z^Ux*iWPT@hEx=XM0tymrUI&Y zm0xA3C6qPfV_*-q&-kXSuZxfqp9`Ifkd40NGGFp9;oqqU5tLVu%!WEQx1Zh?R8@#a zJD78uvhT`h9HrGd4Xi zTKzIo{T}i6-u`Dq9AIwxuYLblwABT~^}d_xFhHoV>XZ-!485lan3y2MJqW^rKrwh= zYQq4*>~)etOxQgdzz+(9w1)|pzbj}E@?AX0L%^Bwxb-3$p~I6SBU5a?imZUG$$#0v~&7?+u6op#M*-)CL-q74QdxSOH2L z(8GOOpp|5|m;?GhgRs3*|C(!nW&z;ZzX4$pp?#vQOqd;z7vz;k#yJRB2Gt0;!gwmA z>aQ}5$Jmf!6m4f`taup~kV*7bZuiNv6($XESznaT&{i_gVJ?+E8#zXPJ|fxn08wHM zHE1T(;{kJCY)^ng9ZKuNng?_QGRUVg#U%Oy49?(a#)M#;Ozw77_#xsgkr}z+FGDkw z7TK@F;GbBFJ#YyU;WjzE;KMf1#>7CFAIJa+5`1Zo5Lw>p4USqElhR#PZSh2Nie%=y znJ85Ldhfn7DMUG4=0fCrE(z}2Mw8>B6X+~c6bEs!)kyr^h zHn|^V8dq@M>he{n=cKWQ1e?HYI7tp)eRxpYM?V$aZt-pEbf!M_eaEO>YLyHVh8$`o zeLvIS)X*wl!%6<_dL$N(v%-zQm3Z!PN8Y`sHjw7}uSIV)dSMP3Ka+rRLz?*A@T{+4 zrURg`J0vDQX)?)<>8z%H>pr(BP-K0G7K)-2R1Fo_{COH9nYLU0bttq*jg9|}nxdmJ+U&LKTCK2=I*)A#Z%S!BuwG7JN@Fv? z_3U{YT$x;~l2&XBRUqP{@EEYxKFdrva){k|D|qzLD8{WyG{=X4AV;O(jX0}@uR9;w z?u+Z21Y}k>ln7A$2EE;+xLzz8p zSK+9lFX|hMRAFtoQqpo*ugYjr?Hv>-_@U%Oj#fpO8pPW9vD(sV^c9z+xzBXdK6oO% z=}lO!uU)}Jcj->e&JKGgzv9!KNu8UMnK!x&h3Vz@p)uifljiF^gV?X0ZA?iBU3rSI zi~HPY{?hTi&D_BUWJ0^IO&u9lH*usuC4%{$-_k_fQh_%U=f1tHE7ke+rx76`yLg57 ztJfHYA{n8q#*?>6V#H94BH@u&pdMnWYOs%Sv`AO#x!ufosZ1H6GR-4V7%g$s=)4=9JKqlD-qotL?j8JLvC}s3HQq9KW78C0zGQU?I z55w78=BV{C+p-5w_;K{>*cTBR%_Du8Q`(9~nL1%>ofSKF!)mz4*|Nwa0wP|77vT(4 zPfqMNa{a{29nIY`Cj{&5%p(5rz|6vUWTkmrDZI2}nVLj^8Qg3O7MgN@Q@lZ5TfB_> zt~xI%s+J)j*78}QLyJZJH$T3hmHV8evM7tQsDsb*4(+(ii@)YuCT_!AFOgUIF<_b6 zB9s>8zd!Aft9(7jjwUVkftr9c0g7z81fTL%dPlV<6u&NUu?h@q(6gS%{h2`eHsuFK zN~xmAtd?S(-(RT{m5Nvm7HUhjqNkf$U?#iDB2a`SqSYzFA;L6GEj~d;po!>=T8=>YJi& zK`K1PIu~@^E~6+9v*1I0Op3OLsO`4)_X96WG)S@noCh-Lt=Lys>F{{5^2=#KyZ(AU`4^vcy`;0{C*xvSAX6qdJ-Kxg+XToL(8~ zx7(;h(3iYNm|y7jIx^OLIVRQ;w!3@D-eDo1-wyk_#Yq@;1t7h=f*oe0`nrlo!zpH34f4hEzAX_oG`Z29$-Y_xiQ*}tgas|3kS7Y$RU6%{}?>~8D;-I&jMt>sQ&@V{(b8C zGd2Uj-T#2He;>I2j7a+vW&bja|BlgrufjiLs{r`$w+j7<$YBAva|}@GLd1@M`hi{3 zF)*ROA^KxNa2y0NgFrv9D+I$K+_ey2oR@$dvikZQ5)3TYLSWRd%e5SUI{*g^=paxY z21JVb?-T?T>fQzZK!p&r1p*~wK%NExYAF~H6b^CyP(TI%1rI_6`T6rtDpNX~l(TgscmdeKaRx5C=5y~6TZsde`+ z$lK{2eF$C`ST}cF7k_2 zwmvh9|#*;SCUOz#R3^FU@7*WFr^A30X+ zJXOy|J4b+af+C;t;%pJSs>(TN(|?O60w*Jr9`NujeSobEz2R zXB|)JXJSdw+o_Z~?jj#+=CyjcRY{s}F}pG6TF^`WazSDOyP?M) zTEJFAI*^KkzSpwv#FdEJB$9@i083;(5nHy?GFeu!!koHk26^BE9r{Dh_k2QI>J0Yx zTq<`HI5nsT^~A6lZmP$p7_N2gb(oAc<>hUHd~0%<4p0!s51;t&Fbj?%XHudMW8(@3#(UTnnrD$4-uOYv_F2- zkuf8~=xJMfACz6LHzSqgEhB_@iHj#kR+=4A|G6_cX)KC;CuoEamAP8x{fAY=XCG@{ zhanggu8c`(i1p*)&A!#cT{>U&+j5L%-F?1uVV#+ExgwIixxdE}p!F06OCQmdluNW+ z=k#{r-bE(flqY+=Dti71@sWGHhdyCK8e?bMpKrJSrvS9yhgx6?|3m#x0NU?W_%kkm zqy9TUBlr!V?IB^}{s3r@h031*4VVT%NB~lT?5E=d>=1zVLqPZM02;953#h!G8$0h@ z5Dfy#1nykFg0d70BEatsazW&yfbECF6l7A~{lQvxdpcn7@jpy>Htf+th;%5!G((zocN2NzDZszwphl?!BTlf_& zXHAU|CN@vzOQ$~!Z%_!e?~fgx$Hnwl-Iy7%b24QZ(pubC@g`vA*LfSD5_D@v`Q|b?Xi5;`s9Y`@;G|aCPxW2z&o*=V}^K80s-drX&%{yXt{W!GUXdVRMcUUZ;vu# zm|FRgeK349KA{uIpm2MBY1J6ak1Sie*7zc&vclf>Dg<7@WK1GN{yll<8c`<2BSWtI z*Un&lSmF>`uGmO69+CsKy$Js+v=2^jZQEX)Z&uGNrBUytO+G|IF7%*Krf6DVi-B!p z`fBoFQIjKZkde(HLLyspN85IpP%y&9`wx+uf443e5V;wBTLcNDw&E;E+;Rk!%(KS^ zdn{Wd!guN4M>wR9h3zqy4=iC)l?a=^$NfnKb9;Pe#W3sMCHawMJ@n3+YuN+Qd#MM_CA0U(aQ}JGMjjzS;w6A6nE^t`+umg-c4e zvnu$~TsRgU`5b#jMWy?VPdt3z5_uaS!nt@X_zUl$JcduQKt^vd#}-u46lx-u5dLm9 zgkuz{*e`Ez7}{lVZ!Ag1us)=k425tLd#PU5a`2CNkecmQq zBn!J44$Z!^NC}G~5fo-ti||q=f&`zVStnil2Q*{f* zc^Qztk@!|z!b~XYp)t%v!C^f`TeWD4qtvF_^L`v^%mEW1}4F zPvX3h`BbGOz)4Ovm@J)|IPQJ<=lOraF5bJ3ibw<=_)HPy$>z^sgFv%#i zr7TDq+cl}AjNPA@sl-(5EDI~PC^zX1V}({a?_O=z#2dhG&}(e4m+s=bvRy}%>dt}g z;vAg$B9s*M_2sUyf_6~9zf|L`5;?SqLk4956~R7^TtjSf^{!;62TwAd`>oPaO}TFj z+2y`^!#(8&#n>2{Q6a3{0$$~HB`03TGTy`8;(_Md!)=UA6TMqt()m|ez2yyM{eOXV zzslVn%UDg8`ex90yc!)Co~(>9k$)CWDN-?6VY>HF72ZX6Q)Znv|#mxjhXP z)_mEjrOadd@?r&D$hVV7sUv+(2r4R|CxByR;(@)OGnQc}_`Zdly5^Pzt)Nb)CP7sY z`}WCKr9j$?ckhEcU@N}FYmdvlGh%;Au(kGG>R9%Eo=36MCDo|~O^j_p#dYZd&chkSi z?XM8;SGgNR`u2z14FE|$cftJxdw0<9pCoT%|5fs41}xD31cHzwF@O*c^ye8Fzzv*{ z0Z9HhBXbKp0uclY1BmN@6?q8Qgupg>fFQet3ef*vApLuR^nbBHYA9BkRu42OETDP) zmBcJC__YZ%GwMs=JEVe3&LWv8UN6Hn+*_e?PC$u%NaWbmqR8r=k*-4C%so7Em*ej%4 zQX)2$dwVeb49hXPw6YD0S*W^O;l%J9s2d{Qi3H;ay1tk&oe1daJ;uw3b!a(h zPyHf>fl{x{+91;yy!LF@hpp#4aYy%n68-B0(=_GOJPw3Fj!XT;!j5<_ zzZf7l@CLRdY7jQXP17h+^kw1(Ad=t`^9K)ZRr=}b3EIT9-#BEcZY(pmXw5hkC`#1J zlA{%&8O=FF0T+XM8jlm&<&xy@@U?jv6`_d3yU;WjbMw+MRcQ1VJTVEA#cpO6hIuXO zoNt{+d_bU=LXE%6ib@im=Q(K8Sm$*qF{?eU>}d4%`IE6hXTjrB-BmR1g4M;QSrX;r z{o~>R+2cf>Dq$@K#>`^@?J`iLzGxN-yA7rb4V)3NjZxYL>Z`b;tc9*xo*V|0Yts5F zU|9G+Yj*j)wE>m!hx(t)F2C2`&$u8E9`LgNUsiR9AtNl%#Q&$e)X`zl(S9FQ*Jm&q zAlT(TFcJKlZ6|r5J&?V>FHT=wc`iQ|jW34m{q^Vw>PU5J?2Mtn?;0!4dUJUyJ|HLX z8H{g4KhAoZ$hBxuM~YC^@+m$`wBSI1j$)HoSS4fjndJBvc|Ug|0&a~Aj88|^^|dt5 zKDdea#mZzju9)eG7xM&#SdfOK4_vTA_dK>SKm(>JlK3-|yO7z>B9C>S0I~`OU%{5@x)nrpoM_ahA|^VkTJ~}G*9EI|B!f+ zcym&+10J~s(JOuTG@IupKj3^mVik-Zn*-mnAjsRJKX1d+;Of!kot{OBsAOm6ljb;C zrdBKUJ&n7MnK(xdI^-qI1RL+ORnc-vnvn|)65Ro6(OxN?;@E{N{IEDpAgrO;a+#~UJ) z+Y-ly>pm4L3Y>UNj3ricF9(;0%Lkv|;jt3DTtHV^K&@9|N_>b^z@7hqPS!Uwxg72ZQwA|EvyukA2V4)pq3_S3Y2|^)+;o>cz^HL1(wb+IJ zo`}KPH@=M95K%828nXAIzhIrW!C}_~?*X2G97)L2rS^-05IWeERWu&z;QnqAPcZ7c zYncXksj6V>|@MxkFh{`fQ$&E6PR*+b?l(6=5QHIjt}zTXd(Ji7(MT-x4tR5RZP3wCv*6HzG zi@KH=e$ggPs{RH4lZfvbnTmztB>rW%b?Q#2Ah`5bVZGs-l&Z;ut>#`d9E+`7WaiNB z6+(nKOv;8s?PXL~3Q2st2EnQ1fv5&;D4%@m$l5w^SS6y0S9w8S>Y?l}dl*esZ?9I4 zXJ?&`hvPv(@$?h(A`?;Q8lg?Z(Chf{% z(9OgyY*+BsU!HeVxZW%_)L%}d)b)RlK}rh4McKuwKT0gd#b28X?ms)G$V7UWDa0E9 z*Buhumf}i*gL19^k;Y;Ueyn`a5jkH@i+G~E>b<~|u&^;)Y0T=J)IWKk-T zU4g7k_yd<7b}&NmR54+!bg|_E9Tad4c`C3?vr)zpNscftRdPudP>7KnVWbhxX)~h; zche$;vc;BhGlrg=A%*z`ENS=YQJGLs*m1T;*r##eH&Y&dYH>iz;`Ms6Xmd%clEoG# zX0bD1??J#CDriGONuyF-h!aLhbz*6bPUc@rEnYpB)jLDB@!-m>*mK84qHuW!L(fw$ zMB}ySBuIKTud*fjl(}TVwip0fD%qL+vUF`H9Of@O)bW&3oIRLdMC%6?oo-noctR;i6zdNFW)`H{e>spT2P#UbI(5?i+JQgXU=$Ysg6z^%)U7x*y_25|B-G> zETm~P%DvU3yLOaKR_F^-ITY)QZ#tC&k9`$)X8k`e`@LcRs^Vpq~fFmT9 zVi#~yf?Z*+a$DRtn}kmE2|3+(Y?u1_54JCTvK?gwu0= zur8?gzGuHI5Lm3P-!V~n|6Mlo+abTcu|X-NpegKt^SJ2$(1~-S4d2X zPQO~?cV_NPG2aI%F7{+ZEoi;s2Tm=s6eaXkA%YZPQ*)f_OXFZlY|G%V($(DMXdr>F zWU)$hP)PIPl%w9I2ic)8S(6g&0dR>TgWK)C#a1`SgwOl&zM7Vu^#Nbd`O-KYuI;j( z@Ey<9AKcn(qe5sV0XqdAvNZgPr0f^zXGHAE<(>ViW+fpaD8=dicO4IZe@&{x`}wl= zBEk)kY)mu$Pd)k3!g0O#=;h%?p5(lF-IEh)$*yS4OP@}!N6}~8S6b|!6T3sN+gC^n z!&P4UHuRaIu{bJ6U*4Ym#?0%g>DT#_>p0zypx_y|S3g%P&>+H>ZENE{ZL{M=%Z*nF zoG8;OX~dAK5e;Q3b}>w|=`hjxXeNQ+`7GxOTtQsMR62GPIWcf?GF3aHtH%hoM@{Kb zk)|!Bn?Hyp*Y~BWrtTVqHwuj|9wXXY`og|`t~S!_g88)hv&^Pfn&>yw_mL7~rt%jW zV|l^N4#w>IwI0n6!%In4o}Fq{wkK~c+^7ccPS0n>#l>>5M#;z2#}&L^xIk|JF1_44 zytFgT$zO90<{LOK9wxAjo953QFh1#sRA*n4tClvDA}mimG2;$wmax6*K4o~qzV<9F z`RRt-yJMOh*e|iEv+ME`{Au4YCa%BItTjBEAW*bVe2%Q-X8*Ln@%A6k{?~<3wC^EF z<-iP141g5s-)8b3d#&yu$uD5!?o7E?@N3{K7OvN+hb^M1>0mWxDAly@w=Y95UQ18W z*mx2-)jdN)is(S>P zKHJ9A3l*bzO+_I?!@&H>0-ahkJpP-%+__smds?vk(!;b&{?3|juTHIu3h_7v1|R-s#;>rPL6N*pdsl# z7lFu-_ib>MI;%_tq2Hhh@Sz8E?7x$0{>+>M)&42{1zoj2bF2TjngbZm|AVgDpSiz( zTnGl(;(y`-f9DAQY54!FxAtdN>>n*n|936@#+d!NjXysG|7ar@*m?ZtU-N%>B>vEe zLj|*HzoU?Lvb2Rk;4&AKTE!<4KfrcF&*ts}3_}CpyZJnqn%&a*@sT@2wsVF>rfSiRr zkTbL9`c=mDs|?6l*aJCJb92Da2vFmHX8tGv&i`KWUv;?uDB=F24$mJYJb#q%{!zmF zM+x5_C45vAHV(E<_CSw*eNj|G4{Q&hUS< zDIHMj=D_$RS4yX_+Krorfb4|P$(RvXBC)HVe0uX;*;Od>LDbed5eBn@MZ9vPA?vV1 zud!E#?kde$W(e%}wEk3jV8#2{6svVghkbR2`G7S|+aVi!<`T(UT@8P`>v!%g$w`yZ zSPJ3MZ5}s?B%4V^l5*7*_8WYtl9Ru?`DTlvZ1X#&mSSZoz( z(s9!a-+M|I{qJKn-=pL)%z%kh3(C#H2I|5KfAIhEi&3R0N9`Y$V%+|eSd%`t#ifx(?#dvAu1AP-4t1(7RO6B?wI9 zPT+Q4j`O&`nRN9iP~sjN3EO1C0v*1#(fgW%Ox~!fi=;rj_~~8jhv6{psgC^~@~TS9 zLSO&bk@aPY_8rfN?T!vYJ=wQ($n&Owv^0mN&h*+*m@Q?2lT3H0)xu4OKOV$r+|v?SLFSEvF8Y^;7kCub zRq@ry2ySSG9w<(=wpXvUv`+Lw0wgp9U$QZ0)Pefq+yZnOyScz5*y8^! zjbwU6SAGK))Y0{k0(O_%FH=pZh`ai%9jSKkOQKT)IIQ`F16hD=riY*&zwd0j*1O&T zk(ZS}^kxh6zNi%ssb)SkhIngg%V@l4;6E&BEAY`gjmM#_pUq&VYWl8htxcgo--mp! z_wcnK`SW0mB6#}e6SbTvO9AG7p6rTbs+^uVRZ-hEeX$U93Xhg%mf)vm%!1m(6CoiV z3?v_>N(j`I4a|(CI7m_JJR8)`f?L+(bPR&S&2p95+dBrbgb?GF=Syu^hNKS%Y|&u- z5hdglg|3Z|Y~Q9it;CpW(kW)7N}CzUxQFiMj4M=Ux_o_fQu>9I)H82x^nQJ7YX z(hC`QDiL^raK6aRy!y?1-H5r8PRylCU9^StgeKg=kT>{DH(9%!Mg-;cPMOtebvo~= zw00tOTG8q(UshRp;uB2tRn6o>Zm0!2KZojZ#az={WTbNZSbFKFmO5dZX6^O(qV!27 z;wg2|yBPM;bEi?-d3UHqUkl5U1#*afO;f@m_Y+T{hykB$^S!O2S#_$L=M=;7__uJQ zla~|9f)1rOcif3{kJ1mG2~X=sw@jhwCbbW;gT<`JLMuVf*2Z3iFRgCvVGVf8_oCjt zA}n)SObREDH$8jBT|>BmJ-9CZUla3x-Jbh1y#}iNQ~C?6#-B;pKd$~4YVm(23jet9 zDZn87OA_%1tp9TL(Fa^x39ylXdN}|}>VJdM-=OrbK?#<>A%YYDB^qE4^sj-E4gg9V z7_Hc|K_CzBLjfk2tUj+93tAjHgU|x2zL@28NE*n)Nr|J8= z#SUz{(tS(@VK#Kp5m!TL%oBN~?xQJING2_pRg`mKp$;L0&GY7)hv!&cLOvL!6NL=w zvP!eMuFMfK7pm~| zyDh&rlH>jHw24i>(9=(TidArG{Ch)dZY_p=M!g+;hDVZ|ef-bfdf|P{iC5z925;*q zz$}p+6p9ljTv_x-EHUGK$>unOFhmQVM&Lf@R=!egwR?h+=tjNEAI60G)FT3dj$Fj5 zTA4Yl=%@i<8(`fjV67HDPjx!PEJ4K5vAWdKBOwVgUOVJhq>D404>K{5zXlV{wIJAbwEqhpa<^+T;w>gaYIbQk8Ja0m2z~`YfIm2PL^u`FgW&*_L zbX|Jm$B+J&pM}h6c%#;|pHX6L!1;Yi)qxcL*j%?BN8vt0w9;aYHa`uXPMm$cz2>T< z^65>$sGLg`nNfK!w#4Y$iIlIf%j%l>Q`;-o;cE8YeI*rWuMt)R54a^Zga)((E8abv z@+U5dOTY4?AWC`l>Rq4UyB49>zKa=clKb|^X-7q8$x1s0c}HF!a6f0UPH=xMT;;2j z2L*Dv5*{9kJpe4-e<#BJzr&S3&+A{X@O%2d!WGxwa3vAI6*Hi#e+xT*3p@Wp*zvnb zqGJbGkh3R$6<2f}ofr5(4`L>dlA|+{4L{A4`SviLJ2=9Q^Oe+*OH622L{K5dTx8?j zA2(B%>Z&aa4QS?~td6dq;`m-JE=hmA|EccoJfdx&$oZp-UwXwnv|p9>$m)UolWgL( zQTgjx5F29FHFm|%pw_GF;!i{MTUqvJ6yEdj7-MREgDZARD#&4JPI{{I{I#ZVP^D74 zytVQsVh6ly^lE#t=*X_qT)NbgB|pz$KfSc4mgtJ!ytwnB4BuWy-hWysvlXs~lGMx9 z;F}EYO|b4pA*n5BpI@x{e^WVJliV?G@7s-W7Ll~`anYICtb8(sS5Atzmq_ApRgta=B4UTGnI*-A2DAi!VvqVk!M@Ic(&3U+xM4{q zpEDM_3hQEixsnYRAj$cP&1f1GA={00H*g;n6{ov>zCFZtn=X!y&93poR$3*G_3*G- zrh@kc#8jCew3yKPlxB#6p0c2iA%aBcjhD#ldaPPlLnVPl>(?*_LuIMML?EaLmP&}y%32r=Y$@7z8V zE50pU$*ELF3uMq&*AQ~cc{Z={uIHmUD~;}xH)?vqSQ99v7P?s$wjz!A);0K<>5+aE zQ~AfI%wxlMitlJ(iiQZ1Y1@i#xc8I6L+rG@O|RDoM$4hXtOH65a3=2X#W(d{9+!4` zm8xryZX_bbsM_V$C@0&!ldlD_iVzgd0m&}~7sDN~Tv-qA-2CGytg`&c$WP~UHw%+_#Nx53ftF!G}9Ed3`W+lMCzAPth$D-=Rmu)~S$uTv9bCYkS~ zE<;JFV&DhkgQFuWU%^u33A8I>yNDMISouyU07$p>%W^{yrzus4o&3aU#%1CYPqK%yqF1T!7e=S(&f@A?5yW0JGuFrmkr%K z{WY?sBI4)As;SOue9?Jm&kplA1f=~yaXF8#4z96}A$H3l6 zD@n`fTw6ZT7Ua>Brr&#tLufWphhbvoE{)gmJm712x&8xcC)(YNb4@SZA;fWVaN7xr z&hU)kK*tl?|EZg$T$}d@o)NqQCta>ZwyE@xQbjz4Gx^*xbU(8`F6_$FonNW{NpIvw zIPnM;rs2bHUwp@vj2ursqCPRgBfOgVrbzDK93pWE(FH_g!?`OnZ`PM6UML&iB4t}` z?lfbA?PsjM*P+TCAUNizj^fj|_sJjY=X7lcJs|?=`HSzMVh*W0b$=5U|Im-}!?I?u zlS<)i3nc4Gv}u}&ls>=`A3G3GOE~=XYk#{FdQ13|TH2OywAZ23T#_C3iKRLMx*04I z9oBVgbE+l9Nz{pol~rI@Yc^SWF2UhW7lgfTxn(La>+8|p5^t`##A;(!;{rq#+y-Zl zebw<RG;}3W}6Tr%t-jJq4)xb0f?+FXjf?Zt9U0*>)RPufpc2z zLDO)RKZtLn%>sT%T@D+rwFnz!5Wlo^b}72)FvL3cgkFBEb8@G{IUHf&=q5KB9Lg&9 zF1W7dzYR9d}dc~#zYMf{(}Y5f`L{@}9!C8qxy2|f%E#9&~8 z0Zt1FsBrwvY5mP<{R>VD(G^ap4$y_$0W={0ngsvCFkxN*FVNWbF)Z~GDt;=;XCO)yOOxELM zhEK$x=mvdaTNtixke=076VBDBOGu}=U_D3^y{lW)Q|P$DC;L22dLUw~jcBKUiEUMZ zQ$L3i={B94$D)v)Tta zgX?~bHB4I-_Ke%_^`odPM?iMCq zK0n^s@~WO2uEnb`Q;<_;-E70PQY1Ethp_8K7I%pJIK+@^JXQ(x7rMk1?lKbT)!^$g zy3!f;>f+hfrrj|#4TnauCmEy*T9X>Q&dQ#mhCHF>Q-8gGshOTG1t`To?Q!8_q$HB1nddr; zT_;T)BYlH?K@Y8x#u!=gqjm=0m0D>}!75{KO}Y#xiIPGoxr|G6eBB`}**7B#$_#gY zk8Cp6cgjPHMP~e#Jw|j;*fzKn*bzBIEnbifXG-T+Qd6FB=aI`nFzG4FTlX&NB(tdc{qnY1(S?4esi~wOT zxKd34hQ0u@+2L+iD?L2bMUw~apZfT#)N&29vmBP|74q;$3>Yga6CWnEbd0q?XV{$@rd0TpdDs$> zidDpM0_`x@ZZzA3B1-FgNnM0js7X656lGz2yrWK8_Usqu50{3C zWS=1K)Z-%%3>q}N=RRW6o^LUp)T3YPcBePBK%TR(yRAu|1D0^=?nh4pw;}thzIWX3 zb-dwf@1(LY5j|DQQV}7PR}jsNJrs+TTqJZ)7|Yb;1hxhwLNr-tjgj}O8qTXdmBhh< z`)u<3NoB!%Y(10O6T|@{&Kjy$t$QK^MZwSYv9`v^_{dk=a;->bYaA4A_(O@`MZ!gn zJNlQkl}m1$TQ-j%3gJjcoh9o&Uzssl4orVoowJ{sXNu8Ey>M zj6^!ySk^~V7Q=M2T4gc5RKRkV{`wCuwErpV^?#3|ut^UYxoOg>}rM|sxKIinZWdLWEd(rzd*~1HP(1NJZ zk{dqmXSg_Tp^f+P0jb7DEMaj)K9R%ZPpGj8Xf0Zl7!@e<%IYHoen<-MrINAI2(268 zp%`yEzWMxN#5p`-{!}AjdnZtDT4}}KvwFTTAJq>2H8eqobbS+l*cH8mdJbdnD|I28 z`aCxLQF2{KBql8S2Q9Y-UPN~jR3Gsx1bD(OtcIfnzzudaCNat9sv#sj!}EEKuAW&! z+paO3H)yBN+H$fb8foVPP?J6cH-L*(J@3pEYX=rn~x* zvd4&@8hBvp7RC;m#@vEe^j9M~Mq(JXN&ru?7hb-5NV)rulQsVQ-~TtMPdc#i0|R3M zP@i~UefMwb^EdVR7t|;8dWFjXpgh3cv#L)YO=WU5dH3kb6ENA zUKfEoE{-;j<|SHVH!JtmtDn~sQkIeF!sGq1vcZ%pa%rDypIe&R*z^#Y$!T)JdefH@ zo2}{ObZI6V2QQs)kR=qGO526p2Hd|5@D-X$ainSGQJKY(;Z|drbS~4+fK?4dP_?y! z`DXoSkmUH(!`^;i&gcZ6n!K!PHSPqhsTkRaN|MNhgeDzDYVG8O94W6ko5IVlHlFIw z=B)#o#cZ|Hu|AG;n+A*4xasA&_2(L=5KX-8I8^NK^cBBQEwKi74`SoK5sOuyBgU*v}{81hU!1zk4FE z$flQUQ-w<6JhNK3s?_aG-i;lIF9J^u?w{#m{dlUnVlzLftxWyQOU4q{tBZ3GSd&aS z$A#(!8EsD-@&rbQHG3o>^@+rUio}zJdOY7uR?V#B-@WUpLBW7|)3w_S!&Wj;HB(L< z0>db*7(_%v!>|*67#=nnLRVa*toiiDzUb!j(KxecH!%=ST_dA)tJZ-uNPP8NEy>j! zyK!q!88b8yqkRoFtmn1C+&of(e3TkjGgGux^jRiV509Nmxv6h9Hgsw>lUti>`B|lX zlRX&Jf#gzR&j6=V{8hYZ4QT=rydAb*C39HShBxa!a6aFR=ivA3yM1&Q;R8we|4v=> z|J`2OpJ&CtNtLUB$puhjfGVp3Ye9cg<-e)&zo5##MPVO_008dcF*U~zXGl1V)^E67LJn$dlG!oz$dcVk>Pd*T5pvwb_BP2f=AtXKH8`R4x9 zfSc~tF7MKr4g^+C19Mlwc+yjl1f$HNBdxw^m6myRDLa-HvR3t(xFGZu?180Nd(kdN z8V~b%x-8w;Tn22AvN3>;a7# zfouGqg7!1*Sf)=1cVBQkT4WaMOQxStZ1%i7$%F36>I@^8*DBULNT*DkC95BbSBj&u zDqB7_=7`xC*AGpuz-4v}tx8@tW4*@Wcduz_Dv(x)Yy|JB*FjpO=!Gn@^p!KC`i^XC zKMRiW>B-RRV>iK4gx*SiT(soQ-`HJs5~{0!3eH&gX;aMTEJf5mnNxVg?-i)de@Do< z2=2y+#*yjfH*@gC@!9|x413QJ3wFtH=%ioT_Ofr1OnGF`hQ0_eGNuU-wD~^fit#-} zVSpp!r}NwGW=TAx$m7f+$pAc}4W9$zBGC*Ji?^tJ`Nv$biLeUhm*rNlUlc6v=lysU za}!hSKip~AB1WP0ck)Gq6&Yq)@LCqTeQe0?-^*?|%uFvEH@sB>91V8_z7ZYp7S)Ya z*Bq$9X~Zsnqe46vkE)U=E05pk*lSljUX6%kn>%3Iit5vpn))TeSe8#{u-s4I*}X^s zp|D6Ag_uoZE?bH7yWPfp1-PLtrqo0~$@=wku8*ve>?V5s*6I&s!)`@|EHidJ$Qi?Q z8Nn!*FjyTPk|m!lLdmw%r%vtrkI?kb%Aoo~n@{(-(^4P^ry) zOi2hEML!_ma-a29UY@BWPfba>NMH(?iN(5nKLy$C)K3tsX6^eqNef^bh;;^D7mZQr zxg_2$M5>Ygv|#<=8E=AUqs&y9T5BnbhQ{i=wZAYWKO2cBuf_L>OZ41WUWhK~`qL86 zv4*}x3p`CD`YjesA$FN>9MeaAZTqM_>O88Qu$UHeZPcl}BvPi@mj!7a?7CBD9EeZK zBN{GEL=Q>{KcF+$=<8Y|$JQWIo~t-=S9RIYK1bg_>S*fTGpy0725SEFs(w-z)Tg%j z+^nJcfvm#3SzL$fb)k$~kSYUjT3Gw~=aucLorIDfQO^?^nnkqSvU%MqcyPn@V(M#c z#94cJ7@?J2En1@F{^@$`9#tj37zo~f^Eom}yzA^f#cQ}>^wp0&2 zbHu>aDO_t_R42ffTSsK4@xa{)W}(Qj3HzWuM8F_k-r)r64G>R}_)6za>j8$1bZKhX zb2&~0tj@Nj{{ce#=~?0pWl8^)?a*u2pnrOj8O(m0 z8pA6!+hEAdcQ>oCEpFcR9dF+}W4(v3=}524c*POXr}U=OnXaz*MySh70@=%b%@r=G8W!Do9a&ZfG{a&iE1 z|FaZ${!G;WO{T)>w}mDNfT^hYTQT%+#n6AP7`oMLGye)$AD{r7*!(r7;<;hMgb*lD zeqCJGcckdrfygnIZvlalmE{Gicsq^LzCWpO?b3MY)p6W=pRpxFC0~j4kaS=vU(E`d zopE(DRb##*QIHop(t2XIPT5I3rrGx1pM1I@$6vvA&|naO-u+l6<{6SPJ!_cn@8}*B@MbKH=1M5s_{2+0Z^9%#xz#_0gaUseIwT833#m ztfYx+@rK%{Ut&`vaa2&gYRGSOv$?0WX{A9ksknTU9xo=VoyzIH?+p^QINXHwl5&PE zMJwmlZm@`_-UmYM&|O-(;HBUAHY99OcwFdQEXphup%*oSbNw zG?Q}yMhctbdRnaSB#LDC5`_y575o*qm(!c#`|Hbazec9!sPuQUms>z*G_qfe)wjJ_ zKPW%Ns>^%ZJ$_c?d4^U;pW;>jR_gVeHai&tjCq$Q@Q&eq3>;cN+2`uZ=sfkVef0}p zCCL{W7y0gtbb36Ogz2kjyS6Jsc^b^s)I0=dH%9m%*A_`-!qETT=0PhO-DDIb3_>od zREbpGm^>b3U@QZJ4C9*5?0H>B9@SR}LJ_)6%Xc#ckv3R__-Xl8PS>USnKR%8@JrNH7$!(h37p)e6V zc{G38t%k^4yY-nsDK@GN#Bipfpgtw8g4@Sq#5SR~_kxyzQt=JdJ_o#Kx1zSH)dbIn zdo|UPkzCtBJ~9lX9ew&-a+I#g6OA9+u2^A{YQY={<19+Z7fU1xT_S^VAsI-D*^#c< z20sO9ln-+?dM}>QAfw41w3EpV3!=7>%0@ND=?{%bx_5tvpR`V~3Bn)Q8HoY09l19O zY(M=<#Svo=LMTdjfG&~$@#VIgF7Ea93yO^#>4&=BCulCd@A_!prR4P7HM~+AzHq;b zy7+O3m+9-~zR~;1^pO6khnLuEcX6Xkxsm}}FZdur?!usvxS+|A=Z5N+1Gj?c#bYax zudX8Q9e22;*KMU2OvSRrOl{RlFWNgd4Hj3(4-&)NztEjcJNu7SjfZ@*l%%>v$j{t|EvzE}XZ)`5^g zwvQd_@3Fj5_ADkTs~0p!WEVuqSCQx??Kx_f<}BoX92;0t^u3IDK3`(o{;uq-v~0ue z`eNwgn#D*#nQU|QB5^NO1*@#ir-g{0r=``nu?(sUF>YhE5uO(Pesy<%HUyhai(jt% zX&&mVjgAt*wAwz~`yuTw=9oJCdFHHl3n+8ym^p63Og;rN?`*T68MzlE?a~kB=kK9T^N!NaX;*Ph&DZz10BB|1k;|?#XNY55yGmZ=4<_lbWQo!cgt`?g+aF zt3i5Xbpe4Md&P+C8lnp38N!59uM%0KJ+-5 zS-;Ajl}uTgQv}I*;S2e4UWSSc9JiU^QNn?~ojSGz>!!T>N@*MjVnEoEObKEhNBd4e z*Z7PiI^ML?Ay4xYwP=F}M38v%CWme!zlF;ItmV@0y56ifOF#eSz3&0Naq)tu6n`8^ zVta+Z;!e*ik*o0u#OJ$?-lbv{GM?R&B3;A^iVJS^Cd6Wwa$m+1ZoY?f@@)AZVI?v( zUZaOk6-OX4gWDtv)Zhf#)DfCS@m@wDGq}ch7EWK>YkiG0!q?`~JZqvMO)dx-hZiQ< z4wh1y$$lH-3bzKQW~y`(9xO{3=6Yg%-t{?B@OuAE^YxnaLbbmaPRE(W0{*G5tn+;^X&KpfsD5`Aa< zb{dnkSCuY#TDw`f6H|r;GZnjtVcHRqmn`bt+IG6PddZt<#J8<5(Q#i>S3Pwmv$L>8 zdUe0i&S|w(pMPYxZF2Gy$AzDEg?}fF9f8TsegO0AHgzyzLX6YkTbL*>1!gBKD<+%&{4=?7>u|Ii^Hy>()2f!l!KT8DsGbQ^s zk<=Uz`~ZF)KvK@XMZmw2^j{+>^9gh41F+k72ryoM4M}x0oEIp5+wBX!I}-9*EU3xU zb~SsJI^UpGOD@5wILkpnp;v477>L|x;CNW8GGEYY(hh9+T?QhjzqrlJC!LB^ zLfYP8xF8Si0a8#|6H#a+pZd)hrWvX+zK@wl#Enz`aDx~0MW0h@JITSG$pJo(u@`@x zpSVaRsz^;zE0ZYrbXh#o0voM16*~uR^UXO{nkz?0OJC>PkB|r`0`rKdfOpuay=r28^!t3spW|Eb&C3#0* zk-*p@^M~RN?<`Y|J0xuz)_(EtNppyKQIUTH6TP~tzG}b=ZdIY*9k<gPCdXc5OyU$nx?fI_Z|)G5 zQaa8*ZWBWmT|k3_2>$Rhc|%r`kCn2fyzPW>|e z({p6cvBr%W6P-vt2B_+P7I6G|;{Kb!q4}E#1_FTN>~G-s8#w+oa6BFsnE#4oat8P( z`fI=eL~Y&R0|l~=22q?>>Q0JE9mpOi^uW!bXC5GPJRwCdlnE&?Y>vIZ98T19I4-go zHwkH*k=+v-)#54a%R=(#kkW>sFSM^hi%5K z<3JhL>z!XF{xPL|w&k}peJ3})5P$hCgn{i{$j#3p((`b>76eE2m+)~zV(fh+NITQ@ zaxMw891UTJd#t%XZq#@RtVvlG>m3rCcn{-oqSK`2QA8J+hdCQp?u#Ou*Xn9tlY8ep zwIxO%f^C_*>Ls)ax&R?z*By_O)6+aZm?<-OSp#=Par*_imh=QSNw?!Na?+_rta3W* z*+}+2RTs^GzNj)Qj*ys3H4)odcUp|q5Hv911%7?VapEtN4+v`LO6jYdf4W$>y zN~`(GvZ4GHEx!e$&hDw)F8^SWtp>AGq3?F2fj7Hl8qHv1OdzH(u3WQ4u;|nUXTIk|=8eW0T$HNYF0(=H`u`;1j|` zH|BkDo?Tjo=uV+E3XeS0sqO+(>1>EyelWeubOGmsbI%9Odfvf;&g4NJ)C~$3Gc6)G zov|;Q={#Pr3?dwvg`mTE*Z2@ak_+{B_nLgj+S{;g9qAQS{7=-@!n*M07c;_O2rMs@iOyQj+r%|2zBdc@)rEc2iA;kUiCe_`aO?l+y`Hv; zt7dE?9sG-eFiHe#(wXpZ@FbcV5!*MdNH{^?M4{&}0`+_i7R@sKk8?F+o3p~UaUSyN z;f<`Ihajtk`u;GUA?Le_7Ni9vp3|ybyRBj#)I284T82pJA9ar9pRk7;IXX;QRMfmL zUO@u$c~HU_yyu_c?hm%{s3x&s_BK9DwT+5ior6cbRF>vZ=PocY{BM1ZHlhyYeZJ@g z<0@}x5c3Ds){CN2en5yKq6fG1ik#{1)J=r?G+w=rjvtMWeIY$@S8vx%{BeVt=M7g( z*jj_-0?js;2?DJA!6t%m6wa-c0|H6S1hp3-)N8l};shMj;U2KWKg6bNXH%?3zbCK3 zAsjkyY{GHrZWHN<{4}@FAk&`1Tm7>yI081lYjGLdn5XlQ#@n0Gr#m)1@E+}F2xg8~ z8un*fqRQu9w!zJ`M0ipOl+m0^Is`$$9RJTkzCTl*e-rs|e-oJi7r?(oNr(MSd}_k} z$^V0e}CNfGhGNc-MwsF$Ip{f5is<<%a#AQt+?*ukAQ& z7z_j)pdL01+zBf1O#y>)^2_=Q7*!ZJAcGA9e{x#?>l@*O0VsooK|T3XTR`cr`=I@H z-LIfkzo7b8eg1M7m|BG2%VB>t3Xo!H81xf1AR7b|4D$$!Ao;65!Ck*R1pgj(N)hPM zuRi@A$I}w{J)FQfY9K1XT-mRyfCN|CH3a1USEVHVcaTmDpfN%~R`}m|(N1~W0eH~{ z!jdbrNT)E{X$eq-PajFFq2Ll>gJ_+I#{@L8Qx6Xnr&U8=Qyvm7tQ@oVnSE)X&Z0#&!_)1Lcd)WzizYXD!1ngKl4;!+x9Jd=i>S=j(GFQbiG-T zqsR-qec2szyKe-$hm#`FH_RtY@nm~-&D#3Ut_VNeY*VZ;zQ#S%e!8ri`4XJ}?&HtG zxH;{o4An?$E&lb-4hnAapQY~(tC9C%rP{6JGo2)03988}ihStA@(p!Gns}B}x{YK- zsDYx8XYoWeiclkZ$5})hy+^86Gr;Q1O!qq-Ttf(YF?g=)?n*Nd)j&;Qe@~3av3Ilw&rb? zkt2@OSuvf@B;V#|lioES7_Iv^MqZ0mKe56xBAym8)$R+7k18b>yJZE1n4rTbZfs~F zi1R6`YE*E1bqU`6nZZ?>z3Bz%Cm- z!u?*LiRT_N7_0ix`YS*+{+Lz(h5s#4!X)mUO9*|0f%*0R6$9~?8!<;q3tLlmTSvfa zJtv!sll5zLHF-=lqF)zbDk{hTffixKf4xzF=?Lo*gzzh#2CTcLycA63Fv%`(fM6}D zDhUHqlYo9_3ZM);jJk@p?BnC(<>lq^F%(EZo#o|a0Bhpn;(B^|hKHwH8(N2lhvQOG zP(YxDh6V@(l3kb$q;P=o0)gE9-Sy4Q?d{F=H8tJc-IbIyDJdyA89Aktq=Yy*rG%sy z85yC|_E3F$=rjnb4+87An#Vyw%MkN82zXf^ybJ=P2to9rkOp^u;8VZB|5v6VTwh8_ zzcpMR1f9-?>SsfzrJ(v!VEtn#7bq1~AB9$R7go_XqhaE$b^SgBqaA z?#s}Ilx5Cks5u163ET(*abE`RaN5Yt^~1Hf>3hmhx19t&loDIu`jGCa7_JwrSA?omV!W}AdFCdh?M`als_X>Q=bt6^cDhw z9D^W?4gSXyrJ#&bU`C)JC zY-qR?6h#V*ats=V9xwMOfl`#>{FQ+E`YD{iI0G$krhp;-UVCj6fM^c)7KqFk8u7a~gWAscDYF zNa-FP-qQ-)XnFY(sG}4Y?he$DGM6&9*NiL7jtkEQ9y=jMrRB@VVc-!7&qjd(x$C>P zLVzbU`||h_3cUn4w&CH{$ID|B5KLSOPzYUy0@aV>{Nt8~mwOs|9xs8%{@1z>YJuB1 zFi&7SG}W|#x$ytz1fz9{O#5p{Tom-&VPLSje!XFPol7iWV6s0c%1CPa7!}?dJ1+=L zh@mNm*IOvpfF)xP$~6PqzD|lQ7@Gw3!#Mky_aL*HnG3SwKXI5r4d-E2AVT)3vX4yK zqB(5J+2+stzUCs3$M68w#M|P<$<0M%u=V^vnez6McR+N+2WI7R*T;zY0_oMqtA{`$ z-(@DWmI5o3XzG=bz+?7~Lous&MIVrtb5HfrIonE~L$g)jn1*U@ms1Hwjzy|csu#Hn zlltK4-p~#YzPwrPMlAl2Q>?h&y4r7lC0$E7)_4Bhe-$z*xkGDoE+SN3Hl+Y3tHc6_ zil2UwK~&nJk}MG>o`FP>K}r86dVz8p1}}A*aU~1+8$b8Uv@667lj(G z!`V`5U}3&Z;GV#nQX@N@U*X)Qb%5P2A|PGsgK=Bb-1oO<*YimhxyBWY38kIUwwX~8 za(X2LZewK{VTM~wML&l9h|RM6I#!Y1q0UZ0-^a>>^Hi6i`&ey6_%sXkLr<)EN{p%c z@+QuiqGwBlpm@*Jc0n?gh4@$&Wv~R1YNJ{f<#ezy6kRGeAE>%5Y;k4Hr0 zSS;Y|ByQez{aVp$WZjbngVZ2lg=#O2)_F7EE~)Wr#;_(#eqYN0o|%CyJk{(2;j?KT z^YAK-Oa$HR`{15lk+%o*+9Pgk4o-SuJtKF_uB$7xTTAM(GJEUty!dhzzRgkW&&68Ww-S;nj9^;gf znhxHkY0ZA$&6<>_d_@AQ3WxWA&aUsU=)O-zHqV=Kn+f3vV1A4xcaZ-UizZ^9g4?G( z6!JthKc~7c+!u}#UH#2Ke(CWFlg6yN_pMt+nl%$gfAXMrZiGUNwcC&NdA=GBwROacirM zigUe$Wz_%ZDiu3DsJs#7%5zaWW!OQ{is}92DnNR24f;%dd~|_kP$hy)!)2_N-6%G0 z+(?-LIp96AxB8~?MZ^5^MSxkC9sfGw-Yu;DGro>>#9SVS*&}8JIjU7t?YK;|d)3N} zgUz1;AH>iZ6G@fK4QugE*89CtwqKo4|HOYHxFB|Y%}7lS(^65hT(=Iomk-?QNu#}Q zQ1HSDpwoEYZQ3e}Q;N%n6<=k3*^1AfG3$9k5{#tyYSx0(xEB$l#9m!}d=1@rG-i74 zb!pLox4#iW&CSQ?#!IEw&uhGqB6d}scce`Q;c_k2k2=Wnw`BtQOs{g@LaSW_M|N7H ztMv3J`3cj5t&t_azck(1gNvsZ@y@zFlv2=KzLF?GM2*sag2Y`_%NrwHE!+5m__=XJ z{Kp?xdJ)NF3d&Br+0!H?+G3mVPMfqo^~#JYdBhZygYrCb!q3ebqu(o#xR*Etyt6Uq z%c+;dQ6y_3>y_^e+)8EE{sh;0aQ2LH7m@a*A!Y8`84(RvS*t4(o+Uor%@!Wld1Qqi z%bKWLoWu}{rHilhGR9CD$X82CL>bmtDaV@e1v1LlK3UdI8lYE%SC{TI^_gf z(|qk|P8~kB{dYRH>)aXb8pVRh=X$G#q@_p)ox))khV%YEvvOr5JubdNBo~!ZNQ8?GrewwpO9vE1JEegj`?4Y}Vf?7eyd~5e%cMScsY7RjC1f)yy{1CG zj_#_v>3w{2FO6b8WoS5~B)kTK#X%i%I0r}l==E#pYi!F^u1c$EZ2ly=*9XT(Xf)qG z$-fGPEB1&OKCFp9=@bq$QB(PZ%Y^wr_t_MNu(JnI&e-Y@Lg_&M3={I;CA4#g{Y`u* zvwy&DRM9TWEv2OCSj4KGR6hPj@32%nTPRtbZ2?-Yl~NDFan zKXVrp_|iX7CfB68I+sQ<$4VvH(xJ5FY8@YyllbtKA@~5x^E-RH?Z93KEz}$5ydjpI z#pkeSV*e>#lxSSuD6*iewx)?UO}!-uuSP@h(*?)=TXM)TPoot)uaT7g*YL(pdDyAM zFnA6HERpOGfqu3<33T?XQTId9@Tf>KN}nEMtX-HkTa^${kt{zKpR*O?kr#MUn-kun z*C?-th~i09_2q8rj?!3c9o5TE>ML<2=L{N9@s1W0b*{m0*v$^R`8XGZk^yFGrGCqv z9$zJ(7@zMN-y!gl3)G@m>ez&xz{KDVH!BJ-!;&8kDfDLjodQymfwt5JJb zRAbhopL3U#z44yNeJ`E3d$dv`1PV~`^m#46gQhw{xf{a7WZ_@S5^AMAmFJ{2LD7JG z`j&jet^D@G~DUV>(l=RA$uUwZ{ zgRF@H534?_Pg>tJPNluXdojM?ige^F!+?=wu7|mNF(IvK3ceb+8Uy+zTsiV}KbD{s z?`!BsUPO8t1#5>IhGOC>!73kwq*-D^`8vnC;M`e}z}N#K(W<^i9d+4~79)#{D7LJd zX3yGi3L5##F1Ogc{Vb6ZPr2&soR7LU<=-TfU0#N<<;Hs3WXnpBtTUwlfU!#_M}m#q z^EuDcw*_qz`Y*oUS^jz9x)(6H7QF#@9T}=cjOR z@AXfm>P{1~6dOo5%u}^6^5l+`2q9p{Sxu6rkjTqZs}e5G&S;Oqyy}=Bxr4&RSSwsl zH7d56?CvzC%&qxiTz?9wK7Fne0oN*2Cso$K0hUTFP8tFCnH!uAfB#wfC>=}iff(&c z3tFQfn|apNF>5OOVy#sGfuW@b!cSOpX#S_Xp-83k7vb@t6vi4s-=f*aG7^qH9&FgD zYQ%tqXYBK7q#*B9%1UZS$okE{IiU{8;q+}1USFxxblvm%VN=#}d77=JN_;LVC13YV zOsLQKGBe6;!%WYp@2PFKqU_un#j*4{JPkfyZ5RgEKrUred~GwpJ69V6Zepj-c$mN_ z$q^o7lyU>*`lBOvs~>)2Yx@Kz*;Um|fp{e>*KWx?8O zf#Q$Ck4$Ayy4bA`po>XpF&FyQeX_4AnXD&~EY6~|Np$Dpmq$2TL%9e71b$;dvGXxT z_t`FGIF8)4YCR;rH8ms0&ox-{1X1I~_y>B;ZD`LKB6bbw?wgB@U_XS+ETyl)Ynve-&=$s)B)+Q30=h3E}_FYRquNj0oq>VNq**3Z0b_b4%)Q|W0Y~FIq1AS zfzW%X!ue&;*yAjVpX)v`fMVr&&wg6PNyv7Z5+qP>3Kt&MMYL5opTkk^CFdr`)A83q z5NQsgiD!hJyxDoYoEOPON|ahDs zb3OTKz3>GcKut!IBZ>-=*JT7Se#_B}LqojOLiJo0zOiW1%(?H6B0q^oNmRsMnnh_) zF2Bk9CyCm*v^woiL4xd&+?E%Xg`FAy`7YzDMW{u$1Qaw+dsLuz(?Y-J?d&^-k9%7S!ZY4!=FCarU+ zZVPDw0JM5W$Lk!0&LjN@fa&m8tbQr-@k3Q;`w}rDMYKfGSRFD|uYf~09Jo3zzlDOz z^wBp1esSAvCO2Hqc?6VyaXO8PUUv86Xq8Im%micEF=GX*?`aV?|M-&3g)Ve8crP#W@ zPUgW0o=NbP#rbKT@j1I#62+l}opW7mnvHSop)b=m`SJKBUlwC@*41_fz4$7-g}AQH zN|at=j}XK|iv=4|P?1T2V)Fl7aBdM-pCcblAOlpF_rN6RU64Rpn!>Q&vIO5Rd^}Ue zYQ4?&E}di;nEUP3i>js?>s{#Nu`VNusRN9?W6xmZ+Q*3Rf{Z$kS%2L~!=ODj#A1s3`VUE+k4z_H-jW;$@xVlIk3{ZfCwhz+UVCZ~}9!biq%A@8~lVhnY__p8#R{sZ3be@ZFHRZh>@1H9?^MvBcQ9ihTS zmkncpPO~1Rr|TGMznyG@;G-9Y5jzE_{rgt%Kq5U&)S{*U|BKf7lg@uim~S@4Q6X3O zI?1+e>$m;P{(+nUK+QNqIV_E@N?a_r4`2k`5vLv~qt`zE6Ko&NnY;$dyMA9nVWT(#GfCK1S;%gb!X$(vGbc@X z9iYLf?S9AVG2vRn_iyF3cqP%^cBg?GUM1R zno)>G5J&NEVk41*6nZHuh)CI6I74)tNo=RZ-DKp)RHyV6wt^o{dFh+)s2->wd?+pi zuorb;6nt;J-6AWk_qmL& z4cR}vsJ$0|kxI-t7Xb)c%C^E8KRre7FtZpD)f@lG|1k+>Y%&1QBX#Nn?n72Z#$H5x z=43(0kv<;r_y|wkY^0JPgvDmh zJ_MB}VVz5$vW67ISw>aEH`7bLUE{k`vheH$isNtqu+FIKfF9pp*G;bv$%US@=S4gvq37kbgiik}B?*c$ zspc+7{>b8cVJzg;FAl?nw~pPn#%BrlF&?jV`6I4<6_g^7ccY*^5NPw@QhIbHYSk+y zEQb;-R|1jlQutH^yE?}pAY8O>gW0y5d)zOGRaKJVVA2c2eUSMohAYKc4p3Q8=}l9g zXIzFxmh_XGfA-MPD=>friOIFA!l+({_a1Ycm80@9@&E7a!!7G;IIr({Q)F-tMddkE z0FJ{3I9eVgJJg}wwcU1$SgUVjgA3az78G-@|>$O)W|7tTX4oOy^80@)W^{C zX4z>w;!7aUtEKgGx7^9YNvA^;hbzd_4caK^WJQ@kt(4%GT@YMVb2PF$Z=SN>9_FFM zR~hQ*5{&^Mnk+N{^HG{Me$)>wAvVo|a*USo)@#zgLF{Xa#2>hO1di{CB4u;5P_F!g zEcR8E@nWD_%TW^rvSxSD#U-YMj;14ymz^kJP~xet3%L$v8?-&}y<}9(-P~6n_cfxp z0Iy>suB;8_c@@U1XV0H4=d)?sLgLedH>V)OYTKnxV$jS#ra{>}^H;nE_Hm2I=o2%@Hn*jF1m%N0MKv4vyDY=#vieC8lY=xm9 zFXD&cP0b8x^FIPF2(~q@87}r~Oysfs4mPRn#)S@AZ#^vrwzx{pWFEKd{ncC80S7b702K?+7+0k1KJ zH~J9-pbKRb|0S(B=zRmd2t_(O$#(a*As&Z)UL@gp;myJ?$g{i-wv)PDdz+#Tf-gvD zxY$eOg%sf(tn*?NlZHW6lmm z($o`H%YT9@tFu3<#%Cyb!|yeug-#C@U0X88qM+XBx7f8PMVWC zKl8n#Z~4OLlzh(%cNk5eCoq-jaaq{2cV6`%%|P5&M+_U)ci9p>Ld~^!2#Y_*^CO+R zCn$pKPd0gqA}0+kawgWktz%Gkh{Nvl2acLwfdmPQB$Is!bO>T$R5Z;dO6O=6r>Kv< z$(tze-(L|l2FylAOg@_UaC3HNxsH~(D8J_Q1Li9{UMiY z&o?J174|Z3)9VkhZEfC~w48HU9T_}RO#-T+>Z1ezbSrv<;y?qQP5u4MaM|YicpetE zOlwbicIAGOMe?XWCtreXRUl0&HnoL(46&G=rBejs1_5#N5knhP<7Sf0Yotrrk$*Pl z*NrZ6ymk*?ddH*sGRWdYF zizEWAE%eABx0KT%b#b%{O6@$Gek5steI9&2Cj6f~lObK!_+HA8sieGkBb zuI}KroG`JFr0@A<1!YWLtN6R9$|g-H6YzxZTs|g@d+0n8dw>rqYU^G929zzru@|LO zoG4%HArN9tCRH%uKsBnfNtSP<00^r+s%1z8c zPv&=aMz!1#3F#sWf`?iLEHF54pqB)L1a_eDP@Bm|6IH?X3`12^7sg|#ux;Y2eC)Hw zaU{ntrso1i-1#NsdU@_VhmiD3<|NjSa5REO?SzO36-M78wiwg{Nz$XLg35Pxx!pDe zvazf2e)8=n$D_kUtB}sy6A!~F+}+cFLp|=)K+`UsFeV}16CI#MiO+U_r?WJ!vwD(` zAkVC3ld77HITb)SFY4t->Vc?9?o(iT^coQ@0mr~06HINH~UzBe7E zVVak-c~kvNL>)zBy!r33y%mPEqaQCXB3`_?dx+e_+1$gP+sI}Jz35)t6 zyODkllySP+{Z&@YXAC7>IFzf;m!Bpm;qj&&H9^)OXCc;aH?xsjJxc)p(7b}Tls)X@ z^jy8d>UsZZf~Go(^6+c8^SraXhFNYQc!b#Yl$7X^Egv$WaS<<)p5D++`c zR&UzuWWQmP~W)HrJG59N? zjLA}2o`{rS+(dE0s&40d3;$IX0zyt8=p%Mf&cm66;xO@!Q+#7-OsVWKy%?|`WBi;o zacE}jSXf~F_A#`d;#fAl(EAc0&b$R-dp}IcF2v{d8WnSaWn&$sPv_X3swFE5NfHCcIPO$&966jMo%41qpN)per*3d+^&`P^J3;_W(3j7lMj=n$P3|Ka!?tTo@Z7ec7@q z>Lv)9jm%Iok>5GV5pOub=zI53$Ud`;mr+vB(dev_JYKM*s$5!H+u#A%ukuxy0ujH^ z>D)I!2|^bxzDX||ltS^D#W+mv$EZ7Nds_XIWncITx)Z~gu8g4s#q87{!w65;zu&Fq zchEYrs4gw)N%#<7m$Sj7PI?-1Ih^wofcJtAK&cwida3z_JmKV#P@$WIt8;m`K29!f zzhv_xu5FVz6t9;E<8SulxXEo8Hp@xHwdVd&wuPP)OfI{jT0YR#01$EDJ9G)}(U8PZ zV%9Ab)|O*lKU^f}7g$ z*9}K{00{g_P>B_{0mhHC}Qfubhy)egd-tpyr7kvGb@o!~{CA9v<;??wcQWVeDkPttNT5 za)&X7Pvavg`H2RD z&2rS=^X|dP=`u};;*ut~KH|5dI?o@^OU}Otql^9?rr`T!;jN$>Mh&WeUN_TuRZR+c zCxIR_t7fwM6@9dNa3qwcZV@V)d9&kwH@0op>(uwo+oz$*?|^Yk_dyWDaH`p3y^&Kg z0jEvM=yclpPss_Iof(v}`es`<=)n^YJmjJ2Mfy4gvcn|U1# z&=cYO%It$3^d2)pGm&LWs6R1xE*p)%nl#)E+b7}m<0i|hGOBM|8=_s^*ym#;u=V?w zG;C%ZI)DR`UiBS=vj^hto)ispl*Qmf&dZ{PCr75pCi|VJ6duBSGUw1RJU%{(+5Ols z_7+o?S{0*}L?la8jW1z}N{IL!J}@10bwF$lYGdYLv&E^Sy*4%9lkUTS4tQM?HEGElz8zEmE{ubznSmproe>I&j6fGkH8dVRo2wO?#54)R`DBuD7md-6 zztPnX^g_j$lq5+P>`0&pJej&mk)DN21k-d0)Wd<_KAiQam`jReZU3-M%JGtTBgjscj7&q}k(n~O{aO~~MZcJP9y#Hav@A;N}YPZ<~o zifJVk{N*uCEXM!s`1EXcH3=ro+8ABL6B$|3@)DWZ!;f#t)hNZ&jpsqIGNT=e?yL%G zo2>mO&phYr43AiVccIwex`NYi%N`f*C}Nyn(Y*x-<1#OzJPnG~)4DZvFv+Tu|Hv(% zpM|V=eVvAdNzt}*qecI;$+joWWsucgyizItOKa{6Pd0Ap1-qamIsdq6`NUuTWs%5cz=u#DO0)N`pil7z+0!**6JzsiG8PO8;^(4BCwcaUSO z#Q^oveh=%dw?rz0vH zTu&t`uEsqF$3H{InFFt)Jr9#5%hZv3vUl)^?0O4r=GEp4z z@`i=42XJ;Xl*&xp`S)bSUIPW5U0qAx&zH~@>dTWqYMM!%$-t2uo)`bDN;lMUHqJ%t zS2A!HpCiMOjvB6`@US`F%O7+`1Cnr3O;*5{jiSOtG zuaM|2WK&E>5E_l65gc8N1I1{YXr7np(|Fy^qWQLJ%#&@hNw#bj@*147o@Z6Y&4#*% zL@E?7)9f{B7tr|}?z|3^(3>GRVn;=Gt#xD%a#Ba(`FZ$Y<7_+6xIeP@6lTy>oAgX> z)br|W!o8Hp&xM7GAuQKQA_OACQ=LAgLLxFS&Jx&T0%-T3Kqyf*8oW0r^KzBThfNGH zVL9JUE-@AWM;*u#7-6?D=K{%v6FrF#8Ry_UNRQxq?|>-Qz;%GY)2hl)O_lQ*{cn8M z6dd@$E8Oj0aa3aLQvD2V6U<91Q!vmXOwo?}d5+gm0<8u$0my-a(5 zjUG{;A9f;?E>1NLUio`(Z#uH#t$e{uQSA;DL&&@`cQ_E}VgQt@tg;dh8V!sL2z>vU zuWHV|cQ3A32AMVyJ1{r_y+;qF?+qym#5$jIU)ocd&Ld-59Y0Y0-#l#+?-Wf7Tkg?j z)&!Hfj2}I-%Ytpq>5}#_O6FNur~(hAwSS>aIRn#yBlwBQm_?XU zL~PPxc(DHvx2Ge7iiP?$^gq?J6!V_e8(QkPE5T>OdXa4KC>M2ArB}wclVz=QAAEsC zed~-H%95A+Ra3wsj0?_2$P0FbvsHd1Yquzx7wD|e&R(xwvhoFx#C>ISBrJg=o!kI= zn_G)vG3O(LY*IDbNhPB$iDItI+MBkH5bwBkm|0wvXt|roU9c)=*PK8%-b2w8fp-;N zHuEg|#I0W>hM&x1Sy2RBj5z&myfQd@z_3#Xb^y1m8P$69X_Eb^k6PpO5pjy)P}MyB z#SoM7J@j3jekg0xzU*E~l>YN@dTQ2tjTA9tv75+ohJu7PBz7dmpfmcxJv`_~3!KI& z8(U;cVT)}nTaI@T)WNuAljl$$jE&K3%6AiC9yF*?cc@g-ph`oHu{ZZMk|+d@!qn57 z&T)udiKr3*K+we67Llb~weqrwkKdSe@=f|Y$4^JF0r^qQP*X~t`31OqDv^R&9A@A^Wv0I zgtAHMYH3exxY1bwp(A~0RllWYO_ZX1wEh#P5L5~h35S>Qy&|5~Wm;$*BHs_i^ymrVKXxLI^Nld0JQ!nx6O zEMGWwen#4OkEfp>*4=!J21p3n3BBE()Eh_bL8!aGq3*X&bM(rHmv}i!VOH(x@JR|W zKh+;o;FE8<?;7QWok8YGGw4*5cA2qda(Krb6i`wFf=+%xCDe8N4G69G7# zh0{FQL{{FcC+L~xO*TJ1?!Gxf=ct|V5Hvx8>_MdB(rj2X#I>P&E+Q}EZhO67Lx2Am z123bZsjp@n#jyOfX+57!plfqiI+DVi(>cPs2jKKN#{t>h%1cId?tn||rCJD+a%=s4CJCs^HH{~mZB@;B2o6|Z9k}20P2jS#>WF=DDekwhYV!}9=FTC z@(~e~`-E{XVn-Rkp3-4-Pf7zV&<|~buGysCxHzek57{ z+?Aaeil$91-%IYeUdb(c6P6C~{KqnqecOdHfIW-csPN*5RF(R5+4z0P~XmtRoB(==J?ov%(l-WM7UqgVLwQK`7hF^%TYT zcTW(Xqwv`Qj}z*YBgAIsd!JTFU@>}FmVtf16MHt$rTZ@codja-Q;}_#oUO!FLCnL{ zYkzFKgkRk%OUKV}p*fNSi_>LS=cl!&z(oeFIrx^I03%%P;2T*?Eed>nE*@L?ufMXL1vqbs%Ji6^b z(db@h0ul9TeuFvkV)+tTLM4L4Pq?jdv1*WG9EADLzIo9utYh8tzXm0`pndZ8+;E9X z`Vh|N%eGjKZ*_*gu9=S{Bg!b-VaVbJz+KEm9X8wS!v&IAmYMyC;r>MeeXdlwY7Jb) zwvi#t>+0G9axuDdd^Wi5N#=A%D$5`Mcn^N*=Q#wS_iR7#DCt2j8)aK$V34g`L5fCY!PhLr36M>^4WUQM(rGBe zpvf|k?z%#jAoXd8(9~7BOMrR?@QMKMs4F^#lf~6Z5Ns=%2z^>KQQ2>iF5qID)%6j# zOoyJlXv2q-wR{+;VI88W5!L}0yF3t)9 z_19}NPM)n7Vw_{rB}SS%zlHV|HfX48v$IUP#^h6GBwh{H+8j%&aWWBNZm`3s{(auZ8VE&&whti8GftF{4=f!jywp0 z=Ak}jHI_@kG4gUtq@XNp3SBou+V{Ud1?5@&!HbHRZ_RnFcHPxyFrTHtPZCv-lJ{#J zdw@U^lXVz#HMeidqWEefL=`Rc{zRK)@R@p&1@n~elrj0r|z)8-TRUX^~F72t8YTaT*tAv!BcaGIyy1 zp0Ch@p&fLXKb^jq} zV0n4hY^l8mXhRtLbBK>o#UeY({&tZV4=Lr`PU|nYNZ^u7MJ4HN9sUg_-?MEw8nu3? z%Zq1q#Voc}m1XlBl^8_ctO?VIOUUHfFXooHEy2e_PSPykcQrCYZdNgW_BKnZ!sgzO z@R;kmp?WV-k3JxI4HQZh-dzFx3eetkCmq5|$)e;u2OvZZK^mhZ*HIF0(uBD^vXKo~ zIlZS<7GyyL)tF(D@7Cn88F|UZhA6-2c&|^g4^Vgtv{6VO8QnGer{pe00H%hp|7+Zy zfzSy65cAvi8}F4qSO#N~G}IT1#^YVXAHBfTWMFO`XyRt|^Z#K|O^#SgA(zmu4r zAlQJiuf9DZpuX;kbhUpIuNOU_)a8|y{xF_bLAKRR1(`_y9)0xvM=!Pdg|+Y9BvlyR zf7Lq_DUwDft#=)55YngfH7I#_r}9$(04S{q>+GX(TPv&|ixrzLTX&t2XU0uj?TT(! zo20bzCo$lTgO}}Yet!+24!8*L4}8A_&Tj~x#J)Gt4Ed?D%zA9{Nf0b`Tr50~Q39nu z3gwlX7ybyjGm^$83{XLO9H8C-<-bZF=Qp|=hC>PNPI(wF{?FuYUPx<2sg7w1{9y_8n~FL7Q}QQA*tg{J@Dc6e#E)F|}cjzV;Q znO}iKNCs_@iztzkF@J=`T8cr_Kh=%r$9V z>wSb@19U9^^Q-)mtiTm_Kkc7=yJv&#U4Dw8=O@`Atb?a@V;gJS7cdS46B!-o9jjyq zizVd@qX|lay6Q2rT@59w=K`WQhPKcUxSJ)xH+Zrcdb>C^+=|Kpa?Nf=qxG7#+tE~n zSTq6)?yV#5_&P|UAWvkOwj%(Dnupr*cSxQ8e47WQYmWusG7h?Bx2Ln-(Gyn@PofkO zghANh(1kX+{~IAg#M8O3pD^gk!{|%*1{O~(Lph!|_TU2?- z7lltYIdNA2x~TwJwqowFITBvvE2u@-` zs$dq#;Xv=uRkx0AO&T+OfSwT`8Dc&rDar!p;l9o>TuM;@vx2p^&#Xr@-t~0DaX>Es z&MVWA_u%j9_dl*u*`4vxh@@#(e`)tuM&5>L5_5QA9+G+UN8$Zd{ByT#Qn$*S(ar8C z$H=yoQRzM|aq;%{D*#1a@4O0eUXdjZ2HO0WdH;Ns>L5J&7~SOYDuz;@EsMhUM|q9% zI$x}1ZU~&Cc{ZtSXgC`pktL!cdxP^3NO0RdzX2`uYM}7NKIaaGoW#T*##dpKr(dGu zpw60@(}d6YBx|xWS*$`)XR{_-X?>B=?f~HHje{WrZ|_a|#7hgO;$#`*AHeqQZ&Ic^ zb9-Fe``=<`*|}7s>bs55=!5E8#FRf_q;Q{fRQw+2%7ZdA;{s=XXd|7^P&;G<{=h7?6Y$f}qk%A~;e1!4j5 zC-T}tqzkb#Z{b~{0~c){b)P;HM!5Kho7~a6@t%ES_|=KyRK#H54u$os#z^nGVd-0& z&^LvD#-Gp&+ktrt?-CujXoILy8A(aO0oErSDd5d43-E0mkQEYLSWcaTr7jTKbG3)=wPJXylk58Rf+Pec@KU; z78t~RGMpJ0dAi2{*XWo($D-|%kCm7XJ)I+Le)#7ytGU#y8y2GwKJC1RLSB;VO$^K% zc#qb>AeQglvu?2C(ir!l&H2kb7ux+1q2njeYyYg4Rb6la{h5?Mj61!&O!-#`m88!@ zOdsFVq0@~D zuHanMlkGP5)@_^=rka~nrdoU9J@O7{*n7b)#0B|leyG}0d^QW(Xu^j4o_pyn8^ed835uf5@@xs@?eEpWyL34V#emXNr8s(lDU)EWcM(xJE z+VB>6Ik~HjxbAN#RneZL$o?S#{I<|}_cx%-`>+BeE zzin?|5R2fO^s6oP=AIr#L7HR4HnPiLn;)4r&jS8UTR6dsEDHJiVB+?i%U_;QTJ7m% zf9F{-5YTIOvHl#!0jQ(;uzn0YQphAW z6ok4!!=TUCE9&cVXGgvn&F=oVn-`O1gLX#5c^?16?s4HUVz+H+5fp}T@zt1bHMbk6 zswO^pSU5GCSD;8<2DcIN=N$BR;IDV@*I$VwQ2}v+mf`3@o-ary+>F-?196Ehp&Ng= ziuJ{Y@G~Q2gH?FiAE9l%dy`I2;Sa&h?QAacndN54HeKOXWgAhskjY2Tirh6FAle~e z00>|xYTr1FIe(x&UrPis42|2anSUx-IT%8!x;V#c^c~#Bm*4hIW%0F%=0P@})HmhL z%9!xvqMkRiRlhgjBIgJEzf6um7YbKV%H3`J=kug{KZxg+oNoBe{Apn@@V)$lU)nQ3 z@76_)k-2uB*8K8#lJVmSKfd^583b7{FI{blWoHvP2qHvL0iXllE9j%}qSm~GvA5RG zL3ii`%!Y!)(Y&fpD*2YYi_i&0zZNHFNn^s$#yh@-_csMs^&^4XW(B{EqSD6QN;Wv- z&C0#$R_H|i0vN{p@bjWU)&4S6KxPn)*& z_;2>T5aAF=(zv$sxX!zu!29Or6payb5QT#zjF*&|!un$f@EsENpscWclkg`?Bkx-G zQCx&avOgAXc-@S~p8iM40R^g$PF_^AnyUwu!`3H7w#6eZ$H?2wXmsRs-Y!|MlSl6> z$`JHQFL}lBN8-&3PSCbvKg6R0hNH;&f>)Tu8OT$YEAzV7ApZKHvNzkj3bxzTrflly znm>ty0FmWAaW%;y^uk4+7)Mesn~|eW{JlKV7ecA>{TE(R`TX{Xf9O?C7ei3Rqzovo zWZyL5ruN2M5-_iV8hy}d?p8*jwqdB;tz7J(_X^T2n-NbM+xweaQ&ih6|F)ZWqar=0 z94CXUG-_)Ss#`>cZy3MP@y86xK(>?%nxD z&#{T@2I}XMUp)Rmjs9o`l(;Pm?fi(;?iiU_dynXWSCW0kHPogiS5WV%h!)ZNLw7Ec4*R_T~Vis|cK@ zG-L{k?I$j9+^%e#L|Xokur^LzMLGbDNAAx63g6V*PqDx#l~>s{V3S>gse{;bjLcLL$BbQsi7PeL_+$SSWMPYs(Kl^!au{ z3b=C1ms^qoXWqd&@Z0*C8|x=%2%a!T$*Ly*vR9z`#dNP=AY{)lz)Z*qiTBcnG|1GH zBhxs?VK_URu*psy>a56e^v|+$(#hztk4;APyoSAiJvtk7uvjl82UXPu&pGOgM(N^p0KX&clh@w~OJR%Wto1`_dq}w#v&N@+ zjRC|~%O3?J!|nWDSPhTT{AaLbULu%dyzES?3N2>~0NwjssLt04@LJ(IwCpPfu>kPp z&5gb|5Gg$0!z3Q8Q5J@|@kS?Qn(pRJ5oQ0^B;yiaK+*~~NipFE-_=RJ#kavwkKnxO0Qw8!~RAi*KPNX3yFhF4NU<-^Mm=HQ0{u?9UTybdO z%(J^YJXkc?AGlNZQ zpbBxXX9c+xK4&SXlrfAtaFh=xtOJ9SAkKg!9(Nhk zmF07D&?nR!8y$&_lXz#BDcW@rC$w(;T1Vu0c3r?1tB>I>I z)qQNZQC^*z){js5*-)1#=Md23?ey~jjKf}%(1%A%G#J^8YwitItrj(V{a*MCP#EA?R`Tw$nOaD_p z+>AvjGj&|+0s>x6k|UNmujfn-j0%K>E=U=ABSMK9Q?F%e^wTq7bmN;#3W&S>v6*SX=I~5gZu_cCfjc( z1Hybi<|>Wu61}0dKKYTNqs46{br?UfFMZ;eO$wXum032){-YxdNdvpjkmXRyB5zN2 zqhhG}>$=IZdR3pA@lj;Mi<;Y>?7ls%&*t;1u2zh*PZJJ}9f(Np`PnrMuzSN>h~mpM zHtPqZZ?`&Mi@M>Xw{4{ewf?5Ap&}g1KMolQ$7Z9wgMw>+`O`iiMU{QHz4-nT+T12Z zUDPapYg05wqDG38BJ5t)16(pTZsnR>V{ecuEy@nuW#yV84GqZEJP1Cy7iX|T`;)sB zK6&fM@i`eq(mG17;h3VXwwJZ;W@1KjJ6D1HyQwHzV3+%*EYejdZo%zFCTDM$JjX1) z-uBn$xyezi%-5u}^X!i-#i*T6$WJUk|KwK=aTza;!aq5iZEX-Vqa=)~ZB+(Ybh{I& ztLNM`>3VyLxw{vSp2jQXO97liLr};0T{Kw6oOjfIFXzT`@4fZYEDN$~o(0Q%oNf%~Bt2-4k#sqF4Zt8--G$V&+VM#E&~z}6LGZmF zwo0lv`qMy~hN)2~2RDrxp z5$vRQ7wInthsO5?-OhmOK>d3V&%#^dswe02Wa4TPG#2Ar$rljJYMOsH{4N+El8*WN z+L`Xl$~fYz_wRoXui+w!Ev%A0A_L>Vc{&2UNJzeG%A$<;slV?%mi)fBG^&k|lqAI$ zyYjA#mb5l3qvq>|yIV-l&wp4BT@#P8Z7!QR!kE+aa*_ph zM}3_PIBk&@=cR1chHf#5WC5vEAr&HWRDTgvz+qjm+Aeh(^NkUX9s_6!?Fdhl$}@v@ ztK(I6H)-bbQb5gdUREc66x?v$^H)WcBaHt!?e36t%|A!j2OVH?3XuHD;OqlBQ1rz_ zR+daSF!}PIyXBwVn@bGqAM&dFpxgNRHi;8mh$&%WGe3~NE?bo3V2|xV*$xcOK0wX^ z2&v0?!dV?m%A(s>uMLyaIzN^`kf5}%q*0=B1AzQcpIm4dG{E+t46n>VfCI&73t7Bj z+su~B$>aknEz@K9S=y-k(P*2dh4G z2>h{}Ot#hO!hZ^*tjfx`)#c7)ONyNvt2mIv$on_pISAVmTv#jBoT=~49&;W!J5XG0 zQ=}Zk((I_U#Q-01w8G0#b7q^-|rO64PM1WA~rSYqV zV-n{RIz8>nh`$&is>X=`C_-B6UDr9^r72K{&*xc{7qJ^z63qGv9f*C(IsGqEpmU`` znyLOWNFGXp*=$-<$nVS~0FPxdDJrg$rLVtsXzC!dr6#MRJ2B3NW7_eP3}|pfU}X8kgV$mJd!qAN~Gz-1jL8i*cn}uva{K|vQMjea(9{Yuk;d? zl9OmQTllRTjtkke(7t+kj89p;PjKgzZFB|pGzXFi9ep3P_xt{7(L~UsjInh-$*R3$o?RaPB}DPr3RG<*$pw6S}SHS0sreygZfClUyxQ!obK$*i(9Vkr6*= z{!5u#vo@&evq?6;JfqTiewKZlY|jcVngZW0`;>77CSb#5D;m@I`|r<1zB>g!aOCU= z&XB}YL5UrK(l=lleTzBh*`t5T{#=*vf3p#>a2dYLe+*obqBzrNDOc zSL%^qp7aW_4^q&u-Z)Eu6vR=gAihip$f=)$MRk1>9V05o6+J&3 zg?=}1;{2`(m9W9-!ktm*y&X|32a+Tbn02tu68?Oa?XB;>q{#*iNEVF-t5g@8GDA09 zKZf4=dJ@#^?kK?IR`!n-sq%h7p-&o5rF5oe>M1=@EV@in=K+|a31(dn`Lqem)AzE9 zWB0Kf?J%*cDwqVt*{H07uNU%#1}cSjYx#ka%2NGQ04}4+DW-Ef-+@<+a0?7QsD{UR zHP6b^XcPGh&mZTW<*8c@%cD0 z57wmJ7VU0+TqB5N@n~R&s?fc+Gmb=?1hXzb?WCVENA>n@mGkfKb^0el@^K>FKLaI( zBL35+Kpn~EvqX9dKa6W@%ErzrT|K4s7vPfcDtVp9`FoEe@l=qa=bPx#a**izqnm1m zM+CKWV>nwyRRWeokeG_Yv3$hU3Ewb+@ba=~+t$2nWZn-T?`MdJMY}ie_=jFK9WZ^K zcU3@77Bm@ImNqUt5x;OVD_kNKFsf{73`y=jxvi!>Zf-apw|{qEh7J1 zF&!`*CkvPy7!jrQt&A>kasXpMoWC8vP&LK?U7v01j9Y1ZDam z9}#YirsmM^2m^|kJl@v zeLbG=7YmF%{61bOY8-SPcQ=CVO;7B7?&9L!7hJJQUow-w5v6H+K3*sKN*9A4+f>^u zuaQTnC4W>oRb3NbO|$RZw1P3Ye!TDb8Eaq^241?ow>JtBASX#YUuK(mxaQZHxM;nt zPIg}|o3%Ygt1J^4CT?a~Rt9Nfpf*G0-v-5aF)a%lSEpL&Ak25axL2WjBNPE8c!~Qk zZos1f0*cU<920B7f2sL=wegbAF`Bfu(CA0G{WzanT(?fMk69B&W-QC&lPn14_4BAs zzsIa(k~lA4aR2hX?7eZ|{CxQY_CB;XZ-I0M_-iu@$NEP_vuUz_ppKSFUWUb#e>qPs zqF~uv_`1sxn#LkSE#bmdTu82@;@$Vjl}%d#5uIP`#`@K)cPxR$1pGh z=x+)cqHi-poA8?lv#OAJ8v5d$ArZD7@wK2dzCv!(@7(`kGKCPz!O`Xxy|T1f%G0^t z3pJz8&j?|5^!^JPB`UplL;F24DW|OSc~z%q(#kADax{v^{V2~Zr;OkQm&!;?RPmPr ztWd(~hu86+KjnG5t4P8L=iSd5=;dj+a$E5?29`JyTO_q+HP7ekGK|XVpZWFFrrF&# zMUzH;j6K*ChxeLZQ!eCn0<@Ofi|TGNd1$8z?hHq3ZK|`s-6>Z>r;~F{ja*eMl)sYe zQytHNp(Gl{+daA#c@gE@Rnd4a$#$}wXSl8|cp~^V34Jp)x`xb;Hd9`5bCQ~)GRT4^ zs%O?%^uv$k?4t47C*N##BW~Ix5s?2T=|+R8r*0(g^*;9j?zI=#X0D9mrB{?mEEe-fdk$?GdT|JH^nZs1eNz5e?p>0ZGf z^b3H}5!F?~&?s5%Y!S&@Ce0QgoU3SG+MDeJEv~ZC)`B1|Fs?{PO4~&XWgCN%6KFHl zHZA5gREA`6=C$Etczuj9NhiWL=bWe^>I4d+_w8sy<1Ej=N5K~i(0U7g|Hs$jHma8u zmwT%OSpgtOnA+Ralzcz9`clGisZRPYjoC@Cio`^G3EkC!?~fyZ$RK^CrIuyYoy)2d=m%|@WYK~Ro*JnObbE^PD2QiIr!6Gf zHhg+vf6hec`b@&uj9716!uogse;V?utj*_H!yijCg!`&;88H8U4?ganN)nvn)gbDdX-FHj`HM z{{J*RB7qL$cR(O(9>m{Mlhj`EX%c)H*<)%{3;D=Yd3X6G>n?F2IU6o5<>`P|x4E~9 zy%1gT7Ekzx7X-QSU%Vst`jF9Uc@0bME|%EZaN!Kg{Hu|Vj~Hx5-r{p}HV?8g@kfsq z4V;8%0s&f+)z>9kcpQUL2;voX?mrm7zwy$uF;inVF-JGNgET6?{YRU|#c8tH*wCI?zPothXF_rb#`OKo%KnVj-JkUdezKZvUf6o{ zBm%JUL%MR`?HCLNu4c2@N8}0!E#I?}o$w`zRa=0v#Zg+ODYP4r^0MI4DAiBf1a(ZwzMntmdAdm-(P0zNQDXde z5vDEIx9*`en|71upJ(B@>X;-lg&1%2sGodKPwU&*y3`=kpQBmEO&B@5Dip@_+C^yi zyP0F;pU_atvMtgV$qzUpSVZW{qrAwYul%Ej5CI@akp7rMfuT_vt%?=@&*6vy(Q4)X z;Q_eLi{b?GI*wI=CRqOUF^CmKtDE8h7rwdS|3vg65hHs1f_GV*evTi_!h}g2#>l+$ z$I!q_1EmD?cx8c{Kd%4!_2DACXU^9cy3ObFn?%I@$|Somw6mt3uaacX8RGgq%Ucsl zXm|ea%t6f8VvrCT!IRs3cKK;L$&o+jkB_$z+Q01X52)1-G5<|v^P?ZO8+~+ek5-d= z_DZRM^6;+t7kGIIW2j8O2W}nuf~MsnvW0BJ`d>o0LvQ+vZ?gZ7Av`+`pFFeR+82!B zFpfvHT}fT_Mv>RlD1d)Vf_dG{G2omRr$-ijD055GZ_wSY(J*nULSwxH|C)hj5r5`~ zfW+la``cBaFvc_dU0Rs@h&M@!hxu>WEpU&RNo3iKi}3#nc=5yhhHpJ>>j7Fb@51>H$ITOfKX=#B}H|0AOqA6>T`;E7J4P*(!=R zA#;OlyCd()gmzU`&2sxk746XeN&lw;oxy{ZHl0R9iu3v;bgqNm3&i1jj*dp5L|A;J z=MaM&?>k}%{uMA;7|}f~2K@g9-6aRWi)sxMw4>os7FktXrA7TAtBapaliyO4b4+pM zMK6D?8X1yg8Cv*e#)ZZj%J@bIk-v#a7^ zTJZbWDr=$;^`D|B4|g(7XVr&78h`p#HtbjCcOa5nJ`H%G9;W(V6R41qgYKl=^L4HO z$@?GxP%}N5FB|#mkOXu*?;i?+4M<9-Ep&&$4ljZ_S(`<;aqnWn5l4?^GhW=ITk!H^ zS~hjltm1N+ExBp-hgGs!cu&HN8#mKpzRl)kd9Y$dWDm{zzyJyKLuS5GouIwS>4F)0f^luSI2d52iisK{tDLBto=1; zeg%%ftaqv1^q*xr@Xe^sE~Bqp_RIhdoB2)xm*{3R{tYPbDTxaHU+_Tozd@J$9`DZ% zAr)yFe~ZsCWC?wwo-OV8HZ%zy7Ly!*_3HFFLdjx{#u(+e=2i<^Xw){|J$m7Hj4Yd= zs_W)v!_s3miLYPhOJ>SdoH+nT(j51a6750|%nRH4{Ed?yZ12tas<5`XMq7^SVbK4$ zNs~;9DRW#>ow*BwPnShh)SrTEnFVFcT^zvRhi7&i6}Jlv(bt@1&{&K(fCEaqvA1bi zZP7i|-$U~}#khAv6`fC$%O=aS+nmLH5!-X*8y+H6IuYP*`wh6j?o@Bj*dslQG67Sg zFeD6!Y8;_Q1k;0ks(fDo6#2)E>LdVOln4yr@()4q&z6JZPh04Pbrhuu^f7*?;Tbz+ zWeK122t%xXmWon)=sgVZ!y`5e+iX&m7g2-}9QcXxE=4)g5aa_D9TFt}KolPnO*l_^#}nu@ zxb5ZqA~|;>0faB?G+yN~ip{Jnax1f*8d*39ijidO>E?Wt|L0Top;|(5ij(K#=x3Cl zen#$3A9T`pdi=bON65?d?HNA{q%;vR5Zbu};_B}hl;UDJ+#x;eLwgWXZwBxl;lOnW zY>YqnQ+n}>jk8^BY=N#gL)p8l%Ce3(d?6-bewe0leuL`VpgvAY*`r7T#3*zL^Gtqi z(JO5y*}Tqweh%5CkJB_y(~Ep((dgomCg!Lc{jWg?AEgLT8R{FlfL^*_J(oiThPZ9Fx{r#VNd4w+Z#bA6!oIiqcv7LGCQ$h#BfSx=E2fRuLJtV#2-ltkn z$FV2SDD;S;-3i#C{z_km10>vCyH3&?%^viara}9M{^P?;|7V3*lEEXp3BzcG4h0Q8 zqcKp8vJn*Fk{Hv5=}kSKMYonq5OMU&c40rppA!z8gq;LlL!zq@=v=^IfP(Y&#KX`* zIFQ~9Q1QJYkk(<}7g?1=-P}k{)wHk1`~8%qBFuE5c26jeW2Sbc_ALtM| zN+8~04jhjJ4CQwC-tByPh*t^fI%Mxk{l)D`)q=tO+$;k^9edOf8vjhH#mV09 zxZpYT6{IBj&QH|=+|Q;B!wm-!hM#P*D0VWbE_9*)rP*Y-Piw)R^Y&_YsTlXh)?=GP}tK5M+vj!w7-07eGs4T>8J+wNF; z6(pO_HZJe@PJ6rjk8Q(aeuWAt-Bsj}tI{&EWQiV0XMa2=Vbq*(Lt-2fNZ_}&NBKol zO)98DwvbvjMfTqx2DY)>1s zg2=&1*3|VZ8gYtHLgf^c6tjL={3Ug?k1Dr>bNv_ea$H4%1lR!Z!>%a5X}FU}R<3S} zd2J2a$}~UC^Mo%SyfEIdNSZPF@Sk?UGSGouBCeZ@KQTN%aW8S-f>1F=PURh0mo%mB zJsnAbsiZT-b4_9{J&TX^?O7NX^YqzX%<`|ZrYLIaFY=#XxiN2qF~AGhJ85z~W6QEZ zj|`RNB(?FQ_acjb<&wh2yztvi5jYv|KqXdPN7m|ptKrabE{<&w!)3J(w zXq?MugBrt0Do=Vkd04Q-MOZY?<1bZ|mc9P$0IAwMqg4tQNBLM|_;zJ6iY;xCE&o9w z{B&`P8i~9KW!95&QBHQ|G~$R>MQy%YbNXj-YLg8##jv>9?2fn|%L{EBaiL^ZqNIn( zI$qWMuWwb)GpI$gqj-es!eo$HPZ1b)uam^rxd=ohcV!ZkE^rS$(dRy(GS(XMDgM@I zm_x+vQbZey$wOk|uZif-BspGT@n6hsy12I~SJ|0$5>Crvl;l@oTFx-43xdg#yF!@t zSVtd-WTG;)HRlA)@u{{Q^CIiyy8&a7?m=I+*f}4Qs3f@zz-sQMIiN&u;P@WiCMxrcR?1U1UP5_VNw6n^?b#foah+oeo(tOkaE#*VO0EX?utzoSFsvhWGh;YuC^{O=8*pZ{}yfFQ7-^2gkHSzA6A2gO7eRru(PD^4|J+v-dD<6vL_ zFlYqP1k_n!osZKeUxo=z0ku2y}?hh5>}$KeS(g@~G(uo%?@o z2s`9oH@=yKGPngYhBi{(N3e<=YV_hJnyfs5i?L zjFN>ac4vn03!0M-^`Qr#q9oWIX8qSF|AK}P`rst1trT0$!*v+v#YL0NXH702 zYp{$dP!bu+KB<HV(^Eu63Vk1kX^Ly$m-9AlW4Us$H4BR{9* zyuQTP6@AH)uhGJ^{!ld-VDPK=+c+8Ih%!bv^2vqY0|`7}dds`#5z8d`4L7e4 zuM7RX>8{Wt&Wquc?>(dTjHe;B9!I%#GrBx9FH=Wc%MwfPkcgDCMyGv)Pm zC=1e$k3ps1Xv%ZqC`-c+jFc@!LeBS#rXk)goZx%wBFizrxH+6Z=uHP53K=Z^x=AMM z0V!QD13?`{wal?rbzNF6qtZY+%Gwzm{Uj(f8Q71Kd!5MM^f1H;Lyk6y!_BS@<424X zZ_XA*-mL5mL>n{CC2h^mVt(_ukfl6b?R;f;qhn)iN!_;C<>kn2O5lPCNNM0R?4GbFAZ~7ZnitUc~)env*<=Lr&R}t;>{-xAV47tgONz7;(j?R*Blis8upD~1X7ovQWaP($e(1Sj0c5L&J449mN zR|YpSO&+8AL(`n3HjOr|Yd0m_fgc`~<*dFaK4yLSV_d;H{}9s*ld|ng{nw0PxE$*w z{fH6qU7go>Zdp@**PUp@^o)YJlf}7PfP_=z@43|c0$iYD7@!U`yuTpe#rT`Ns`9Z3 zehm-%HXTM3vH=0&D^nA>Yk}N$RB#=;RF!TFx+r+ALBgny(VOf|1)~l2x;k|Ie2Rc@d(n&*mKL9QAF^pB5v27b(Ge zCdVn{e}SHr-0r^j1hIJ90-A1jV=hpgS4N<7-vL~}v?I%14pe^;nv2pQsFZI=Fi`*E z{A+CM?9%2{d6e&L9aJY5$co&?Tme8$%ht1oJ@Ln?iWuY3i4k#lYRRgYL1UcExOG1o zrx&M8D1);){(pscGVG8*U@zSD1L7UByfQsQ?;C=KZ~+G@7vCRskvhxy+b6VN4)2KV zv@VYLcXoaq6s9(@Bipd#Ewe1&P5DD*W}z7;MV8fbem^;jbagew=%sz+{t^XrcE+!@ z7(=o>Nn_#Kr(E2Bg)T}TVc^@#f+b42^El*PBTzrHoC2O7) zB#hrs0x?hsF#Jp7yv8`9D9+A9(G3A)Lv$!7ao%tdD~83_cC_K*6NZ?WxmV5d_)&Ei z0z;Bx%h%&>EtAq*@E&;!J!fxl9(V~ug3?}5UKwS07fA1!4%VN#Fra31woCH5;0Ke; z495**9ZhAO^4i+d>ms*l%CAy&_|&mU8RFhX4^72S6N79%FLsaUD!7`73gV>7lTr7< z2DKuXBr1x;_hS8*b#I*YOK?~IrBQgph4U5V(7^f?Fi6?7{JH$=Xv{H_URKQ~3}dJ3 zZdYXq;fs;*@!MH=Z%)(bq>RvKKz)&BOjsO{bAuc@%5UoB_8(?poj-)(?xAc@B(#Wl%ZmLA5U!s4U9;!=ADG{Pa44HAfAlhv5Em#2HCnc1 zHb?m?&Juo@u?`=ZOC~9!GjUHr1zAjGk9d0VJVf=StS4}Q|KN;t#iBt3jTJ8vY!QCCJ!HEvG zH3^ppvByO<{&bOlT*Zqp{-NV2u#8TBoX*S83(;lQ#qCvtaST7<)`vSl6$|1ppF{@=x*y|(FebasPw5k=t1$aETm;4p{97K*U< zIADLjw3xea+x6n8sq5_@S+@OW`wzx!-A2m7sr)ic7f;tO=6gLa$Be518u}}soPRSN zbcy>f&AZ^}i=$F>SABmeqbC>_4>!`9@U*UVno)?|?Z7q*H{ZXfIU{}?eSq{2{>G;B z;6nqIFZ=K2 zJ?KJ^L#QtTeg7*Va{B&L_!ytXaiMz>(sVA)DL>T^-YzZT>76|ht{JbIieC@^QPmg4 zY0UQ|!8WMMf^Q9!6NnjyzBetmm5skQS-q0&z&jxTv>NmcC<=pw{l+K=LW`05u-}-s z(8cqfLN3ZC3jOb>n~YTk+`q8+i+tvG6rd4uyZO$=ELJ}oG%@GxPbf|;N*%^PAB4bjsu+iD#sNx^Oj-eUKfQV9P>Gj1D zcK||C`tME0Ow^tKYTm*=6eWn$S5HRfuXKLjai|sM<#bawDY|KlBg|TM@M5IXIf_!% ztdjWTXT*jcBf2z2gEn35&><-CzcIqhq$zareq-K&eJog<%`qF3==j$~pu?N@ z?!)o=JU-SBOk4ly|9e3}Y5n-`y83533991s+u1HmPcf)%lVglR9-3wO<->oZT&}-2 zUvBfPsy}4evQER(C|)N!WB4#|;V|R}Ia}mm85Pkwv_&(^Z>-@wB43EO+jL2CiE`%w zfbd@$VGhsu{S$pc>;r#gUV~8kAX)z$C8J#5UplJ8SnAFn^D+*@)=$36KAeb)W=IzB zPGnF!uRn0t!?2ha$tErGc$B7V$sv1v`Sk?aUNvht|94u%`n`5HUtnTr=oph^TMUB} zo67v4DDiZ~rbEm*usUoH`#fN-boM)j7D~%FX=3Jt)85lKw!WNTV*# zNQ=QeZkC^WNmr^N;nBRjnQxbj#$btrVae{OZZ6;(C(|!kP*uxj_F%ko6z-RM%F=@t z_@BnpNH$ssvH~M$+1H7II+e@oJ7&QN1JprBp(S|dJ2bF_oRr(kgiXU?@Gs44I0!~b zT8C)Y!omCtPq|S7FXNCfj?L&{)UMB=b(l6Ovdcf0RWU_fMC|dBH%lc>a zcaq-3GGIo4%X{MLLIWSASJ9VB)+}UMbv=F|i=1katvAm93RI-J#GL!eNPzd`Z7zbl z=2*7wI|%=kc>@Q+{Wf0ZskQk<>G}^bxUG1+DtErOo1!X8lrSpmrkS6_sa>0O%eq(1 zvuoL-0Kp0>l;4|h#Fj)O4{fW`nIC)*1mR_#qkS;mC`S=Q2}A~#>A|}?f*_It@hfov zJwe2Wr}h!gNytM(OAh2;RV15E7iP1#XHjJ!gF%UkahS>$ z+#qpx`6f>vcDDI6=NInirDI+1DpO$!!t0Dg6Kdz|5rm*`iX~?pUvC^mnm>EQ@jzGE z{?$0>Fh`_#i1YmXgqb$nrFj%T+AH)7n{&qEax|_~sItqdnpOP#<3qmF z&zf93J=f`1vtSyNC~>Pt7{aVw+s_;MPG7EpW+R%_c9U4XM%o7G(X%p@O(pXUS_ot# zV=*G!PQ09QNn&ELxkLhuE%F&gp&jec;FQCkma{XYjfM0*^M=3^hZJh{^Lh+gqnd4Q z=Cks$nKhrZ{Ue=;!}tmJag*l7$ecyo9U_cJ&@kq_&uZRK`E8nu5r$iu5(*YXQqP5w zq{tUlj!eOui_+#buEei%9^<^2l{p%7l+kdd>R?v!Gi^?@7EO~MLj!<(#-*Sz+$6iv zCUI*AI-qn^nP+2mHkEJ9geVu?8wH=AJa(g)_slCoXGwtKbb*-NS@LX;;>Wd36F2`1 zvaof01f~^KA^A_6qgvXk%Oc^IeN4FC8$IIU;4On7XCFee=Mn_<4aw*)>xb!#c__dC z%}(s9uB!5~Nu6c$y0+SsduEiDOJv++@`tSasLDKzH{oCW zwzV~zaTq4wzZ4HK8vg|}A-Sl#7hVc2Ez-)8B^xNdGI|N&C3Ba;_s!7P`v(+6Cu)p2 zUq5c1O#;Pa#r}hIIRw_XHp%}p+T8GyWbn6MI0~m_nOhqbRWM8JSNB1J2qmb90Z8~0 z?jGuK-JF#3I9%47Yv(65$A$h zC`5V{XUW|tLRqACl)X{by{%*o8=Ck01o8?+Etqh#h|}Z+I-tzI@G0jC$%w(%MC(Z$Vy$9f~2d-`h&TzN>cEXt6`5E+1X#yOdx41>Q zMjwpxAC$l4AIQRLUW}!_8C#c|Rdbo!#O6~JSzc#3d&nts2Oqsy&Y1<)e;?KD5-(nNo4^*=O1 ze~ol7i0VTtybn=`^2<*j%ci-BFs_4gP9Ytizbj^WT3l9l@{S}6CJld5C>bm*>+U4t z%!Ip+lij=8<%>_k-#5;JJ*2)duLygWfM8?>TCvN@{mKsc7nIZZSob5rY0$6O-P$bN zv^{Tt)2(TlAK|8a4&zAdt|XBh(t}*bA+XRIY|!Nw4@t6HCzhJ7WSFU(NDOG%vz?_IZ@1QN)(3kicb;9sa(Nz|Q%t8Tu56h=;`R^goly zKN&aN1EBqzMR^{(?E}yzE}*KbUfM;JV|@DUs?dL6IFd8!Jpe})P=~D`o(w%sw$fn( zZO!>6t@F^tO_e|IFszgP2S1htx~ptk+vzIBz~o11efc@1#FeNLJ&Dq9S4m$Ae`Pw5 z_+lI#Ie`Mgm5NdeyOYNgL(KXdgV~YYaGK^qvcDAK~q0sK)G#t2x(b7oedP zO+L@^m>aceM6p^7*=_Lw+EDbDQhsAPIH2Kd{rksm$6OLfRt)!@{EX84Dm3G=j=eUj zt8MTPXD)Y7ax*A@M`1iRF*i455o9Jxba4no<*&S<%%AlzVEH#7qhnUb5k!P@p~FcK zd?tx5@suYV*?p3+BH#rY=q|Ket*DXIy}y6jo#bQGAKt$U`jt`GUloS3TYCbXGupKJ zR$Lk7ItU*noT?hdUz$9A_<#qSH760&D;2=+FTwn@=AKDXg`FYL>3m3B$Sqs`S0Hjb zL2B)9R(u@A^5F>`RWh$Cb|u`k@~cespnZWlz9}xW3b<3^#j{*iM0BLz8G!==VwhYM z`Vq%OF54U*nwHxg29Nj2=s2~Zt+;Op#yVZ{`|sa0hKolas;46g;88yE5`qaXhpLHoY?nmtt3LgH5b1VI4v6iqpc86nj}+03G@g7T^9yoKWuuoOHUuW|FmoQlMC-7bs~DBB#ehe=T_hU95fD} zg!)vrQcYAL|H4&SSuc#s%SJX=O|F`NT`@ATDs9xPxL{pkbV6Vok0rO4HIVzRk!TKa zuE8K0XwM=>Lc}FTEe>vL{3nA(*v5YeS@KV#s-FNvBK3weLffvpEs#{|{|sCVeOTh{ z4T@Y<9w(zTEmk(!9i7P=<`zlKIeELRs;s_>itjQnxR0w2r!I)V>H>ai2Ea({M%nos z3UGX#mlQQh2M9K(zkQH!9~eCs$#}eT59)sd5)IK=hDEMe>h9`nUYvd|>upu8qS?(6 z$3M7I$hXB6Tfy?^0b}5ONrNRRLA~PCkCVR_`b+Z~gd_s`$3P=wy@DflRQR~aSfC&O zYJk9-G$_fO{(Vu`aX-!WKLrHQ}x(2(z=Yp3hI}ZE&~! zG_N1na7z?exZibhc5@O@`EtZAZ0J2O1JF1^6i)T%!Om>)=)}^%+KamEimkl?n+R7ZT68{guiKgw3W=o7e+9|XxOTx=j{d&~O)FfaD_qrA##?+o<6j5cO`4wE#BpSk&2?25 z=rCRP_+L+A6*r$`_y#(`bJ>(ph8FrtsU`;X7P%}hp|qJD|=S+ zKXV9T@5u>Wt_S7CnChs~J?+MA#|L34GyIT|jT?{f6ao#u%V|2kL zEn83Ii(usR*dSFA0a&>ldl-IM| z%3o0=TjW4i*?PW8YpzE0p+iG*JH6^g9xnfe?VnumU84+)z^bDjrT!m4Ct6&m_PZn? zZR@{`dEW3X0tY5fQg3!MzGK?*W$MEywwKwmZtiNE*c}I!t+)8=Ddg7=yv0tm3Oq0l zOhQ+`B`Aii2{!;L6V%wx*x_uo*tD*ipw9UUhv z_5H7*m*C_ME<(R_Q+gMUQN|_J-8KvEmRVKL=KQ0>eBP|SJy=Aa zKYc{8b>fBwph_6Zo-JozWCP$O3t*a`g=Sn7$vvJKdWX227svICA4NWt{2im8P_I%M zYIpTlyj|ko8i6-x=lfqlcQFSJQ`(-axbU)!Vw}QnJLTK?ZI;#1qen#%xy>@sq6Ou9 zlK3>)@LMp^F?xx;!3moS(EQC+gYh9$r-=ViY#va^TArd1!5|++Ibv;gPg8QsL0A4H zfc9^5aB#;1zW*giybUJG_E9qIfwHou(QRn?Z+x2!%~B@dx2Gyz9gTtHjAdK{H3 zQQHVn2I7VW5m2IzlWC3iG@C5z>$4YRXp%Q*2%Afr)|N5bw6qDoe)PuC-~9bGqPhl5 z00{F>We&u@H41_pCh30-10gELx$5a%VUR$EqeK}CjYXzrWRlwI<{BCq%_KL8-Cbmp zc~j$tB~B-#H{zHl6f?6cW+%nZ{FW*a*W{Q-ep)I^70CtnHNG^`}=sL2K zcL1+ge-DmX_?%dc>TGqGc)GOcCTYnf{8_V{mo*d@ZnIEukqH8&Vuh=ZW%T`IcKXOc z9qD_^nZ1JpJYibg9HrB4bMMxFT;FrA-*7WND~pl8;kP_YkBHgFRgC%P^k)~^qw4ks zr`US0N*DkvdLh%06$*}Oc` zA;J@M+i`wb=V^NMc_ZDg#yl`ebQo=aF?uxP?ms2~!Y`)`nd5AdiP% zx=GTU4DCO(H+QiG9JY60+QhED${(ENvP$nkcxdsXz=M!?fxOBu9!Ouv%QYp^4tJZD z+PZuWjFg9`f0qrkzp8HL*6g51pd~yh8*0>5b;0S+MP3(0Y2|}ah3*oRsh%N0ZDR|2 zR?7QPp|rZj(px{LA5p}s<=ldv#3R{$+s%@DW)sJcqd2s(|AxDcSd`M`kbt9x7=lhN zBaW{eWx(XTW^AXp`XJzaQ1)IB@+djs7iI03FPHgrNBNE2eV#rdljNt|&*_ZcVsG!t zO_IuX3Q}N_h+h+tl6X++W$2R&9G!Ktt6VNMl zpuB#&;g5KgWf1y4$R&|q?QXi@cZ$fr4c`@kzDb)p&o54*9e?P}5kVN+tEQQic|7G< zSEJ*pKJaxSOjD%XJzttVJ@!oe`UZ*|It6#D5~@KcQt`$X#jUgxQt1^CVDpYC z=(Z@UCiNbpBL8XAf}iiEX%wY4uhD-%uU{p|xzBl7*45v#DTW@-ggN><4&J#)kxAGG zbs#7|rqK)#${_eX00T_2;JeZE^AXBfn3w#gls`^aO>H+RNK|u0RBJ|s#Q;mC!qRJ) z(?2=cf47~?PwY-q-@&EDk&b~HFq0s z^Z(RMg(2hW^Da8EXV-InzaWptpsq9%R0JzK5QsE)Ewjq%tGu|$p;jv0U3(9L`@W4v zOi9o3djRL!#v$@feyHoc7=1;GJm7{C9_OSeMMXV%`ok%YdBnj5FT}Z;vx2Wn?y{;W z-A_Oy&s|jH{F>D*Ol0{RdjJa2V8|e=9Hjf!pT|YhG|Ow;E%P}{XI0bir>bx+qL|zBYhuumB!3p0=Mwqn?wAtf z5^|Xi5r;tCzbE$8JqRUCqaQ$li}x+p0Qx|^n#So2EDdtFo?MKyiRxQ(RZK~LT0Gk3 zOZvV35`WfzpH8yUaxajPY;B1+VB6fr>vW|HrqIDHeiT>9=;0{+fnu2_VKIw0`6&42zF7h9+QX-d1KD!~!m(FLhT z(E*TP!U!X(eefHmA@nRgJHR0WlAv+`lW&(x8?OGgizFH4^(viVd>STZ9ov&^lHqoj zbzOYUSN#TOPs!=GWOY52NaHAhBU?DT68D0-6V0!JxV?d=l>MwN*+1lfuG=L2OtDgu7v8>dkn8Zc>0F+6}wzcXu~kH ziks&o&24!4HBM3+*J#E~Ajcoj>{VR!VyfNI#20HDt ziO<5vA86Vn2Tc+e_00)0uli8kWtT;I8hZEq*O6v{SaO29b?cvnL@qF_dKDbd>+G&6d|^JDYCq`H&^HyAV%7;SS#)=`}P| zk-h7PLy|)Q9nyuV)d&9qIvIzudKg@@^E=F>7pa{EpR!4v&#EgO_KSr!DJkZ)XdJlz zpU`p_=tg=eD%1Dwjh+1rQ~n}4oka_7*wZoK+!QyXRh5UFNsJ>|^3E{AUP|wB{=Q#1 zN7$nS0xjni7;en+7~AIzd9akLx@2&cGk zn19ZvAk@x7=m_u8dfWlAA>V%kZ!V}XxNIM&NVq{U%jOq((wgvGZi~!t%_G0LF#KE! zC2&$T^X6!nf*0O-I6 zP{OZi`@kXfzlUE$>4ojX_|3~=}ACk50Qb|sfhE-TwKDr`Z<@i&{#o4U+z zL@ks+I%w?B6(b|H;0-{dcUTXu{ZsJ*Kn^%G>=kuL>+m7=KY?Gw8C;_0aENN!?iTmO zOKrsSpW>3uU`EY%Uh_Xfdluh}&#ueam%3_hrg1tQ#HY2swWHB#zHO0b%g(waUh=7oUfBrdRjJh^#>R8XO}5o7i*}$ zNRFC}nqq(f?Iu>Wn!sS+Pae`JuWRHVGgd{by>|zo^M91<2nRe}bhv`<*#s4Gcc_tz zlBql2fFqKx!xY(56gr_pT)fVrBPeZc<#4*n#J7(*nm6>t(B;^iVf;iUKRHfUMg9@Q z9G!%)(}*0#A3vJibgVO_DsmsL4nZ&P`J?nz|1o58_&$i3iz(zo!y!F+;1REo-#|xG zK0OcaqK6?#arJhh>C+Q-YsOFr)3Iper10OQ+`zHQ?zR|2l%GK~NrX(TvTX(R?)V#r zcvQR@MWO-Qq)Cba%$4N`TXI~)(GfQ1!pP?oR=z)1+8a=saBCDrdcX5YkcOi9kO@{t ze-8#Cy7=n8iPHrGfb?dM(&K3SB0P;p53%fo3}0pYtE^(8%es(E)tG4JV|d?)&pTa1TO~K#3wK8KoL@0ZP^b ztw)*7q`wnR4Sxj^Q@ID>zhqaCNNV;z$4}fxxmkTZ#dxIq$E$`xZ!?y^HnB6CeF(W!DZDID}3hV}~(+0lUM{f1&Rhe+^{U$a}o=;aGEb zm*?liVwlhCY9LM|*u+OA$0S+BjaTo=-RMj-N6De;GLPz#3+Ug=?w;iCR0tjJ0N%~>dZ#bk;Bg_!Lqi%mU6jIlb!)j) zOJh?G5oH@#T*`)W$SGZD=M3qY7KyfPYwxKD;Fw6rrIsXr0u z`|GHg&$;{MWb#KF0zW^|L8GtDA-y8^L=RnE!?v2k<6 zjv-9Th97n|4+TdgRXr=G-LoAgvd180cb2cd+s%vNM+*ZU0Y>tT8AXYSzjW>2E1pA; zK<0?trLqnpxB#$lFRPv3-+^Nt0w|7IulZKmJHSZqnJ!2Y07)i0;P2Hx8d)C-stOt#s%ZVz$~=+!)R=ey3c(4aTM)V zRaPyxpDwtH&#wN%&49CJUPkF@*XZtL@gZz(AM&F7n9VG|i{T`q1`7ju^h6%=RS(~v zOa1Mr4u1#s^!GTFX5xn*{T1Kk+XF*gMkxG}z2ofp#SvEkC=O%ORbBr)D(Cg-=aY12 zlW|dHWijVoxJAKV3Wdqps^$x|J1$!*H{M!aSBnHEH7R7z@2ozt$%0d2dOJKEbRH8! z)jj=_`6nz&?%7_$AYdS@10g7t6^ZB@o>$D*eBav*(aFy>TS997)NV_7CjY$1FrfzX_i4Sx%>37z?hGDV@$ZJxFGI`FO5{H zoETewvuWb{F@J>GC0FIJ{Nain(b4|}e#0n1|COLUhq4bNb$CwzK&*I;y(q5gAiGGP z<=#4OuAYg`kH73Z^T3JuZJk>*hwa^GSk>CPDBwx2(wkC6dT$B{(mT>oK#<;hN2N)Z zB1pA>Qbk1tX#yfeKtQU1fFL3rQF@hrzqr=gYcKac=dOLvpL3sw=*yTS8Oa#m%$ym? zNJ7VnX%AFKcyy;->T7pp1D)w=#^^76Ke+F^yzG#g;;wy*?Zh4&nBIL$V}$Kda=vU* zu;w9ojxMsMHEGgbkNt^WIS&i5JMM&@4O8E-ZjV{rXyp&}PI<_~d!lO1dZ8vfy>22Z zBR$e_E@hcP|HNUISF_p2Q^ZRJJG&l&yG_#sJ{`nTdFqNvRy1!@?<_PhD+i{xzuafm zxY_ZgdRvd7YmXtQWzCG>xMvOR^aUE8JmHSny$3#~ygILH(;_sS6Ym^r!-_F7q3V0A zS^2PO%}C*`fla0Hclh!-M>yD>=3jRbBP{D(?D0byiOl;QF^QTs*AB|WN=t{YrN4HF zPwI3q>MpCxClTzBrc}v28M=pkcGOQ6<4^-LTfK=y&zyVfL!{kycQ>8#nrW*!)hXkw zE}g^Yr>a%ky)7n0M$R!6F*P#YywrGZ@z(7ey~;MWv>#X;YWp--mR4ix*b^lc5*m5) zN7O|_jqY@I2ZWK)r#}6}^6vXi*>{%@U*SPFwk*simYv=&n-$-P`_jCWZAIZnLm(wg zG-;I9neR&(%Mj8R*ZY`gv1FPMLtkId3NDCDIj>%8)%bbQGPs7^MuCu3M?kP#qREKi zRfb)=OF@;}P`L0;0zpvS*vrUuM$wb2{OiM=Lt8j;1HC(qenw_o#THZhiR+Bl@C96# zt`dBH8~kSVQ#}R^?lnsJd(Z787Y>_VoxYEmqc2;te_U~pSjf>(?~#9^Rzj;*Y(QJJ zv_$X{CHlFz)MqiYaoV9{j49vMxkG942pd`k2}Zh7rLpXlcdq~o!@^^P;oio=v$(-M;n>(#^T>ipmMRrbe8qR@GH=bj zCAdPTl?fHqw(Trm@`#!kJkNgdA@X>OJ@W*ImZ&Z*-PPp->epn;MOlKujXOnz=hmOl zzTlRto{nczNoJ5!z%FdbT-)Q~w{)O9?ba`Mc%`7uCsBU(`q@l6e}#$g6?vmz8Mm^G z?wFiNp_*p}LvypG``3up`>LO;G+zz8YMp!7K|54aM1Z}%kSX9)?-lPHwv7j~I0z$tj=u1m z&Wj2{Hw&g`c1t9Y$8L0mnOc)a%?M)~cF>*5{3wv8k;B#cjo{$6R~hYn3J1DKs>$)D z(%=Vo4;qi%P+48gzSGLMClD079cx|KC)YZ3-uqjYoODN!bcSX&)5CG2)RTu~PTLs0 zd^$8Qemv+jOOSpwwk1n5YV(2Qz|zS3(IL?_ReuYA!mstYVyoR$72^e(qTS75M0QhY zw~})ok0&UF3YCi+_}mFMV%tUU2W9Kf zRJGhvgv#i3u8VQ1qF89 z`{%j`G}hNLuUm=6nG58Mr3DBzH-@km$Dg38SG{2H-Z+WlEUlG3dA{CuU#V&P!&4b- zTP|luf&-Qg@Kee~qfoGO;YkV>5LiCeDDgg4carC$cfS;Bl$A*q=k?fNukJAYp!yv< zbt)c?!GUk9Bcvw|mlAJQ+Hid7J11hIo%y-b)rEpKA(Kp%n13*d$|A)yCF65jNqfLBa zTP?QJ~7AW3)!2iK?0j&ezBz zvb(zOdXJ8DNUs*j^N}5&TYC^3K7MMIo6o?7ySmCJn?Ba`R**MAc2Mr+&ZlSlA8yn0 zi;vS72WjLCad8<1)7Mm-jXEPgmvg8ll{+l9N-{>RDJ{wEs;%`ggF`X&ke<%X?+KA! zW^VXbf|!DroB1QhcW*swWI0F!FX)N7H{-~ssgs0Nn?a0}=C!lZ0rox*RHfIyg3jX9%=9L)a`hW+mSA* zskArh75j+%e2n#ay&IO!=G|!_PAa9+64$npcM8FCGOmhw-&AqV+|Z8f4fQ{eD2bex zRS6QCVMyg0FwWl!p}t_+LfkO%l_T0~pZvDA1uOim=z`pmm(2%Ld&0rA+RIcGM*SbC zg?5dKSKf~_J|PO&J0Eg_-tS;yL7N3lTu0=QPO{hT;D&4I$3X5FZhK+djPvSu(?Tv9 zUCb0ZINwh-{muBReE4ma+)@#S=UH{y$LmTsVoF~<=%41~Kb7;=l50=#nq|w%O8k~Q zdKghi+<8s~iTx$|e7`sE^n6$9C$xwbZ8%G;BO>K2w$*2pZxL`{wI;Hc7FTS3$I+D1 z5V&@eXV&Z$VGb(?d9z-Cq&;0($rK%i%VK?q@3FfL7xv?dgW^vSeZLd+fxzx*CM!j5 z-zh?#LrdZ$_#^N-2WND2W}aT#&K7^?<{67k?pe*fY-t~m6LoWbmnAy!x`DfjHBZz5 zE!}9sNai60!z0zozWwg-wo?B>UT>N*yDR!9L<8;wI?oIRy!|rYuPo&f@4f4N>k3p5bWx?~AXqRq}-RXO~lTmbqLnYi- zklx$W@bT{K_hs>T<>)|~hdx~f+%)_xo1ZvTeaSd*mcw?inTB{Vj)F`FWd!>F((#=vr%7;WFk!pxcI_vDU z686$~!5=&A^T7`M8N7}bt--Atj|_TJ?Ste|-Q3#Sd6Sm&Y8qmf4o@q_+EJw!i60t& zK!e@lrZY?xWo~N5O5H~2yA^WnjYgw@>%X_!Z%(Y2)7`_Xiyx=-HBG9jERk`LSArnNU~w#X^E>LIfs(@5RMVBgSEdb#eronlA1 zWQHOsrze@h0NT9#6?YP9CH5XwJ?gae57uCEYra9CA}DgaE8oUPusg`6i09oS$K|J)^)bn;wn9C( zZdP=4*W_BNEd0p{g3nW&;Y#pI1&#LLyuJSLp%T6U$i@(Q?=ZMc1I&rr<3M#k73bvOjK5S$3*C+RO!grZ?FmOYR|PhcxKM+g;CznN^gs zNNy(U2q_i3nr5I+R=1f=Rd2&34x{gKoh&YCvD_4{3>>EPxo|LNG`?+$UrGuc-cN4peEGhUE7p;N69Uv>R+G3pNA-PB1> z4;EMIg-dSa^nvPbeLIu0cQQ?G7+RpqL?{+;$`h@Z#Ilbbw}~-fg+C(e>p-fE^*uP| zweKks8+e47TKdP=ZTJTca1X}7JNG+G_(^656J9|&g}q6v>IEj$v(pb$2?_X^@?P<- zbzj;hrS4Wyi19#;42Y^8tbIM3z}Trj8d4xIescKbbH)eRfyZjyU(w>{2g&3r+eK@8 zXy*`?U@olQET85viaHzKi}v!o>nnA8x(~;OvTh5#%NDWin(@D<;G?5eVq`jCSMp6U z$J^v!BAvefQ}x0lqk9>b6&9Fp_GncypOWk%D_WV{`*6jHaH?0Nko#lxO&%r6V(-M( zygM??r7wbmIW4}g5z(V7Xo@OEpVYnoNOXFrN)!G-dXH6g>9NS+3$By7lAFz&#}g)> zbD&lmB(g6hvQ|m8yb>_x0nWoplO5vTi3E3SZmF~rf`)|(}yB>~j32Q%?xDs>ffo4MP zQs}&D%ycIYZ*2bt9`>M)cVuENW|g)Ai*4UtPMaF7R&E&qDO1!A-QD>LlEj>*0G`!L zriQn8JX0z}Pgse*9dlI4!`hrPVtA;iH!;*dO_;E?Y0S822&ZEDaR_VEG@`##O8Aq9 z+-R3LNt9@<@yKp&&@!Uo)|aWP>*Wuqx4-Q+_75#QxclgQk!FcA+1?=<4VL6p%ft(I zC>T@~H5Cfw%tN)+F)%4m*eF~WcM`?6G7wINhC-zRVi;rX>BVpD=I82Z{u_MNeu zr5V(U`o4=~E-;9O+^!jka;qh|P2IQ<+O~mC7aMktD~R_@6^gi7G6jq5Y{_@Qr^gCo z(S@sd@xun<)|#^`lbKexGsu!UW`mk$h0RZupYo|%st)m2+fBso;h7re4A>VW!XPu- ze>h*~k4sl~Hvj{LIy}Tc{WW|AY(%xMkw8ND=zkBNnWv4j7r(oMmA8+l4a5+MBOfv@ zJVDm6QGi5oD`G^pLn`gQBFEbj(d;o76(Ru@E2E|pc&fY`3#VGyLS$~QNgtleXFS~F z3gWb(Hk2JKW4hrZ5dS>%`?UWd9y7Idn3K8g3!O-hkAdrUmx@2=n3{Ct=!$V0szsaW z+Sc4^83|W0Nf&2Hc=+I5+l`&bw5e>D1fg@n=2t9Y-f^fT&&im)H0(SqZIqPIc$5_^ z>2WppR+oVvmSu@P#T6kUMvFm(mrPNnwUTE>pklvO8x5To_2+hT>CW>8LYPK_jzW<` z#s4}O%e%RH+qin0>0foX@w$veKwJH<3&3rp$@>7gl3^hPf609g#{3q;pHcfYV?XIr zWp;QozTluxWPizc7RrKbzkdz=Z~5_mpYtT@*0*(;v)L#Vg5odv6S02F?_y)^V8QR; zVqs?^$mi~A_iG7${mlHikp29B`2_u%`PVJx&&-mKf6M$|Za>=USlGzy*zhkLf+YOp zx6CBV$Y=RFP9@$gid*=^D9q(+cqmi}CjQUy=*UEXIVd4ao z3fUU|&HJB{*s0beK+>`lxhKEZsWRndMst;Xmyc)g?i%`i8TFRto4{wV zn>k!L^~B(l1BI~S%TdlsK5mk!GSPtze5nDgxAeH0UkT$!6GQ0*pWSPZy;eie#9U3! zO{0syrCY*aFUJ?ibesQv0(rh zIarer^;oirc&9$*-FJt?$jyeW}zMAiIDP2GZ~ zh{Y3Sp&gb2-5eSkb=AyEvGkl-A{iV58r|aNPplZc<^11-tmnH_oISLasW30d6~2|= zit#N#tA8(w{&gqM$n9p&_aj@QwK*6zVfV0!Dvn-lkPt1`re zC)W~M3RrNmHL|VMa)~k2!l$^nr}!QQkMjEKsm0j{Sv^c*ewfhkK7qRKz|*w*yvA~g zNa@*!{!ZVG>fC;q>URWam*1*vPldg_&eBdfm6A$358VYDX$HdP{TRw5)A|%i=WnUL zWQU%H4!YKy^t#MOSht-lNb5CIW18OE3VW1QG{5W`I`nwpuz5YsbbBLBB{w!D=rZ5r z+ppK|uBY9)_Rc(Uooeu%k=t?NM}sTYSC137e973dmz=!xL&UX&FVcQbp5nrrav`n? z#>#>nu5JRp9T&P*DK)uGss5tDNgE-?1ooP3mb+U?+heaprD;3;`<}M5RK;~+uh?(j z+~n`tm6}<^qhNZORsL0+Df_Fy26~~{`_l|IXC!7`eX~3~xtd9_l({T_Y3fd|^>r=G zZ<6^U@%fYXB0g`h1FH0HB$v*KHsie5g<0dd}!0 z8VAs&hsu zEcHV>@mP_}BhA>013EAG*PDsDc5EoSF4 zTG2qMhu$L5N-w6rt=~1uCb)=QF*v08!_oDvY>M9$nj~$APs+R?M}AK!w_R^F9;JJ9 zTC?lC73Zk&F=BJk)lm~YYSOQAT={P2%t;2&-e4ptDn)b%u-CM<(Q8j%#PCi0QoJzp zBPOca7vs`fuTp&)p4;(XN|IA zYD@qpi+x(6$MDAMzH+v&z8B7%4z9XCd*^(7V88}>&8F`sleC@1uOXk$q(8Frh!%N3 zY$g+2WI1%EOeh`grq3N$28~&@U4AD2FZzlP4;r+Un^}c49W4As{XSaB@S`^_?+!Fl zdfzL28`uB#996tNEx(Q@f7z!P!;3VR`D<>Jz4n>gG&YjNb00izqm&ZyDM?JFyjN}7 zRH@sLuvV6!GS`hl%lv~?UK_JlV=LnbI^}g%^LDWl14NDf8XHD7<9<`ia@&ik?@b>K zn4G#SSW3))+^@ep&wJF-_Bmsig{@k?H0S0E)(WlrSoEV<{l-dmMywFAL87uRAhvm!k+Aa zvP#cIM(sZx_0Z=kw_aA6qU>#f4GST_`blnS9}e0d**rNaa{HI+-f>>5r_Mndbn4D{lBu1i1BN%Jsr-vj&b&m20uTWXG*_BX~V)WHQHSo(Ij-W zrF2p&K3*O5!RNFhO{>F>b90UaX+~A2ye=2tSVQa6J$_HS4SPxLy!7CgOZ6^-W={qa zlB8>Rs0jkK+l!7Z;dum>XC};59+w~USn7^pjBPnDLmV1#^`l|x^Sm{CENwP5l{s@Y zCs8WB>1C;I!Hw>#i|?y9Yw#am*Q8NAxzC;{789jPhVGOt{YGq2j}f!cnq);xT=r8# zP-|4MPde4c4}pW{mWRA@Vp4Z>fW0PyAhPs6`y5d7LO`gO1PXKp@tkN#;N z`D?~s_bGp7eaMqBdYoG^A|>!*4AxL#+3B z+7v(VNKnf-OfdHF@DQ1Znw^`9&`;-a|OONWqPa6l-KrV=vU93YKXdjL%d)E$6?@dbcp1*#R0 z0p#ccY4+~{$d1B=%*P<5fE4-2GeJrUDF>v;@<`ZGCMxaIvs3F|qJ* zaIkR+@d*hD@CgWx5s{G`BO)atARwV4Atk4vq@*Myrlz5ypdq86q(Byeh5>mnvGB04 z@F>XS=-p!**iFTdHeYKUAcNK zEIcAI>PB=-QgTXa+U+}c@8#s?JVi6f-sJ@wzbSXVlH=>iH* zr!=yDjP3~zowuYNMt3da*Ao@-(q(un^b5(uH(FhmPFn76vtKz_K>06FP6y-esXiTX zy7u7a7de@cytZMB$Q>=e8OItXBxq7Vazd$v zM@WXCFLBWxW9V_wnfC{D-$$tid0d@0W?q-9p`M$;c1V6S=QToOF)AyUN067HpOg?6;9c9F+4qbKLF5#*ICr z$3OgZUkA#Gk?EGSP7j%iQxegv>Mt&RJ!?JqOdCy+;%~u82?+YMpPrW#Je`-hgIlD~ z=bBTz{Q2SArdSHWOQ&APT|qIi46rxxW4xPmG8UV7rGBx(*X=$5HYVD~T?5L%}1 zL@lZU9amoW+fG~u>q${d-r{ehBYf|)Q5VKLdd$-JCiZmJ)$GWt1j;^THuuiF zTr;58)a?6~OX=C~M9R;0O*8D7W0Kj3-0;innLJ(|%|Wcad=pL@%lfcNcV(*|X3Nx3 z8}}W*7zUE-Cbg-M39YnnE7^7J5k47zv~7dDjjt0+9WUgej# z9?9(OGp=u@3T2tRX$rrO^OYBTeSB|p;f#}d>%^K>ab?nk=nd;M<&RZjRtdK=h)o4k z#Z_4@C28ERsK4EpzN>oP`Sio)ZENSW8xLY6%;T>eqVU+W7%EKt$Cog+g9H-Zp1qjO zxOsV^tX3=K&3zV=)Y+`hmSOe6B#z&5)Ldn@172CC*6Nsup7PiD(H0Qr2x6!4L7pS zGSQM$?`6;(smZprRTkc9$JHkZNV!zb9xu{9L`B$)^Y2itckf|oE$1Fk-VU$(YSJgY zOex)vIlySX=~T5AbtAiS*)63S6QNeIW-;`_ zJ*oE#>smy^U%oLg^_VpYbF~NiSva_nov%k3L=Y)q3uh#EW1s@D{<|9SJMR(?ZD%t6 zNEqu553peED)Z4=@CZyimv-NaD4C+C7-uW?#FINA!?re(Q(i_7I4f3)BKM>z`wNTm zzvI~+T*!}fohYsr3=2O*sYMXVPcwQe)YJw{)b{;%HNE|El}JmtIS6y_T3tVXcF*4B z9My64*qkR$j(xoIK^D#^zLTs1Up|}Xk&c|$5vuwW9A!#fcAqUwrY|Q=xXN1H2TSX* zSYc#wO4a>NvXU2NI*KX{F&tUwNB$)S#D_zFv=)!jqX-;D;D2=l{=4IU^#0EN3_e{JZEReBE6YR$ z=Fc)AA&;b%Q%Jsn`rK?GBN zAIo&!!Oq_MXMFT+Tr7~Y6k4{n4pufO8kp(N6BbN=oYnA#vl=KuSUeIo6oI1%97W(L z0!I-zioj6>jv{arfujf6k|P+mZfeq{kb%1_0@^N3PpPuJK3u?U8c`lmO|wNBZ}XPVeIY zxwVxR&;jJ$Y^2{Bt}RC~fs=q4oB}L>6|e!g1>ooP|D2F=0dBwpcmW^a2Lb@OeqRU( z0}*fZffH~BF2EJI0e9d5 zJb@SR20p+S_<<|nD!2yxK>!E@K_D1h2O%I7gn@7n0U|*ZxB;R;42T7B;3kL%3E&n; z1W6znq<~bA25y5p;4Zia(m@8e4<3L_kOi_q4#)+0;33EdkHBN_1QdX$;2C%h3PBNg z0g6EhCr<xMU%?#s2EK!NumBdp z5?BT+U=^%^b+7?8!4~)dw!seA1$$s09DqZBTpWYk+JO$>)&UeIzyjC+2O#(9-~oI< z0FcKF90SPhLd1XskOJhf732W9pNA4q0ct=4j)N0`7SI8DzyRPLjGy=YoP-p){qGcD z0jz)xumj|NAWpyqxB(B~1;}k^{6GK*0wEv_M8Iiq28aSNAP&v~2_OlOXBtQWX&?h+ zfgF$r3P2Gk0cD^9RDl{mPK%xg8bA|h0d1fIbOCa0l0LWqE&>B!2#mlbU<^#aWnc=- zfH|-LmcR;F0~=rq?0`LR0FJ;3I0F~p3fzD@@Bp5`3wQ$`;0ye~6>t??1O6ZY1cD$C z46cI^5DLOTIEVm|APU?7(I5uIf;ey!#DfHI3nYRhkPK2lDo6vj!5wfH+ym(#1KbA> zKqkln*&qkxf;{jLG?)Ri;47E|-@tb;4;H{8SOUvn1+0QKunsoBCfEW$z&6+c zyI>FOg9C5~kR3l7KnEBA6JP;sfCF#=9>50#fDjx5M1UBO08&5($N>eQ1XO?;Ade(D z4o(1CKnLgn17HM9;3QxMrvM9J1#EyFZ~)}aQ7*s@cmOZp1N=Y$2m&D>3`D?ba0ZA1 zF(3|*$B{??NpKEG0cjuukf(3R0ePSR6oC>@1}Z=mr~!3w9%ukfparyn4$uX9Kp$KH z7l8pV1V-QzFa{<7*)B~XH3R0r0$2hoU=5JzwvgHZd*A>ZffH~B$aGgo-GDpr0G_}L zcmrhm{=eb>`!tyJmq8a=5^m6PV1ky9E2N%3TMWp~;qMJpu*djgff$g5h%7 zH_WZkxfwnC=8uC&4qYQs1qYJzjKO4`GU_r~#2U>M5C<|EYRcLRz zLi^7RUL#|?Av$h9+k5}jayelA$l|cT->v`Ue_FpbEZ^^EjgeOlzehmLHvd%rpN04z{`h0ve>eXB!`1yu J_W#NH{|`DlvSI)L literal 0 HcmV?d00001 From 4cd8cf542bb1d45fe96df77ef35b91da49f72d30 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Fri, 10 May 2019 17:00:26 +0200 Subject: [PATCH 54/88] Makefile: remove superfluous variable CXXOMPFLAGS Remove the superfluous variable CXXOMPFLAGS, and instead, add OpenMP flags to the variable CXXFLAGS. This simplifies the Makefile and reduces the risk of issues such as https://github.com/cp2k/cp2k/issues/338 occuring. --- Makefile | 2 +- Makefile.inc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 2db3a2bf1d6..f0b83b6d510 100644 --- a/Makefile +++ b/Makefile @@ -497,7 +497,7 @@ FYPPFLAGS ?= -n $(CXX) -c $(CXXFLAGS) $< libcusmm.o: libcusmm.cpp parameters.h cusmm_kernels.h - $(CXX) -c $(CXXFLAGS) $(CXXOMPFLAGS) -DARCH_NUMBER=$(ARCH_NUMBER) $< + $(CXX) -c $(CXXFLAGS) -DARCH_NUMBER=$(ARCH_NUMBER) $< %.o: %.cu $(NVCC) -c $(NVFLAGS) -I'$(SRCDIR)' $< diff --git a/Makefile.inc b/Makefile.inc index eeb0cdeb509..3c1633021ff 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -147,7 +147,7 @@ else endif ifneq (0,$(OMP)) # using OpenMP - CXXOMPFLAGS = -fopenmp + CXXFLAGS += -fopenmp FCFLAGS += -fopenmp endif From 1a2b7721eb4b6b9508145605d6ac4960523c1cbe Mon Sep 17 00:00:00 2001 From: shoshijak Date: Wed, 24 Apr 2019 06:29:29 -0400 Subject: [PATCH 55/88] Workaround race condition on ORNL's Summit Occurs when running tests/dbcsr_tensor_unittest built with MPI, OpenMP and CUDA support. Credit goes to Joost VandeVondele @vondele --- src/acc/dbcsr_acc_hostmem.F | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/acc/dbcsr_acc_hostmem.F b/src/acc/dbcsr_acc_hostmem.F index 65f87095f59..3eb0cc49fca 100644 --- a/src/acc/dbcsr_acc_hostmem.F +++ b/src/acc/dbcsr_acc_hostmem.F @@ -129,6 +129,9 @@ SUBROUTINE acc_hostmem_dealloc_raw(host_mem_c_ptr, stream) INTEGER :: istat TYPE(C_PTR) :: stream_cptr +! Workaround for a segmentation fault on ORNL's Summit +!$OMP CRITICAL + IF (.NOT. acc_stream_associated(stream)) & DBCSR_ABORT("acc_hostmem_dealloc_raw: stream not associated") @@ -137,6 +140,7 @@ SUBROUTINE acc_hostmem_dealloc_raw(host_mem_c_ptr, stream) istat = cuda_host_mem_dealloc_cu(host_mem_c_ptr, stream_cptr) IF (istat /= 0) & DBCSR_ABORT("acc_hostmem_dealloc_raw: Could not deallocate host pinned memory") +!$OMP END CRITICAL END SUBROUTINE acc_hostmem_dealloc_raw #endif From 61f1a00577eac0bcee1cca9e6a24fbb632af3530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Tue, 14 May 2019 10:14:42 +0200 Subject: [PATCH 56/88] cmake/libcusmm: fix building with OpenMP required for #183 --- src/acc/libsmm_acc/libcusmm/CMakeLists.txt | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/acc/libsmm_acc/libcusmm/CMakeLists.txt b/src/acc/libsmm_acc/libcusmm/CMakeLists.txt index a8e622ed4af..d83ae514e23 100644 --- a/src/acc/libsmm_acc/libcusmm/CMakeLists.txt +++ b/src/acc/libsmm_acc/libcusmm/CMakeLists.txt @@ -30,11 +30,21 @@ add_library(libcusmm OBJECT cusmm_kernels.h ) -# use legacy directives here due to missing target_include_directories support -# for object libraries with CMake < 3.12 -include_directories( +if (OpenMP_FOUND) + # with CMake 3.12+ the following can be replaced by: + # target_link_libraries(libcusmm PRIVATE OpenMP::OpenMP_CXX) + target_compile_options(libcusmm PRIVATE + $ + ) +endif () + +target_include_directories(libcusmm PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) -include_directories(SYSTEM ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}) -add_definitions(-DARCH_NUMBER=${CUDA_ARCH_NUMBER}) + +target_include_directories(libcusmm SYSTEM PRIVATE + ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES} + ) + +target_compile_definitions(libcusmm PRIVATE -DARCH_NUMBER=${CUDA_ARCH_NUMBER}) From 619ccd1c70bba54c4df3aa2c5bdf33bd87d0c2b5 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Fri, 10 May 2019 14:19:17 +0200 Subject: [PATCH 57/88] Check consistency dbcsr-libcusmm when building with openmp In DBCSR's initialisation, if DBCSR is compiled with OpenMP support, add a call checking whether libcusmm was compiled with OpenMP support as well. This is meant to prevent issues such as https://github.com/cp2k/cp2k/issues/338 from arising in the future --- src/acc/libsmm_acc/include/libsmm_acc.h | 2 ++ src/acc/libsmm_acc/libcusmm/libcusmm.cpp | 10 ++++++++++ src/core/dbcsr_lib.F | 20 +++++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/acc/libsmm_acc/include/libsmm_acc.h b/src/acc/libsmm_acc/include/libsmm_acc.h index 6dbcc0d4527..abfdff4882b 100644 --- a/src/acc/libsmm_acc/include/libsmm_acc.h +++ b/src/acc/libsmm_acc/include/libsmm_acc.h @@ -18,6 +18,8 @@ int libsmm_acc_process (void *param_stack, int stack_size, int libsmm_acc_transpose (void *trs_stack, int offset, int nblks, void *buffer, int datatype, int m, int n, void* stream); +bool libsmm_acc_libcusmm_is_thread_safe (); + #ifdef __cplusplus } #endif diff --git a/src/acc/libsmm_acc/libcusmm/libcusmm.cpp b/src/acc/libsmm_acc/libcusmm/libcusmm.cpp index ba6ae8b7540..f00f57574df 100644 --- a/src/acc/libsmm_acc/libcusmm/libcusmm.cpp +++ b/src/acc/libsmm_acc/libcusmm/libcusmm.cpp @@ -361,3 +361,13 @@ extern "C" int libsmm_acc_transpose (void *trs_stack, int offset, int nblks, voi return 0; // maximum size over any dimention return libcusmm_transpose_d((int *) trs_stack, offset, nblks, (double *) buffer, m, n, *((CUstream *) stream)); } + + +//=========================================================================== +extern "C" bool libsmm_acc_libcusmm_is_thread_safe () { +#if defined _OPENMP + return true; +#else + return false; +#endif +} diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index c3a5ab7b0dc..861d8ff151d 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -77,6 +77,18 @@ MODULE dbcsr_lib MODULE PROCEDURE dbcsr_init_lib_hooks END INTERFACE +#if defined (__DBCSR_ACC) + INTERFACE + FUNCTION libsmm_acc_libcusmm_is_thread_safe_cu() & + RESULT(thread_safe) & + BIND(C, name="libsmm_acc_libcusmm_is_thread_safe") + IMPORT + INTEGER :: thread_safe + + END FUNCTION libsmm_acc_libcusmm_is_thread_safe_cu + END INTERFACE +#endif + CONTAINS ! ************************************************************************************************** @@ -152,7 +164,7 @@ SUBROUTINE dbcsr_init_lib_pre(mp_comm, io_unit) INTEGER, INTENT(IN) :: mp_comm INTEGER, INTENT(IN), OPTIONAL :: io_unit - INTEGER :: numnodes, mynode + INTEGER :: numnodes, mynode, thread_safe IF (PRESENT(io_unit)) THEN ext_io_unit = io_unit @@ -164,6 +176,12 @@ SUBROUTINE dbcsr_init_lib_pre(mp_comm, io_unit) CALL dbcsr_mp_make_env(mp_env, default_group, mp_comm) #if defined(__LIBXSMM) CALL libxsmm_init() +#endif +#if defined(__DBCSR_ACC) +!$ thread_safe = libsmm_acc_libcusmm_is_thread_safe_cu() +!$ IF (thread_safe /= 0) then +!$ DBCSR_ABORT("libcusmm not compiled to be thread-safe. Recompile with OpenMP support") +!$ endif #endif END SUBROUTINE dbcsr_init_lib_pre From 6721ee89b0461385befca5ffabc0554e7db2c9f1 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Mon, 13 May 2019 09:19:09 +0200 Subject: [PATCH 58/88] Fix unused variable --- src/core/dbcsr_lib.F | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 861d8ff151d..24cfe7dbff9 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -164,7 +164,11 @@ SUBROUTINE dbcsr_init_lib_pre(mp_comm, io_unit) INTEGER, INTENT(IN) :: mp_comm INTEGER, INTENT(IN), OPTIONAL :: io_unit - INTEGER :: numnodes, mynode, thread_safe + INTEGER :: numnodes, mynode + +#if defined(__DBCSR_ACC) +!$ INTEGER :: thread_safe +#endif IF (PRESENT(io_unit)) THEN ext_io_unit = io_unit From 3bd4c2c5b4d99b8a67291b02d5f818702a0805c2 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Mon, 13 May 2019 13:53:26 +0200 Subject: [PATCH 59/88] Threading level consistency check: fix 1) Change libsmm_acc_libcusmm_is_thread_safe return itype from bool to int. 2) Also abort if DBCSR is not threaded but libcusmm is --- src/acc/libsmm_acc/include/libsmm_acc.h | 2 +- src/acc/libsmm_acc/libcusmm/libcusmm.cpp | 6 +++--- src/core/dbcsr_lib.F | 26 ++++++++++++++++++------ 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/acc/libsmm_acc/include/libsmm_acc.h b/src/acc/libsmm_acc/include/libsmm_acc.h index abfdff4882b..be710576fe6 100644 --- a/src/acc/libsmm_acc/include/libsmm_acc.h +++ b/src/acc/libsmm_acc/include/libsmm_acc.h @@ -18,7 +18,7 @@ int libsmm_acc_process (void *param_stack, int stack_size, int libsmm_acc_transpose (void *trs_stack, int offset, int nblks, void *buffer, int datatype, int m, int n, void* stream); -bool libsmm_acc_libcusmm_is_thread_safe (); +int libsmm_acc_libcusmm_is_thread_safe (); #ifdef __cplusplus } diff --git a/src/acc/libsmm_acc/libcusmm/libcusmm.cpp b/src/acc/libsmm_acc/libcusmm/libcusmm.cpp index f00f57574df..f9273bc7b75 100644 --- a/src/acc/libsmm_acc/libcusmm/libcusmm.cpp +++ b/src/acc/libsmm_acc/libcusmm/libcusmm.cpp @@ -364,10 +364,10 @@ extern "C" int libsmm_acc_transpose (void *trs_stack, int offset, int nblks, voi //=========================================================================== -extern "C" bool libsmm_acc_libcusmm_is_thread_safe () { +extern "C" int libsmm_acc_libcusmm_is_thread_safe () { #if defined _OPENMP - return true; + return 1; // i.e. true, libcusmm is threaded #else - return false; + return 0; // i.e. false, libcusmm is not threaded #endif } diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 24cfe7dbff9..b1cb9b9a49b 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -58,6 +58,10 @@ MODULE dbcsr_lib !$ USE OMP_LIB, ONLY: omp_get_thread_num, omp_get_num_threads +#if defined (__DBCSR_ACC) + USE ISO_C_BINDING, ONLY: C_INT +#endif + IMPLICIT NONE PRIVATE @@ -83,7 +87,7 @@ FUNCTION libsmm_acc_libcusmm_is_thread_safe_cu() & RESULT(thread_safe) & BIND(C, name="libsmm_acc_libcusmm_is_thread_safe") IMPORT - INTEGER :: thread_safe + INTEGER(KIND=C_INT) :: thread_safe END FUNCTION libsmm_acc_libcusmm_is_thread_safe_cu END INTERFACE @@ -167,7 +171,7 @@ SUBROUTINE dbcsr_init_lib_pre(mp_comm, io_unit) INTEGER :: numnodes, mynode #if defined(__DBCSR_ACC) -!$ INTEGER :: thread_safe + INTEGER :: dbcsr_thread_safe, libcusmm_thread_safe #endif IF (PRESENT(io_unit)) THEN @@ -181,11 +185,21 @@ SUBROUTINE dbcsr_init_lib_pre(mp_comm, io_unit) #if defined(__LIBXSMM) CALL libxsmm_init() #endif + #if defined(__DBCSR_ACC) -!$ thread_safe = libsmm_acc_libcusmm_is_thread_safe_cu() -!$ IF (thread_safe /= 0) then -!$ DBCSR_ABORT("libcusmm not compiled to be thread-safe. Recompile with OpenMP support") -!$ endif + libcusmm_thread_safe = libsmm_acc_libcusmm_is_thread_safe_cu() ! 0: not threaded, 1: threaded + dbcsr_thread_safe = 0 ! not threaded +!$ dbcsr_thread_safe = 1 ! if DBCSR is compiled with openmp, set to threaded + ! Check whether DBCSR and libcusmm (GPU backend) have the same level of threading + IF (dbcsr_thread_safe /= libcusmm_thread_safe) then + IF (dbcsr_thread_safe /= 0) then + DBCSR_ABORT("DBCSR's and libcusmm's threading levels do not match. DBCSR is compiled +with threading support while libcusmm is compiled without threading support.") + ELSE + DBCSR_ABORT("DBCSR's and libcusmm's threading levels do not match. DBCSR is compiled +without threading support while libcusmm is compiled with threading support.") + ENDIF + ENDIF #endif END SUBROUTINE dbcsr_init_lib_pre From b16bc890acc1a0338fc7a80bf21f21c06265126e Mon Sep 17 00:00:00 2001 From: Alfio Lazzaro Date: Wed, 15 May 2019 19:49:47 +0200 Subject: [PATCH 60/88] Remove MacOS compilation by default --- Makefile.inc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Makefile.inc b/Makefile.inc index 3c1633021ff..5a4c9f70405 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -230,7 +230,5 @@ endif ####################################################################################### # # Enable for MacOS compilation (-D__ACCELERATE is for Apple Accelerate library) - -FCFLAGS += -D__NO_STATM_ACCESS -D__ACCELERATEa - +#FCFLAGS += -D__NO_STATM_ACCESS -D__ACCELERATE ####################################################################################### From 2403e81957b144df6b0e1da336a0bd5bb21a1fd7 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Wed, 15 May 2019 10:36:24 +0200 Subject: [PATCH 61/88] README: add missing cmake options to documentation --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 5dc5160d164..7fa016df56d 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,13 @@ The configuration flags are (default first): -DUSE_OPENMP= -DUSE_SMM= -DUSE_CUDA= + -DUSE_CUBLAS= -DWITH_C_API= -DWITH_EXAMPLES= -DWITH_GPU= -DTEST_MPI_RANKS= + -DTEST_OMP_THREADS=<2,N> + -DCMAKE_BUILD_TYPE= ## Contributing to DBCSR From ea6af6e10ff2d75277a3fad63036f50c1d6f4bc5 Mon Sep 17 00:00:00 2001 From: shoshijak Date: Wed, 15 May 2019 11:59:45 +0200 Subject: [PATCH 62/88] cmake: add support for OpenMPI fortran compiler wrappers --- CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d5b054c0a91..952e908fc00 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,13 @@ if (Fortran_COMPILER_NAME MATCHES "ftn") list(GET ftn_verbose_OUT 0 Fortran_COMPILER_NAME) endif () +# if the fortran compiler command is an OPENMPI wrapper, find out what the underlying compiler is +if (Fortran_COMPILER_NAME MATCHES "mpif*") # mpif77, mpif90 or mpifort + execute_process(COMMAND ${Fortran_COMPILER_NAME} --showme OUTPUT_VARIABLE mpif_verbose_OUT ERROR_QUIET) + separate_arguments(mpif_verbose_OUT NATIVE_COMMAND "${mpif_verbose_OUT}") + list(GET mpif_verbose_OUT 0 Fortran_COMPILER_NAME) +endif () + # Depending on the fortran compiler, set the appropriate compiler flags if (Fortran_COMPILER_NAME MATCHES "^gfortran.*") set(CMAKE_CXX_FLAGS "-std=c++11") From 535a5a965956116ca06c492371652f9faff84bbf Mon Sep 17 00:00:00 2001 From: Hans Pabst Date: Tue, 21 May 2019 21:00:16 +0200 Subject: [PATCH 63/88] Use mpi_comm_set_errhandler instead of mpi_errhandler_set (conditional wrt __MPI_VERSION). (#188) --- src/mpi/dbcsr_mpiwrap.F | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mpi/dbcsr_mpiwrap.F b/src/mpi/dbcsr_mpiwrap.F index 7853f6e6984..2c8d4ec5ebe 100644 --- a/src/mpi/dbcsr_mpiwrap.F +++ b/src/mpi/dbcsr_mpiwrap.F @@ -690,7 +690,7 @@ MODULE dbcsr_mpiwrap TYPE(mp_perf_env_type), POINTER :: mp_perf_env => Null() END TYPE mp_perf_env_p_type - ! introduce a stack of mp_perfs, first index is the stack pointer, for convience is replacing + ! introduce a stack of mp_perfs, first index is the stack pointer, for convenience is replacing INTEGER, PARAMETER :: max_stack_size = 10 INTEGER :: stack_pointer = 0 ! target attribute needed as a hack around ifc 7.1 bug @@ -760,7 +760,11 @@ SUBROUTINE mp_world_init(mp_comm) !$ ENDIF !$OMP END MASTER !$ ENDIF +#if __MPI_VERSION > 2 + CALL mpi_comm_set_errhandler(MPI_COMM_WORLD, MPI_ERRORS_RETURN, ierr) +#else CALL mpi_errhandler_set(MPI_COMM_WORLD, MPI_ERRORS_RETURN, ierr) +#endif IF (ierr /= 0) CALL mp_stop(ierr, "mpi_errhandler_set @ mp_world_init") mp_comm = MPI_COMM_WORLD debug_comm_count = 1 @@ -2014,7 +2018,7 @@ END SUBROUTINE mp_comm_split_direct ! ************************************************************************************************** !> \brief splits the given communicator in group in subgroups trying to organize !> them in a way that the communication within each subgroup is -!> efficent (but not necessarily the communication between subgroups) +!> efficient (but not necessarily the communication between subgroups) !> \param comm the mpi communicator that you want to split !> \param sub_comm the communicator for the subgroup (created, needs to be freed later) !> \param ngroups actual number of groups From 8514e87352b2dd40fe20dd41f3788f8a96125fb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Tue, 4 Jun 2019 10:37:24 +0200 Subject: [PATCH 64/88] switch to F2008 (#136) * check for F2008 support * cmake: add more F2008 compiler tests * cmake: drop separate F2008 compile flags --- CMakeLists.txt | 13 +++++------ README.md | 2 +- cmake/CheckCompilerSupport.cmake | 20 +++++++++++++++++ .../compiler-tests/f2008-block_construct.f90 | 14 ++++++++++++ cmake/compiler-tests/f2008-contiguous.f90 | 22 +++++++++++++++++++ cmake/compiler-tests/f2008-norm2.f90 | 14 ++++++++++++ src/CMakeLists.txt | 2 +- 7 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 cmake/CheckCompilerSupport.cmake create mode 100644 cmake/compiler-tests/f2008-block_construct.f90 create mode 100644 cmake/compiler-tests/f2008-contiguous.f90 create mode 100644 cmake/compiler-tests/f2008-norm2.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 952e908fc00..fa33c33b66f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,11 +110,10 @@ if (Fortran_COMPILER_NAME MATCHES "^gfortran.*") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -funroll-loops") set(CMAKE_CXX_FLAGS_COVERAGE "-O0 -fprofile-arcs -ftest-coverage") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -ggdb") - set(CMAKE_Fortran_FLAGS "-ffree-form -ffree-line-length-none -std=f2003") + set(CMAKE_Fortran_FLAGS "-ffree-form -ffree-line-length-none -std=f2008ts") set(CMAKE_Fortran_FLAGS_RELEASE "-O3 -funroll-loops") set(CMAKE_Fortran_FLAGS_DEBUG "-O0 -ggdb") set(CMAKE_Fortran_FLAGS_COVERAGE "-O0 -fprofile-arcs -ftest-coverage") - set(F2008_COMPILER_FLAGS "-std=f2008ts") # on Apple we may compile with GCC Fortran, but build/link C/C++ code with Apple's clang, requiring special treatment if (APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "AppleClang") @@ -126,22 +125,20 @@ if (Fortran_COMPILER_NAME MATCHES "^gfortran.*") elseif (Fortran_COMPILER_NAME MATCHES "^ifort.*") set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -debug") - set(CMAKE_Fortran_FLAGS "-free -stand f03 -fpp") + set(CMAKE_Fortran_FLAGS "-free -stand f08 -fpp") # Disable the line-length-extension warning #5268 set(CMAKE_Fortran_FLAGS_RELEASE "-O3 -diag-disable=5268") set(CMAKE_Fortran_FLAGS_DEBUG "-O0 -debug") - set(F2008_COMPILER_FLAGS "-stand f08") elseif (Fortran_COMPILER_NAME MATCHES "^pgf.*") set(CMAKE_CXX_FLAGS_RELEASE "-fast") set(CMAKE_CXX_FLAGS_DEBUG "-g") - set(CMAKE_Fortran_FLAGS "-Mfreeform -Mextend -Mallocatable=03") + set(CMAKE_Fortran_FLAGS "-Mfreeform -Mextend -Mallocatable=03") # -Mallocatable=03: enable F2003+ assignment semantics set(CMAKE_Fortran_FLAGS_RELEASE "-fast") set(CMAKE_Fortran_FLAGS_DEBUG "-g") - set(F2008_COMPILER_FLAGS "") else () message(WARNING "\ Unknown compiler, trying with just '-O2'/'-O0 -g'.\n\ -You will most likely have to pass additonal options for free-form Fortran 2003/2008 for your compiler!\n\ +You will most likely have to pass additonal options for free-form Fortran 2008 for your compiler!\n\ Please open an issue at https://github.com/cp2k/dbcsr/issues with the reported compiler name and the required flags.") message("-- Fortran_COMPILER_NAME: " ${Fortran_COMPILER_NAME}) message("-- CMAKE_Fortran_COMPILER full path: " ${CMAKE_Fortran_COMPILER}) @@ -150,6 +147,8 @@ Please open an issue at https://github.com/cp2k/dbcsr/issues with the reported c set(CMAKE_Fortran_FLAGS_DEBUG "-O0 -g") endif () +include(CheckCompilerSupport) + file(STRINGS VERSION VERSION_INFO) foreach(line ${VERSION_INFO}) if (${line} MATCHES "^([^#].*)=[ \t]*(.*)$") diff --git a/README.md b/README.md index 7fa016df56d..168e8d1b515 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ It is MPI and OpenMP parallel and can exploit GPUs via CUDA. You absolutely need: * GNU make -* a Fortran compiler which supports at least Fortran 2003 (respectively 2008+TS when using the C-bindings) +* a Fortran compiler which supports at least Fortran 2008 (including the TS 29113 when using the C-bindings) * a LAPACK implementation (reference, OpenBLAS-bundled and MKL have been tested. Note: DBCSR linked to OpenBLAS 0.3.6 gives wrong results on Power9 architectures.) * a BLAS implementation (reference, OpenBLAS-bundled and MKL have been tested) * a Python version installed (2.7 or 3.6+ have been tested) with Numpy diff --git a/cmake/CheckCompilerSupport.cmake b/cmake/CheckCompilerSupport.cmake new file mode 100644 index 00000000000..acfc4b78059 --- /dev/null +++ b/cmake/CheckCompilerSupport.cmake @@ -0,0 +1,20 @@ + +include(CheckFortranSourceCompiles) + +set(CHECK_PROGRAMS + f2008-norm2.f90 + f2008-block_construct.f90 + f2008-contiguous.f90 + ) + +foreach (prog ${CHECK_PROGRAMS}) + get_filename_component(prog_ext ${prog} EXT) # get the src extension to pass along + get_filename_component(prog_name ${prog} NAME_WE) + + file(READ "${CMAKE_CURRENT_LIST_DIR}/compiler-tests/${prog}" prog_src) + check_fortran_source_compiles("${prog_src}" "${prog_name}" SRC_EXT "${prog_ext}") + + if (NOT ${prog_name}) + message(FATAL_ERROR "Your compiler does not support all required F2008 features") + endif () +endforeach () diff --git a/cmake/compiler-tests/f2008-block_construct.f90 b/cmake/compiler-tests/f2008-block_construct.f90 new file mode 100644 index 00000000000..a84160b1a36 --- /dev/null +++ b/cmake/compiler-tests/f2008-block_construct.f90 @@ -0,0 +1,14 @@ +!--------------------------------------------------------------------------------------------------! +! Copyright (C) by the DBCSR developers group - All rights reserved ! +! This file is part of the DBCSR library. ! +! ! +! For information on the license, see the LICENSE file. ! +! For further information please visit https://dbcsr.cp2k.org ! +! SPDX-License-Identifier: GPL-2.0+ ! +!--------------------------------------------------------------------------------------------------! + +program main + try: block + exit try + end block try +end program diff --git a/cmake/compiler-tests/f2008-contiguous.f90 b/cmake/compiler-tests/f2008-contiguous.f90 new file mode 100644 index 00000000000..24609622dc1 --- /dev/null +++ b/cmake/compiler-tests/f2008-contiguous.f90 @@ -0,0 +1,22 @@ +!--------------------------------------------------------------------------------------------------! +! Copyright (C) by the DBCSR developers group - All rights reserved ! +! This file is part of the DBCSR library. ! +! ! +! For information on the license, see the LICENSE file. ! +! For further information please visit https://dbcsr.cp2k.org ! +! SPDX-License-Identifier: GPL-2.0+ ! +!--------------------------------------------------------------------------------------------------! + +program main + implicit none + + ! test whether the compiler supports the CONTIGUOUS keyword + integer, allocatable, target :: targ(:) + integer, contiguous, pointer :: ptr(:) + + ! allocated data is always contiguous + allocate(targ(10)) + ptr => targ + + ! IS_CONTIGUOUS was implemented in gcc-9 and is therefore not tested for yet +end program diff --git a/cmake/compiler-tests/f2008-norm2.f90 b/cmake/compiler-tests/f2008-norm2.f90 new file mode 100644 index 00000000000..42b8ab5a70a --- /dev/null +++ b/cmake/compiler-tests/f2008-norm2.f90 @@ -0,0 +1,14 @@ +!--------------------------------------------------------------------------------------------------! +! Copyright (C) by the DBCSR developers group - All rights reserved ! +! This file is part of the DBCSR library. ! +! ! +! For information on the license, see the LICENSE file. ! +! For further information please visit https://dbcsr.cp2k.org ! +! SPDX-License-Identifier: GPL-2.0+ ! +!--------------------------------------------------------------------------------------------------! + +program main + implicit none + real :: x(2) = [ real :: 3, 4 ] + if (abs(norm2(x) - 5.) > 1.0D-5) stop 1 +end program diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 60db7319165..ca962d89933 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -247,7 +247,7 @@ if (WITH_C_API) add_library(dbcsr_c ${DBCSR_C_SRCS}) set_target_properties(dbcsr_c PROPERTIES LINKER_LANGUAGE Fortran - COMPILE_FLAGS "${F2008_COMPILER_FLAGS}") + ) target_link_libraries(dbcsr_c PRIVATE dbcsr) target_link_libraries(dbcsr_c PUBLIC MPI::MPI_C) # the C API always needs MPI target_include_directories(dbcsr_c PUBLIC From 35baef850026becb87ba55e990f2282603554d1c Mon Sep 17 00:00:00 2001 From: shoshijak Date: Mon, 3 Jun 2019 14:46:20 +0200 Subject: [PATCH 65/88] libcusmm: fix cray compiler warnings --- src/acc/libsmm_acc/libcusmm/generate_kernels.py | 1 + src/acc/libsmm_acc/libcusmm/generate_parameters.py | 1 + src/acc/libsmm_acc/libcusmm/libcusmm.cpp | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/acc/libsmm_acc/libcusmm/generate_kernels.py b/src/acc/libsmm_acc/libcusmm/generate_kernels.py index 7aab18b2e6a..ce518f4c35c 100755 --- a/src/acc/libsmm_acc/libcusmm/generate_kernels.py +++ b/src/acc/libsmm_acc/libcusmm/generate_kernels.py @@ -82,6 +82,7 @@ def main(kernels_folder): file_h += "\n" + separator + cpp_function_to_string(kernel, kernel_name) + "\n" file_h += "#endif\n" file_h += "//EOF" + file_h += "\n\n" # Write file_h_path = "cusmm_kernels.h" diff --git a/src/acc/libsmm_acc/libcusmm/generate_parameters.py b/src/acc/libsmm_acc/libcusmm/generate_parameters.py index c467eec48d1..13bfa0a4ef2 100755 --- a/src/acc/libsmm_acc/libcusmm/generate_parameters.py +++ b/src/acc/libsmm_acc/libcusmm/generate_parameters.py @@ -102,6 +102,7 @@ def write_parameters_file(all_pars): #endif //EOF +\n\n """ return out, all_pars diff --git a/src/acc/libsmm_acc/libcusmm/libcusmm.cpp b/src/acc/libsmm_acc/libcusmm/libcusmm.cpp index f9273bc7b75..3eab299cc02 100644 --- a/src/acc/libsmm_acc/libcusmm/libcusmm.cpp +++ b/src/acc/libsmm_acc/libcusmm/libcusmm.cpp @@ -198,7 +198,7 @@ void add_kernel_handle_to_jitted_kernels(CUfunction kern_func, CUstream stream, //=========================================================================== int libcusmm_process_d(int *param_stack, int stack_size, CUstream stream, int m, int n, int k, double *a_data, double *b_data, double *c_data){ - CUfunction kern_func; + CUfunction kern_func = NULL; int threads, grouping; Triplet h_mnk = { m, n, k }; From 202139e38d6c6d3c4ed5fb13d3d068847cb21948 Mon Sep 17 00:00:00 2001 From: Alfio Lazzaro Date: Tue, 4 Jun 2019 10:45:19 +0200 Subject: [PATCH 66/88] Update Makefile.inc to 2008ts As part of #136 --- Makefile.inc | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Makefile.inc b/Makefile.inc index 5a4c9f70405..90bacc00fa6 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -137,7 +137,7 @@ endif ifneq (0,$(GNU)) # DEFAULT, just edit... CXXFLAGS += -std=c++11 - FCFLAGS += -std=f2003 -ffree-form -fimplicit-none -ffree-line-length-512 + FCFLAGS += -std=f2008ts -ffree-form -fimplicit-none -ffree-line-length-512 OPTFLAGS += -O3 -g -fno-omit-frame-pointer -funroll-loops else # Intel compiler flags @@ -157,14 +157,6 @@ CXXFLAGS += $(OPTFLAGS) FCFLAGS += $(OPTFLAGS) LDFLAGS += $(FCFLAGS) -####################################################################################### -# -# C interface requires new F2008ts standard - -ifeq ($(CINT),1) -FCFLAGS := $(subst -std=f2003,-std=f2008ts,$(FCFLAGS)) -endif - ####################################################################################### # # Macro for MPI parallelization. From ddb56b24f70b86c1a71fa0d64c3e981c8e14cc8a Mon Sep 17 00:00:00 2001 From: Alfio Lazzaro Date: Tue, 4 Jun 2019 10:48:48 +0200 Subject: [PATCH 67/88] Update Makefile example to 2008ts standard --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 3450a376590..946305a59dc 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -6,7 +6,7 @@ CXX = mpicxx FLAGS = -O3 -fopenmp INCLUDES = -I$(DBCSRDIR)/include FCFLAGS = $(FLAGS) $(INCLUDES) -fno-omit-frame-pointer \ - -funroll-loops -ffree-form -std=f2003 -fimplicit-none + -funroll-loops -ffree-form -std=f2008ts -fimplicit-none CXXFLAGS = $(FLAGS) $(INCLUDES) -std=c++11 LIBS = -L$(DBCSRDIR)/lib -ldbcsr LIBS += -L${LAPACK_PATH}/lib -llapack -lblas From 5c82e4ef978658a0fed03e67377ccd661d3cb182 Mon Sep 17 00:00:00 2001 From: Hans Pabst Date: Tue, 21 May 2019 20:13:50 +0200 Subject: [PATCH 68/88] Since DBCSR moved to F2008, the CONTIGUOUS keyword can be used. MPI requires contiguous buffers hence the wrapper can safely make that statement. In fact, this fixes occurrences of "created temporary array" (unveiled with related GNU Fortran flags). This change can be taken further by adding more uses of the CONTIGUOUS even in the MPI wrapper module (or anywhere else). --- src/mpi/dbcsr_mpiwrap.f90 | 64 +++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/src/mpi/dbcsr_mpiwrap.f90 b/src/mpi/dbcsr_mpiwrap.f90 index eac73621b7f..33bf9603745 100644 --- a/src/mpi/dbcsr_mpiwrap.f90 +++ b/src/mpi/dbcsr_mpiwrap.f90 @@ -22,7 +22,7 @@ ! ***************************************************************************** SUBROUTINE mp_shift_${nametype1}$m(msg, group, displ_in) - ${type1}$, INTENT(INOUT) :: msg(:, :) + ${type1}$, CONTIGUOUS, INTENT(INOUT) :: msg(:, :) INTEGER, INTENT(IN) :: group INTEGER, INTENT(IN), OPTIONAL :: displ_in @@ -79,7 +79,7 @@ END SUBROUTINE mp_shift_${nametype1}$m ! ***************************************************************************** SUBROUTINE mp_shift_${nametype1}$ (msg, group, displ_in) - ${type1}$, INTENT(INOUT) :: msg(:) + ${type1}$, CONTIGUOUS, INTENT(INOUT) :: msg(:) INTEGER, INTENT(IN) :: group INTEGER, INTENT(IN), OPTIONAL :: displ_in @@ -144,10 +144,10 @@ END SUBROUTINE mp_shift_${nametype1}$ ! ***************************************************************************** SUBROUTINE mp_alltoall_${nametype1}$11v(sb, scount, sdispl, rb, rcount, rdispl, group) - ${type1}$, DIMENSION(:), INTENT(IN) :: sb - INTEGER, DIMENSION(:), INTENT(IN) :: scount, sdispl - ${type1}$, DIMENSION(:), INTENT(INOUT) :: rb - INTEGER, DIMENSION(:), INTENT(IN) :: rcount, rdispl + ${type1}$, CONTIGUOUS, INTENT(IN) :: sb(:) + INTEGER, CONTIGUOUS, INTENT(IN) :: scount(:), sdispl(:) + ${type1}$, CONTIGUOUS, INTENT(INOUT) :: rb(:) + INTEGER, CONTIGUOUS, INTENT(IN) :: rcount(:), rdispl(:) INTEGER, INTENT(IN) :: group CHARACTER(len=*), PARAMETER :: routineN = 'mp_alltoall_${nametype1}$11v', & @@ -197,12 +197,10 @@ END SUBROUTINE mp_alltoall_${nametype1}$11v ! ***************************************************************************** SUBROUTINE mp_alltoall_${nametype1}$22v(sb, scount, sdispl, rb, rcount, rdispl, group) - ${type1}$, DIMENSION(:, :), & - INTENT(IN) :: sb - INTEGER, DIMENSION(:), INTENT(IN) :: scount, sdispl - ${type1}$, DIMENSION(:, :), & - INTENT(INOUT) :: rb - INTEGER, DIMENSION(:), INTENT(IN) :: rcount, rdispl + ${type1}$, CONTIGUOUS, INTENT(IN) :: sb(:, :) + INTEGER, CONTIGUOUS, INTENT(IN) :: scount(:), sdispl(:) + ${type1}$, CONTIGUOUS, INTENT(INOUT) :: rb(:, :) + INTEGER, CONTIGUOUS, INTENT(IN) :: rcount(:), rdispl(:) INTEGER, INTENT(IN) :: group CHARACTER(len=*), PARAMETER :: routineN = 'mp_alltoall_${nametype1}$22v', & @@ -251,8 +249,8 @@ END SUBROUTINE mp_alltoall_${nametype1}$22v ! ***************************************************************************** SUBROUTINE mp_alltoall_${nametype1}$ (sb, rb, count, group) - ${type1}$, DIMENSION(:), INTENT(IN) :: sb - ${type1}$, DIMENSION(:), INTENT(OUT) :: rb + ${type1}$, CONTIGUOUS, INTENT(IN) :: sb(:) + ${type1}$, CONTIGUOUS, INTENT(OUT) :: rb(:) INTEGER, INTENT(IN) :: count, group CHARACTER(len=*), PARAMETER :: routineN = 'mp_alltoall_${nametype1}$', & @@ -293,8 +291,8 @@ END SUBROUTINE mp_alltoall_${nametype1}$ ! ***************************************************************************** SUBROUTINE mp_alltoall_${nametype1}$22(sb, rb, count, group) - ${type1}$, DIMENSION(:, :), INTENT(IN) :: sb - ${type1}$, DIMENSION(:, :), INTENT(OUT) :: rb + ${type1}$, CONTIGUOUS, INTENT(IN) :: sb(:, :) + ${type1}$, CONTIGUOUS, INTENT(OUT) :: rb(:, :) INTEGER, INTENT(IN) :: count, group CHARACTER(len=*), PARAMETER :: routineN = 'mp_alltoall_${nametype1}$22', & @@ -969,13 +967,13 @@ END SUBROUTINE mp_bcast_${nametype1}$3 !> mpi_allreduce ! ***************************************************************************** SUBROUTINE mp_sum_${nametype1}$ (msg, gid) - ${type1}$, INTENT(INOUT) :: msg - INTEGER, INTENT(IN) :: gid + ${type1}$, INTENT(INOUT) :: msg + INTEGER, INTENT(IN) :: gid CHARACTER(len=*), PARAMETER :: routineN = 'mp_sum_${nametype1}$', & routineP = moduleN//':'//routineN - INTEGER :: handle, ierr, msglen + INTEGER :: handle, ierr, msglen ierr = 0 CALL timeset(routineN, handle) @@ -999,7 +997,7 @@ END SUBROUTINE mp_sum_${nametype1}$ !> \note see mp_sum_${nametype1}$ ! ***************************************************************************** SUBROUTINE mp_sum_${nametype1}$v(msg, gid) - ${type1}$, INTENT(INOUT) :: msg(:) + ${type1}$, CONTIGUOUS, INTENT(INOUT) :: msg(:) INTEGER, INTENT(IN) :: gid CHARACTER(len=*), PARAMETER :: routineN = 'mp_sum_${nametype1}$v', & @@ -1081,7 +1079,7 @@ END SUBROUTINE mp_isum_${nametype1}$v !> \note see mp_sum_${nametype1}$ ! ***************************************************************************** SUBROUTINE mp_sum_${nametype1}$m(msg, gid) - ${type1}$, INTENT(INOUT) :: msg(:, :) + ${type1}$, CONTIGUOUS, INTENT(INOUT) :: msg(:, :) INTEGER, INTENT(IN) :: gid CHARACTER(len=*), PARAMETER :: routineN = 'mp_sum_${nametype1}$m', & @@ -1123,7 +1121,7 @@ END SUBROUTINE mp_sum_${nametype1}$m !> \note see mp_sum_${nametype1}$ ! ***************************************************************************** SUBROUTINE mp_sum_${nametype1}$m3(msg, gid) - ${type1}$, INTENT(INOUT) :: msg(:, :, :) + ${type1}$, CONTIGUOUS, INTENT(INOUT) :: msg(:, :, :) INTEGER, INTENT(IN) :: gid CHARACTER(len=*), PARAMETER :: routineN = 'mp_sum_${nametype1}$m3', & @@ -1154,7 +1152,7 @@ END SUBROUTINE mp_sum_${nametype1}$m3 !> \note see mp_sum_${nametype1}$ ! ***************************************************************************** SUBROUTINE mp_sum_${nametype1}$m4(msg, gid) - ${type1}$, INTENT(INOUT) :: msg(:, :, :, :) + ${type1}$, CONTIGUOUS, INTENT(INOUT) :: msg(:, :, :, :) INTEGER, INTENT(IN) :: gid CHARACTER(len=*), PARAMETER :: routineN = 'mp_sum_${nametype1}$m4', & @@ -1190,7 +1188,7 @@ END SUBROUTINE mp_sum_${nametype1}$m4 !> mpi_reduce ! ***************************************************************************** SUBROUTINE mp_sum_root_${nametype1}$v(msg, root, gid) - ${type1}$, INTENT(INOUT) :: msg(:) + ${type1}$, CONTIGUOUS, INTENT(INOUT) :: msg(:) INTEGER, INTENT(IN) :: root, gid CHARACTER(len=*), PARAMETER :: routineN = 'mp_sum_root_${nametype1}$v', & @@ -1238,7 +1236,7 @@ END SUBROUTINE mp_sum_root_${nametype1}$v !> \note see mp_sum_root_${nametype1}$v ! ***************************************************************************** SUBROUTINE mp_sum_root_${nametype1}$m(msg, root, gid) - ${type1}$, INTENT(INOUT) :: msg(:, :) + ${type1}$, CONTIGUOUS, INTENT(INOUT) :: msg(:, :) INTEGER, INTENT(IN) :: root, gid CHARACTER(len=*), PARAMETER :: routineN = 'mp_sum_root_rm', & @@ -1283,16 +1281,16 @@ END SUBROUTINE mp_sum_root_${nametype1}$m !> \param[in] gid Message passing environment identifier ! ***************************************************************************** SUBROUTINE mp_sum_partial_${nametype1}$m(msg, res, gid) - ${type1}$, INTENT(IN) :: msg(:, :) - ${type1}$, INTENT(OUT) :: res(:, :) - INTEGER, INTENT(IN) :: gid + ${type1}$, CONTIGUOUS, INTENT(IN) :: msg(:, :) + ${type1}$, CONTIGUOUS, INTENT(OUT) :: res(:, :) + INTEGER, INTENT(IN) :: gid - CHARACTER(len=*), PARAMETER :: routineN = 'mp_sum_partial_${nametype1}$m' & - , routineP = moduleN//':'//routineN + CHARACTER(len=*), PARAMETER :: routineN = 'mp_sum_partial_${nametype1}$m', & + routineP = moduleN//':'//routineN - INTEGER :: handle, ierr, msglen + INTEGER :: handle, ierr, msglen #if defined(__parallel) - INTEGER :: taskid + INTEGER :: taskid #endif ierr = 0 @@ -1452,7 +1450,7 @@ END SUBROUTINE mp_min_${nametype1}$v !> mpi_allreduce ! ***************************************************************************** SUBROUTINE mp_prod_${nametype1}$ (msg, gid) - ${type1}$, INTENT(INOUT) :: msg + ${type1}$, INTENT(INOUT) :: msg INTEGER, INTENT(IN) :: gid CHARACTER(len=*), PARAMETER :: routineN = 'mp_sum_${nametype1}$', & From 517ae8af6bed4ecbcabb349c7eccfc631edfcb04 Mon Sep 17 00:00:00 2001 From: Hans Pabst Date: Tue, 4 Jun 2019 13:48:51 +0200 Subject: [PATCH 69/88] One more CONTIGUOUS applied to MPI-wrapper. --- src/mpi/dbcsr_mpiwrap.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mpi/dbcsr_mpiwrap.f90 b/src/mpi/dbcsr_mpiwrap.f90 index 33bf9603745..2d8566d9aa2 100644 --- a/src/mpi/dbcsr_mpiwrap.f90 +++ b/src/mpi/dbcsr_mpiwrap.f90 @@ -590,7 +590,7 @@ END SUBROUTINE mp_alltoall_${nametype1}$54 ! ***************************************************************************** !> \brief Send one datum to another process -!> \param[in] msg Dum to send +!> \param[in] msg Scalar to send !> \param[in] dest Destination process !> \param[in] tag Transfer identifier !> \param[in] gid Message passing environment identifier @@ -1419,7 +1419,7 @@ END SUBROUTINE mp_min_${nametype1}$ !> \note see mp_min_${nametype1}$ ! ***************************************************************************** SUBROUTINE mp_min_${nametype1}$v(msg, gid) - ${type1}$, INTENT(INOUT) :: msg(:) + ${type1}$, CONTIGUOUS, INTENT(INOUT) :: msg(:) INTEGER, INTENT(IN) :: gid CHARACTER(len=*), PARAMETER :: routineN = 'mp_min_${nametype1}$v', & From 98484c2df942f77a44cfab55aa24682285deb8e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Tue, 18 Jun 2019 14:38:08 +0200 Subject: [PATCH 70/88] tests: enable OpenMP within tests, drop unused OMP directives by courtesy for the NAG compiler --- tests/CMakeLists.txt | 8 +++++++- tests/dbcsr_unittest1.F | 2 -- tests/dbcsr_unittest2.F | 2 -- tests/dbcsr_unittest3.F | 2 -- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cac9817793e..bd2eda5fb0d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,6 +15,9 @@ set(DBCSR_PERF_SRCS dbcsr_performance_multiply.F) add_executable(dbcsr_perf ${DBCSR_PERF_SRCS}) target_link_libraries(dbcsr_perf dbcsr) +if (OpenMP_FOUND) + target_link_libraries(dbcsr_perf PRIVATE OpenMP::OpenMP_Fortran) +endif () file(GLOB DBCSR_PERF_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "inputs/*.perf" @@ -71,7 +74,10 @@ foreach (dbcsr_test ${DBCSR_TESTS}) else () add_test(NAME ${dbcsr_test} COMMAND ./${dbcsr_test}) endif () - set_tests_properties(${dbcsr_test} PROPERTIES ENVIRONMENT OMP_NUM_THREADS=${TEST_OMP_THREADS}) + if (OpenMP_FOUND) + target_link_libraries(${dbcsr_test} PRIVATE OpenMP::OpenMP_Fortran) + set_tests_properties(${dbcsr_test} PROPERTIES ENVIRONMENT OMP_NUM_THREADS=${TEST_OMP_THREADS}) + endif () endforeach () if (USE_CUDA) diff --git a/tests/dbcsr_unittest1.F b/tests/dbcsr_unittest1.F index 6e508283bba..6584fcf2206 100644 --- a/tests/dbcsr_unittest1.F +++ b/tests/dbcsr_unittest1.F @@ -40,8 +40,6 @@ PROGRAM dbcsr_unittest USE dbcsr_types, ONLY: dbcsr_mp_obj #include "base/dbcsr_base_uses.f90" -!$ USE OMP_LIB, ONLY: omp_get_max_threads, omp_get_thread_num, omp_get_num_threads - IMPLICIT NONE INTEGER :: mp_comm, group, numnodes, mynode, & diff --git a/tests/dbcsr_unittest2.F b/tests/dbcsr_unittest2.F index d34b697758c..c4ff4fe6f7f 100644 --- a/tests/dbcsr_unittest2.F +++ b/tests/dbcsr_unittest2.F @@ -39,8 +39,6 @@ PROGRAM dbcsr_unittest USE dbcsr_types, ONLY: dbcsr_mp_obj #include "base/dbcsr_base_uses.f90" -!$ USE OMP_LIB, ONLY: omp_get_max_threads, omp_get_thread_num, omp_get_num_threads - IMPLICIT NONE INTEGER :: mp_comm, group, numnodes, mynode, & diff --git a/tests/dbcsr_unittest3.F b/tests/dbcsr_unittest3.F index d81b8f91bb9..58b9020b17d 100644 --- a/tests/dbcsr_unittest3.F +++ b/tests/dbcsr_unittest3.F @@ -40,8 +40,6 @@ PROGRAM dbcsr_unittest USE dbcsr_types, ONLY: dbcsr_mp_obj #include "base/dbcsr_base_uses.f90" -!$ USE OMP_LIB, ONLY: omp_get_max_threads, omp_get_thread_num, omp_get_num_threads - IMPLICIT NONE INTEGER :: mp_comm, group, numnodes, mynode, & From e0f13f13b1045c812b30d4ebb1275f2668c2bd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Tue, 18 Jun 2019 15:03:25 +0200 Subject: [PATCH 71/88] cmake/tests: only use generic target_link_libraries --- tests/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bd2eda5fb0d..2331d6ba373 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,7 +16,7 @@ set(DBCSR_PERF_SRCS add_executable(dbcsr_perf ${DBCSR_PERF_SRCS}) target_link_libraries(dbcsr_perf dbcsr) if (OpenMP_FOUND) - target_link_libraries(dbcsr_perf PRIVATE OpenMP::OpenMP_Fortran) + target_link_libraries(dbcsr_perf OpenMP::OpenMP_Fortran) endif () file(GLOB DBCSR_PERF_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -75,7 +75,7 @@ foreach (dbcsr_test ${DBCSR_TESTS}) add_test(NAME ${dbcsr_test} COMMAND ./${dbcsr_test}) endif () if (OpenMP_FOUND) - target_link_libraries(${dbcsr_test} PRIVATE OpenMP::OpenMP_Fortran) + target_link_libraries(${dbcsr_test} OpenMP::OpenMP_Fortran) set_tests_properties(${dbcsr_test} PROPERTIES ENVIRONMENT OMP_NUM_THREADS=${TEST_OMP_THREADS}) endif () endforeach () From 7995f8f42ca9bf4321a1fdaeaf46e0167fb39567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Tue, 18 Jun 2019 16:43:57 +0200 Subject: [PATCH 72/88] cmake/tests: add OpenMP to libcusmm tests for convenience as well --- tests/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2331d6ba373..21cf0534a0a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -112,11 +112,17 @@ if (USE_CUDA) ) add_executable(${libcusmm_test} ${CMAKE_CURRENT_BINARY_DIR}/${libcusmm_test}.cu) target_link_libraries(${libcusmm_test} dbcsr) + if (OpenMP_FOUND) + target_link_libraries(${libcusmm_test} OpenMP::OpenMP_CXX) + endif () endforeach () # Add executables for test files that do not need to be generated by a template add_executable(libcusmm_unittest_transpose ${CMAKE_CURRENT_SOURCE_DIR}/libcusmm_unittest_transpose.cu) target_link_libraries(libcusmm_unittest_transpose dbcsr) + if (OpenMP_FOUND) + target_link_libraries(libcusmm_unittest_transpose OpenMP::OpenMP_CXX) + endif () # Add tests that do not need additional arguments foreach (libcusmm_test ${LIBCUSMM_SIMPLE_TESTS}) From b076dee13c0c13f6a4e42f82c0d9328a914feb29 Mon Sep 17 00:00:00 2001 From: Alfio Lazzaro Date: Tue, 18 Jun 2019 17:43:02 +0200 Subject: [PATCH 73/88] Enable OpenMP for unittests files --- tests/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 21cf0534a0a..aad10885d6b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -59,6 +59,10 @@ set(dbcsr_test_csr_conversions_SRCS dbcsr_test_csr_conversions.F) # but we would need cmake 3.12 to be able to specify target_link_libraries on those to get # the proper compile flags add_library(dbcsr_unittest_common STATIC ${dbcsr_unittest_common_SRCS}) +if (OpenMP_FOUND) + target_link_libraries(dbcsr_unittest_common OpenMP::OpenMP_Fortran) +endif () + if (APPLE AND BLAS_LIBRARIES MATCHES "Accelerate") target_compile_definitions(dbcsr_unittest_common PRIVATE __ACCELERATE) endif() From 39074f92791f22c5bbb7537bb531d00f8c9c428b Mon Sep 17 00:00:00 2001 From: alazzaro Date: Tue, 18 Jun 2019 18:14:08 +0200 Subject: [PATCH 74/88] Add gfortran for homebrew --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ec14ba007c3..d51bcd8059c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ addons: homebrew: packages: - gcc + - gfortran - cmake - openmpi From 2b1e1473c106b66f44684e77ae0c7fac92a70288 Mon Sep 17 00:00:00 2001 From: alazzaro Date: Tue, 18 Jun 2019 18:40:41 +0200 Subject: [PATCH 75/88] Revert "Add gfortran for homebrew" This reverts commit 39074f92791f22c5bbb7537bb531d00f8c9c428b. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d51bcd8059c..ec14ba007c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,6 @@ addons: homebrew: packages: - gcc - - gfortran - cmake - openmpi From a731704b68e64f25db647c00233bddcdc9d0d985 Mon Sep 17 00:00:00 2001 From: Hans Pabst Date: Sat, 22 Jun 2019 22:02:11 +0200 Subject: [PATCH 76/88] Avoid temporary array when expanding/filling into the first row of an 2d-array. --- src/block/dbcsr_index_operations.F | 50 +++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/block/dbcsr_index_operations.F b/src/block/dbcsr_index_operations.F index 163fdf38be3..0847bc476b0 100644 --- a/src/block/dbcsr_index_operations.F +++ b/src/block/dbcsr_index_operations.F @@ -77,6 +77,11 @@ MODULE dbcsr_index_operations PUBLIC :: dbcsr_addto_index_array, dbcsr_clearfrom_index_array, & dbcsr_repoint_index, dbcsr_make_index_exist + INTERFACE dbcsr_expand_row_index + MODULE PROCEDURE dbcsr_expand_row_index_1d, & + dbcsr_expand_row_index_2d + END INTERFACE + INTERFACE dbcsr_count_row_index MODULE PROCEDURE dbcsr_count_row_index_copy, & dbcsr_count_row_index_inplace @@ -244,7 +249,7 @@ END SUBROUTINE make_index_triangular ! ************************************************************************************************** SUBROUTINE dbcsr_make_dbcsr_index(row_p, row_i, nrows, nblks) INTEGER, INTENT(in) :: nrows, nblks - INTEGER, DIMENSION(1:nrows + 1), INTENT(out) :: row_p + INTEGER, DIMENSION(1:nrows + 1), INTENT(out) :: row_p INTEGER, DIMENSION(1:nblks), INTENT(in) :: row_i CHARACTER(len=*), PARAMETER :: routineN = 'dbcsr_make_dbcsr_index', & @@ -283,17 +288,38 @@ END SUBROUTINE dbcsr_make_dbcsr_index !> \param nrows ... !> \param nblks ... ! ************************************************************************************************** - PURE SUBROUTINE dbcsr_expand_row_index(row_p, row_i, nrows, nblks) - INTEGER, INTENT(in) :: nrows, nblks - INTEGER, DIMENSION(1:nrows + 1), INTENT(IN) :: row_p - INTEGER, DIMENSION(1:nblks), INTENT(out) :: row_i + PURE SUBROUTINE dbcsr_expand_row_index_1d(row_p, row_i, nrows, nblks) + INTEGER, INTENT(IN) :: nrows, nblks + INTEGER, DIMENSION(1:nrows + 1), INTENT(IN) :: row_p + INTEGER, DIMENSION(1:nblks), INTENT(OUT) :: row_i - INTEGER :: row + INTEGER :: row DO row = 1, nrows row_i(row_p(row) + 1:row_p(row + 1)) = row ENDDO - END SUBROUTINE dbcsr_expand_row_index + END SUBROUTINE dbcsr_expand_row_index_1d + +! ************************************************************************************************** +!> \brief Expands a row_p index +!> \param row_p ... +!> \param row_i ... +!> \param nrows ... +!> \param nblks ... +! ************************************************************************************************** + PURE SUBROUTINE dbcsr_expand_row_index_2d(row_p, row_i, nrows, nblks) + INTEGER, INTENT(IN) :: nrows, nblks + INTEGER, DIMENSION(1:nrows + 1), INTENT(IN) :: row_p + INTEGER, DIMENSION(:,:), INTENT(OUT) :: row_i + + INTEGER :: row + + MARK_USED(nblks) + ! DBCSR_ASSERT(SIZE(row_i,2) .LE. nblks) + DO row = 1, nrows + row_i(1, row_p(row) + 1:row_p(row + 1)) = row + ENDDO + END SUBROUTINE dbcsr_expand_row_index_2d ! ************************************************************************************************** !> \brief Counts columns-per-row count from row index array, in-place. @@ -303,7 +329,7 @@ END SUBROUTINE dbcsr_expand_row_index ! ************************************************************************************************** PURE SUBROUTINE dbcsr_count_row_index_inplace(rows, nrows) INTEGER, INTENT(IN) :: nrows - INTEGER, DIMENSION(1:nrows + 1), INTENT(INOUT) :: rows + INTEGER, DIMENSION(1:nrows + 1), INTENT(INOUT) :: rows INTEGER :: row @@ -322,7 +348,7 @@ END SUBROUTINE dbcsr_count_row_index_inplace PURE SUBROUTINE dbcsr_count_row_index_copy(rows, counts, nrows) INTEGER, INTENT(IN) :: nrows INTEGER, DIMENSION(1:nrows), INTENT(OUT) :: counts - INTEGER, DIMENSION(1:nrows + 1), INTENT(IN) :: rows + INTEGER, DIMENSION(1:nrows + 1), INTENT(IN) :: rows INTEGER :: row @@ -339,7 +365,7 @@ END SUBROUTINE dbcsr_count_row_index_copy ! ************************************************************************************************** PURE SUBROUTINE dbcsr_build_row_index_inplace(rows, nrows) INTEGER, INTENT(IN) :: nrows - INTEGER, DIMENSION(1:nrows + 1), INTENT(INOUT) :: rows + INTEGER, DIMENSION(1:nrows + 1), INTENT(INOUT) :: rows INTEGER :: o, old_count, row @@ -363,7 +389,7 @@ END SUBROUTINE dbcsr_build_row_index_inplace ! ************************************************************************************************** PURE SUBROUTINE dbcsr_build_row_index_copy(counts, rows, nrows) INTEGER, INTENT(IN) :: nrows - INTEGER, DIMENSION(1:nrows + 1), INTENT(OUT) :: rows + INTEGER, DIMENSION(1:nrows + 1), INTENT(OUT) :: rows INTEGER, DIMENSION(1:nrows), INTENT(IN) :: counts !WTF?!rows(1) = 0 @@ -1273,7 +1299,7 @@ SUBROUTINE dbcsr_make_index_list(matrix, thread_redist) ! nblks = matrix%nblks ALLOCATE (blki(3, nblks)) - CALL dbcsr_expand_row_index(matrix%row_p, blki(1, :), nrows, nblks) + CALL dbcsr_expand_row_index(matrix%row_p, blki, nrows, nblks) IF (matrix%local_indexing) THEN ! If local indexing is enabled, then the rows but not the ! columns are already localized From 53891420300f402fd70afcbfb00e1b21024040d3 Mon Sep 17 00:00:00 2001 From: Hans Pabst Date: Sat, 22 Jun 2019 22:05:49 +0200 Subject: [PATCH 77/88] Make pretty (unrelated to this PR). --- src/core/dbcsr_lib.F | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index b1cb9b9a49b..9730a5ce0a1 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -191,15 +191,15 @@ SUBROUTINE dbcsr_init_lib_pre(mp_comm, io_unit) dbcsr_thread_safe = 0 ! not threaded !$ dbcsr_thread_safe = 1 ! if DBCSR is compiled with openmp, set to threaded ! Check whether DBCSR and libcusmm (GPU backend) have the same level of threading - IF (dbcsr_thread_safe /= libcusmm_thread_safe) then - IF (dbcsr_thread_safe /= 0) then - DBCSR_ABORT("DBCSR's and libcusmm's threading levels do not match. DBCSR is compiled -with threading support while libcusmm is compiled without threading support.") - ELSE - DBCSR_ABORT("DBCSR's and libcusmm's threading levels do not match. DBCSR is compiled -without threading support while libcusmm is compiled with threading support.") - ENDIF - ENDIF + IF (dbcsr_thread_safe /= libcusmm_thread_safe) then + IF (dbcsr_thread_safe /= 0) then + DBCSR_ABORT("DBCSR's and libcusmm's threading levels do not match. DBCSR is compiled + with threading support while libcusmm is compiled without threading support.") + ELSE + DBCSR_ABORT("DBCSR's and libcusmm's threading levels do not match. DBCSR is compiled + without threading support while libcusmm is compiled with threading support.") + ENDIF + ENDIF #endif END SUBROUTINE dbcsr_init_lib_pre From 9792750e4b003855cb4405c46b1f431b72dae072 Mon Sep 17 00:00:00 2001 From: Hans Pabst Date: Sun, 23 Jun 2019 09:05:46 +0200 Subject: [PATCH 78/88] Added dbcsr_expand_row_index_2d (instead of obscure overload); allows to specify destination-row (dst_i). --- src/block/dbcsr_index_operations.F | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/block/dbcsr_index_operations.F b/src/block/dbcsr_index_operations.F index 0847bc476b0..ec75564e5e0 100644 --- a/src/block/dbcsr_index_operations.F +++ b/src/block/dbcsr_index_operations.F @@ -77,11 +77,6 @@ MODULE dbcsr_index_operations PUBLIC :: dbcsr_addto_index_array, dbcsr_clearfrom_index_array, & dbcsr_repoint_index, dbcsr_make_index_exist - INTERFACE dbcsr_expand_row_index - MODULE PROCEDURE dbcsr_expand_row_index_1d, & - dbcsr_expand_row_index_2d - END INTERFACE - INTERFACE dbcsr_count_row_index MODULE PROCEDURE dbcsr_count_row_index_copy, & dbcsr_count_row_index_inplace @@ -288,7 +283,7 @@ END SUBROUTINE dbcsr_make_dbcsr_index !> \param nrows ... !> \param nblks ... ! ************************************************************************************************** - PURE SUBROUTINE dbcsr_expand_row_index_1d(row_p, row_i, nrows, nblks) + PURE SUBROUTINE dbcsr_expand_row_index(row_p, row_i, nrows, nblks) INTEGER, INTENT(IN) :: nrows, nblks INTEGER, DIMENSION(1:nrows + 1), INTENT(IN) :: row_p INTEGER, DIMENSION(1:nblks), INTENT(OUT) :: row_i @@ -298,26 +293,24 @@ PURE SUBROUTINE dbcsr_expand_row_index_1d(row_p, row_i, nrows, nblks) DO row = 1, nrows row_i(row_p(row) + 1:row_p(row + 1)) = row ENDDO - END SUBROUTINE dbcsr_expand_row_index_1d + END SUBROUTINE dbcsr_expand_row_index ! ************************************************************************************************** !> \brief Expands a row_p index !> \param row_p ... !> \param row_i ... !> \param nrows ... -!> \param nblks ... +!> \param dst_i ... ! ************************************************************************************************** - PURE SUBROUTINE dbcsr_expand_row_index_2d(row_p, row_i, nrows, nblks) + PURE SUBROUTINE dbcsr_expand_row_index_2d(row_p, row_i, nrows, dst_i) INTEGER, INTENT(IN) :: nrows, nblks INTEGER, DIMENSION(1:nrows + 1), INTENT(IN) :: row_p INTEGER, DIMENSION(:,:), INTENT(OUT) :: row_i INTEGER :: row - MARK_USED(nblks) - ! DBCSR_ASSERT(SIZE(row_i,2) .LE. nblks) DO row = 1, nrows - row_i(1, row_p(row) + 1:row_p(row + 1)) = row + row_i(dst_i, row_p(row) + 1:row_p(row + 1)) = row ENDDO END SUBROUTINE dbcsr_expand_row_index_2d From 04fecdf05645d019c7c78304df3e52176c5dccac Mon Sep 17 00:00:00 2001 From: Hans Pabst Date: Sun, 23 Jun 2019 18:20:47 +0200 Subject: [PATCH 79/88] Declare dst_i rather than nblks. --- src/block/dbcsr_index_operations.F | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/block/dbcsr_index_operations.F b/src/block/dbcsr_index_operations.F index ec75564e5e0..63724e4b9b9 100644 --- a/src/block/dbcsr_index_operations.F +++ b/src/block/dbcsr_index_operations.F @@ -303,7 +303,7 @@ END SUBROUTINE dbcsr_expand_row_index !> \param dst_i ... ! ************************************************************************************************** PURE SUBROUTINE dbcsr_expand_row_index_2d(row_p, row_i, nrows, dst_i) - INTEGER, INTENT(IN) :: nrows, nblks + INTEGER, INTENT(IN) :: nrows, dst_i INTEGER, DIMENSION(1:nrows + 1), INTENT(IN) :: row_p INTEGER, DIMENSION(:,:), INTENT(OUT) :: row_i From e82857fac10f66e0383ce62fe8e27c7470c1c750 Mon Sep 17 00:00:00 2001 From: Hans Pabst Date: Sun, 23 Jun 2019 18:30:08 +0200 Subject: [PATCH 80/88] Call new dbcsr_expand_row_index_2d rather than dbcsr_expand_row_index. --- src/block/dbcsr_index_operations.F | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/block/dbcsr_index_operations.F b/src/block/dbcsr_index_operations.F index 63724e4b9b9..6316fb7a9c4 100644 --- a/src/block/dbcsr_index_operations.F +++ b/src/block/dbcsr_index_operations.F @@ -1292,7 +1292,7 @@ SUBROUTINE dbcsr_make_index_list(matrix, thread_redist) ! nblks = matrix%nblks ALLOCATE (blki(3, nblks)) - CALL dbcsr_expand_row_index(matrix%row_p, blki, nrows, nblks) + CALL dbcsr_expand_row_index_2d(matrix%row_p, blki, nrows, 1) IF (matrix%local_indexing) THEN ! If local indexing is enabled, then the rows but not the ! columns are already localized From 6aec02fcf30c09b9380a6c61dbb00ee6a71ec7a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Mon, 24 Jun 2019 09:36:23 +0200 Subject: [PATCH 81/88] travis: update and cache homebrew --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index ec14ba007c3..2d1c7a8aeff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,11 +19,13 @@ addons: - gcc - cmake - openmpi + update: true cache: pip: true directories: - $HOME/deps + - $HOME/Library/Caches/Homebrew env: global: From 76b8142065b9cbb3fb047b043a9752b15c7bd72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Mon, 24 Jun 2019 14:03:22 +0200 Subject: [PATCH 82/88] cmake: use CMAKE_Fortran_COMPILER_ID to identify vendors & add Cray support --- CMakeLists.txt | 30 ++++++++++-------------------- README.md | 6 ++++++ examples/CMakeLists.txt | 8 +++++++- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa33c33b66f..9b77534eaec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,24 +88,8 @@ endif () # COMPILER CONFIGURATION: -get_filename_component(Fortran_COMPILER_NAME ${CMAKE_Fortran_COMPILER} NAME) - -# if the fortran compiler command is a CRAY wrapper, find out what the underlying compiler is -if (Fortran_COMPILER_NAME MATCHES "ftn") - execute_process(COMMAND ftn -craype-verbose OUTPUT_VARIABLE ftn_verbose_OUT ERROR_QUIET) - separate_arguments(ftn_verbose_OUT NATIVE_COMMAND "${ftn_verbose_OUT}") - list(GET ftn_verbose_OUT 0 Fortran_COMPILER_NAME) -endif () - -# if the fortran compiler command is an OPENMPI wrapper, find out what the underlying compiler is -if (Fortran_COMPILER_NAME MATCHES "mpif*") # mpif77, mpif90 or mpifort - execute_process(COMMAND ${Fortran_COMPILER_NAME} --showme OUTPUT_VARIABLE mpif_verbose_OUT ERROR_QUIET) - separate_arguments(mpif_verbose_OUT NATIVE_COMMAND "${mpif_verbose_OUT}") - list(GET mpif_verbose_OUT 0 Fortran_COMPILER_NAME) -endif () - # Depending on the fortran compiler, set the appropriate compiler flags -if (Fortran_COMPILER_NAME MATCHES "^gfortran.*") +if (CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS "-std=c++11") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -funroll-loops") set(CMAKE_CXX_FLAGS_COVERAGE "-O0 -fprofile-arcs -ftest-coverage") @@ -122,25 +106,31 @@ if (Fortran_COMPILER_NAME MATCHES "^gfortran.*") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov --coverage") endif () endif () -elseif (Fortran_COMPILER_NAME MATCHES "^ifort.*") +elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "Intel") set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -debug") set(CMAKE_Fortran_FLAGS "-free -stand f08 -fpp") # Disable the line-length-extension warning #5268 set(CMAKE_Fortran_FLAGS_RELEASE "-O3 -diag-disable=5268") set(CMAKE_Fortran_FLAGS_DEBUG "-O0 -debug") -elseif (Fortran_COMPILER_NAME MATCHES "^pgf.*") +elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "PGI") set(CMAKE_CXX_FLAGS_RELEASE "-fast") set(CMAKE_CXX_FLAGS_DEBUG "-g") set(CMAKE_Fortran_FLAGS "-Mfreeform -Mextend -Mallocatable=03") # -Mallocatable=03: enable F2003+ assignment semantics set(CMAKE_Fortran_FLAGS_RELEASE "-fast") set(CMAKE_Fortran_FLAGS_DEBUG "-g") +elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "Cray") + set(CMAKE_CXX_FLAGS_RELEASE "-O3") + set(CMAKE_CXX_FLAGS_DEBUG "-g") + set(CMAKE_Fortran_FLAGS "-f free") + set(CMAKE_Fortran_FLAGS_RELEASE "-O2") + set(CMAKE_Fortran_FLAGS_DEBUG "-g") else () message(WARNING "\ Unknown compiler, trying with just '-O2'/'-O0 -g'.\n\ You will most likely have to pass additonal options for free-form Fortran 2008 for your compiler!\n\ Please open an issue at https://github.com/cp2k/dbcsr/issues with the reported compiler name and the required flags.") - message("-- Fortran_COMPILER_NAME: " ${Fortran_COMPILER_NAME}) + message("-- CMAKE_Fortran_COMPILER_ID: " ${CMAKE_Fortran_COMPILER_ID}) message("-- CMAKE_Fortran_COMPILER full path: " ${CMAKE_Fortran_COMPILER}) set(CMAKE_Fortran_FLAGS_RELEASE "-O2") diff --git a/README.md b/README.md index 168e8d1b515..c4b76f17ce4 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,12 @@ The configuration flags are (default first): -DTEST_OMP_THREADS=<2,N> -DCMAKE_BUILD_TYPE= + +Building on Cray requires an additional flag to make sure CMake is able to properly +detect the compiler behind the compiler wrappers: + + -DCMAKE_SYSTEM_NAME=CrayLinuxEnvironment + ## Contributing to DBCSR Your contribution to the project is welcome! diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 55b391725ef..58c6bfed3cc 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,5 +15,11 @@ endforeach () if (WITH_C_API) add_executable(dbcsr_example_3_cpp dbcsr_example_3.cpp) target_link_libraries(dbcsr_example_3_cpp dbcsr_c MPI::MPI_CXX) - target_compile_features(dbcsr_example_3_cpp PRIVATE cxx_std_14) + + if (CMAKE_CXX_COMPILER_ID STREQUAL "Cray") + # for recent Cray compiler versions CMake doesn't know + target_compile_options(dbcsr_example_3_cpp PRIVATE "-hstd=c++14") + else () + target_compile_features(dbcsr_example_3_cpp PRIVATE cxx_std_14) + endif () endif () From b7ff1c87aa67cfc2c513b0e6b66b8c24b7e1f3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Mon, 24 Jun 2019 14:05:58 +0200 Subject: [PATCH 83/88] ci/daint: introduce parallel builds/tests --- .ci/daint.cscs.ch/Jenkinsfile | 37 +++++++++++++------- .ci/daint.cscs.ch/{build.sh => gnu.build.sh} | 8 +++-- .ci/daint.cscs.ch/{test.sh => gnu.test.sh} | 5 +-- 3 files changed, 33 insertions(+), 17 deletions(-) rename .ci/daint.cscs.ch/{build.sh => gnu.build.sh} (75%) rename .ci/daint.cscs.ch/{test.sh => gnu.test.sh} (89%) diff --git a/.ci/daint.cscs.ch/Jenkinsfile b/.ci/daint.cscs.ch/Jenkinsfile index 46523c97748..1ad0bf5303c 100644 --- a/.ci/daint.cscs.ch/Jenkinsfile +++ b/.ci/daint.cscs.ch/Jenkinsfile @@ -7,21 +7,32 @@ pipeline { checkout scm } } - stage('build') { - steps { - run_batch("0:15:00") - } - } - stage('test') { - steps { - run_batch("1:00:00") + stage("build and test") { + parallel { + stage("GNU") { + stages { + stage('build') { + steps { + run_batch("0:15:00", "gnu", "build") + } + } + stage('test') { + steps { + run_batch("1:00:00", "gnu", "test") + } + } + } + } } } } } -def run_batch(timelimit) { +def run_batch(timelimit, environment, task) { def (account, basename) = env.JOB_NAME.split('/') + def sbatch_script = ".ci/daint.cscs.ch/${environment}.${task}.sh" + def sbatch_out = "sbatch.${env.BUILD_TAG}.${environment}.${task}.out" + // avoid using the shell for variable expansion to // get the final command displayed in Jenkins try { @@ -29,13 +40,13 @@ def run_batch(timelimit) { sbatch --wait \ --time="${timelimit}" \ --account="${account}" \ - --job-name="${basename}" \ - --output="sbatch.${env.BUILD_TAG}.${env.STAGE_NAME}.out" \ - .ci/daint.cscs.ch/${env.STAGE_NAME}.sh + --job-name="${basename}.${environment}.${task}" \ + --output="${sbatch_out}" \ + ${sbatch_script} """ } finally { - echo readFile("sbatch.${env.BUILD_TAG}.${env.STAGE_NAME}.out") + echo readFile("${sbatch_out}") } } diff --git a/.ci/daint.cscs.ch/build.sh b/.ci/daint.cscs.ch/gnu.build.sh similarity index 75% rename from .ci/daint.cscs.ch/build.sh rename to .ci/daint.cscs.ch/gnu.build.sh index 8f7cf7c50ec..ac4ccc0b8cf 100644 --- a/.ci/daint.cscs.ch/build.sh +++ b/.ci/daint.cscs.ch/gnu.build.sh @@ -16,18 +16,22 @@ set -o pipefail module swap PrgEnv-cray PrgEnv-gnu module load daint-gpu cudatoolkit CMake/3.12.0 module unload cray-libsci_acc +module list set -o xtrace # do not set earlier to avoid noise from module umask 0002 # make sure group members can access the data -mkdir --mode=0775 -p "${SCRATCH}/${BUILD_TAG}" -cd "${SCRATCH}/${BUILD_TAG}" +mkdir --mode=0775 -p "${SCRATCH}/${BUILD_TAG}.gnu" +cd "${SCRATCH}/${BUILD_TAG}.gnu" cmake \ + -DCMAKE_SYSTEM_NAME=CrayLinuxEnvironment \ -DUSE_CUDA=ON \ -DUSE_CUBLAS=ON \ -DWITH_GPU=P100 \ + -DBLAS_FOUND=ON -DBLAS_LIBRARIES="-lsci_gnu_mpi_mp" \ + -DLAPACK_FOUND=ON -DLAPACK_LIBRARIES="-lsci_gnu_mpi_mp" \ -DMPIEXEC_EXECUTABLE="$(command -v srun)" \ -DTEST_MPI_RANKS=${SLURM_NTASKS} \ -DTEST_OMP_THREADS=${SLURM_CPUS_PER_TASK} \ diff --git a/.ci/daint.cscs.ch/test.sh b/.ci/daint.cscs.ch/gnu.test.sh similarity index 89% rename from .ci/daint.cscs.ch/test.sh rename to .ci/daint.cscs.ch/gnu.test.sh index e1098f82d35..156aa6e269d 100644 --- a/.ci/daint.cscs.ch/test.sh +++ b/.ci/daint.cscs.ch/gnu.test.sh @@ -16,13 +16,14 @@ set -o pipefail module swap PrgEnv-cray PrgEnv-gnu module load daint-gpu cudatoolkit CMake/3.12.0 module unload cray-libsci_acc +module list set -o xtrace # do not set earlier to avoid noise from module umask 0002 # make sure group members can access the data -mkdir --mode=0775 -p "${SCRATCH}/${BUILD_TAG}" -cd "${SCRATCH}/${BUILD_TAG}" +mkdir --mode=0775 -p "${SCRATCH}/${BUILD_TAG}.gnu" +cd "${SCRATCH}/${BUILD_TAG}.gnu" export CRAY_CUDA_MPS=1 # enable the CUDA proxy for MPI+CUDA export OMP_PROC_BIND=TRUE # set thread affinity From c8c1326aa525f1466913e12fe20d14e126473e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Sun, 2 Jun 2019 18:06:11 +0200 Subject: [PATCH 84/88] core/dbcsr_lib: Cray compiler doesn't support multiline strings in macros --- src/core/dbcsr_lib.F | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/dbcsr_lib.F b/src/core/dbcsr_lib.F index 9730a5ce0a1..b15d2616d4c 100644 --- a/src/core/dbcsr_lib.F +++ b/src/core/dbcsr_lib.F @@ -193,11 +193,9 @@ SUBROUTINE dbcsr_init_lib_pre(mp_comm, io_unit) ! Check whether DBCSR and libcusmm (GPU backend) have the same level of threading IF (dbcsr_thread_safe /= libcusmm_thread_safe) then IF (dbcsr_thread_safe /= 0) then - DBCSR_ABORT("DBCSR's and libcusmm's threading levels do not match. DBCSR is compiled - with threading support while libcusmm is compiled without threading support.") + DBCSR_ABORT("DBCSR's and libcusmm's threading levels do not match. DBCSR is compiled with threading support while libcusmm is compiled without threading support.") ELSE - DBCSR_ABORT("DBCSR's and libcusmm's threading levels do not match. DBCSR is compiled - without threading support while libcusmm is compiled with threading support.") + DBCSR_ABORT("DBCSR's and libcusmm's threading levels do not match. DBCSR is compiled without threading support while libcusmm is compiled with threading support.") ENDIF ENDIF #endif From a9895fcec8bba5eee378c014dd4fb9335b780203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Thu, 6 Jun 2019 08:57:47 +0200 Subject: [PATCH 85/88] cmake: add workaround for Cray false-positives and tcmalloc bug --- CMakeLists.txt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b77534eaec..b39fac74f04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,9 +122,19 @@ elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "PGI") elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "Cray") set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_DEBUG "-g") - set(CMAKE_Fortran_FLAGS "-f free") + set(CMAKE_Fortran_FLAGS "-f free -M 3105") # -M 3105: hide a false-positive warning about modified loop variables due to loop fusing set(CMAKE_Fortran_FLAGS_RELEASE "-O2") - set(CMAKE_Fortran_FLAGS_DEBUG "-g") + set(CMAKE_Fortran_FLAGS_DEBUG "-g -h error_on_warning") + set(CMAKE_Fortran_MODOUT_FLAG "-ef") # override to get lower-case module file names + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) + # prevent deallocation failures due to tcmalloc's free with glibc's aligned_alloc, see https://bugzilla.redhat.com/show_bug.cgi?id=1569391 + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -h system_alloc") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -h system_alloc") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -h system_alloc") + # since the detection of the implicitly linked libraries occurs bevore we can intervene, filter them out again + list(FILTER CMAKE_C_IMPLICIT_LINK_LIBRARIES EXCLUDE REGEX "tcmalloc") + list(FILTER CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES EXCLUDE REGEX "tcmalloc") + endif () else () message(WARNING "\ Unknown compiler, trying with just '-O2'/'-O0 -g'.\n\ From 810f9e3f93098a7927fdf3abd1e48a7e546b5649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Tue, 18 Jun 2019 14:34:53 +0200 Subject: [PATCH 86/88] cmake: add support for NAG --- CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b39fac74f04..c7451c2274c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,6 +119,16 @@ elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "PGI") set(CMAKE_Fortran_FLAGS "-Mfreeform -Mextend -Mallocatable=03") # -Mallocatable=03: enable F2003+ assignment semantics set(CMAKE_Fortran_FLAGS_RELEASE "-fast") set(CMAKE_Fortran_FLAGS_DEBUG "-g") +elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "NAG") + set(CMAKE_CXX_FLAGS_RELEASE "-O3") + set(CMAKE_CXX_FLAGS_DEBUG "-g") + set(CMAKE_Fortran_FLAGS "-f2008 -free -Warn=reallocation -Warn=subnormal") + set(CMAKE_Fortran_FLAGS_RELEASE "-O2") + set(CMAKE_Fortran_FLAGS_DEBUG "-g -C") + if (NOT OpenMP_FOUND) + set(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE} -gline") # -gline is only supported without OpenMP + set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -C=all") # some checks are not available with OpenMP + endif () elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "Cray") set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_DEBUG "-g") From c3f93aa7979d855177e1951a46b60a04c2378ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Mon, 24 Jun 2019 14:32:02 +0200 Subject: [PATCH 87/88] cmake: factor out compiler configuration this also splits the Fortran and C++ compiler configuration since we may encounter a compiler mix (for example with NAG) --- CMakeLists.txt | 72 +--------------------------- cmake/CompilerConfiguration.cmake | 79 +++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 71 deletions(-) create mode 100644 cmake/CompilerConfiguration.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index c7451c2274c..fb82fa5791e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,77 +86,7 @@ if (NOT CMAKE_BUILD_TYPE) FORCE) endif () -# COMPILER CONFIGURATION: - -# Depending on the fortran compiler, set the appropriate compiler flags -if (CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "-std=c++11") - set(CMAKE_CXX_FLAGS_RELEASE "-O3 -funroll-loops") - set(CMAKE_CXX_FLAGS_COVERAGE "-O0 -fprofile-arcs -ftest-coverage") - set(CMAKE_CXX_FLAGS_DEBUG "-O0 -ggdb") - set(CMAKE_Fortran_FLAGS "-ffree-form -ffree-line-length-none -std=f2008ts") - set(CMAKE_Fortran_FLAGS_RELEASE "-O3 -funroll-loops") - set(CMAKE_Fortran_FLAGS_DEBUG "-O0 -ggdb") - set(CMAKE_Fortran_FLAGS_COVERAGE "-O0 -fprofile-arcs -ftest-coverage") - - # on Apple we may compile with GCC Fortran, but build/link C/C++ code with Apple's clang, requiring special treatment - if (APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "AppleClang") - if (CMAKE_BUILD_TYPE MATCHES Coverage) - # when building with coverage suppport, shared libs/executables must be explicitly linked to avoid undefined symbols - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov --coverage") - endif () - endif () -elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "Intel") - set(CMAKE_CXX_FLAGS_RELEASE "-O3") - set(CMAKE_CXX_FLAGS_DEBUG "-O0 -debug") - set(CMAKE_Fortran_FLAGS "-free -stand f08 -fpp") - # Disable the line-length-extension warning #5268 - set(CMAKE_Fortran_FLAGS_RELEASE "-O3 -diag-disable=5268") - set(CMAKE_Fortran_FLAGS_DEBUG "-O0 -debug") -elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "PGI") - set(CMAKE_CXX_FLAGS_RELEASE "-fast") - set(CMAKE_CXX_FLAGS_DEBUG "-g") - set(CMAKE_Fortran_FLAGS "-Mfreeform -Mextend -Mallocatable=03") # -Mallocatable=03: enable F2003+ assignment semantics - set(CMAKE_Fortran_FLAGS_RELEASE "-fast") - set(CMAKE_Fortran_FLAGS_DEBUG "-g") -elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "NAG") - set(CMAKE_CXX_FLAGS_RELEASE "-O3") - set(CMAKE_CXX_FLAGS_DEBUG "-g") - set(CMAKE_Fortran_FLAGS "-f2008 -free -Warn=reallocation -Warn=subnormal") - set(CMAKE_Fortran_FLAGS_RELEASE "-O2") - set(CMAKE_Fortran_FLAGS_DEBUG "-g -C") - if (NOT OpenMP_FOUND) - set(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE} -gline") # -gline is only supported without OpenMP - set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -C=all") # some checks are not available with OpenMP - endif () -elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "Cray") - set(CMAKE_CXX_FLAGS_RELEASE "-O3") - set(CMAKE_CXX_FLAGS_DEBUG "-g") - set(CMAKE_Fortran_FLAGS "-f free -M 3105") # -M 3105: hide a false-positive warning about modified loop variables due to loop fusing - set(CMAKE_Fortran_FLAGS_RELEASE "-O2") - set(CMAKE_Fortran_FLAGS_DEBUG "-g -h error_on_warning") - set(CMAKE_Fortran_MODOUT_FLAG "-ef") # override to get lower-case module file names - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) - # prevent deallocation failures due to tcmalloc's free with glibc's aligned_alloc, see https://bugzilla.redhat.com/show_bug.cgi?id=1569391 - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -h system_alloc") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -h system_alloc") - set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -h system_alloc") - # since the detection of the implicitly linked libraries occurs bevore we can intervene, filter them out again - list(FILTER CMAKE_C_IMPLICIT_LINK_LIBRARIES EXCLUDE REGEX "tcmalloc") - list(FILTER CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES EXCLUDE REGEX "tcmalloc") - endif () -else () - message(WARNING "\ -Unknown compiler, trying with just '-O2'/'-O0 -g'.\n\ -You will most likely have to pass additonal options for free-form Fortran 2008 for your compiler!\n\ -Please open an issue at https://github.com/cp2k/dbcsr/issues with the reported compiler name and the required flags.") - message("-- CMAKE_Fortran_COMPILER_ID: " ${CMAKE_Fortran_COMPILER_ID}) - message("-- CMAKE_Fortran_COMPILER full path: " ${CMAKE_Fortran_COMPILER}) - - set(CMAKE_Fortran_FLAGS_RELEASE "-O2") - set(CMAKE_Fortran_FLAGS_DEBUG "-O0 -g") -endif () - +include(CompilerConfiguration) include(CheckCompilerSupport) file(STRINGS VERSION VERSION_INFO) diff --git a/cmake/CompilerConfiguration.cmake b/cmake/CompilerConfiguration.cmake new file mode 100644 index 00000000000..67b69ed7578 --- /dev/null +++ b/cmake/CompilerConfiguration.cmake @@ -0,0 +1,79 @@ +if (CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + set(CMAKE_Fortran_FLAGS "-ffree-form -ffree-line-length-none -std=f2008ts") + set(CMAKE_Fortran_FLAGS_RELEASE "-O3 -funroll-loops") + set(CMAKE_Fortran_FLAGS_DEBUG "-O0 -ggdb") + set(CMAKE_Fortran_FLAGS_COVERAGE "-O0 -fprofile-arcs -ftest-coverage") +elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "Intel") + set(CMAKE_Fortran_FLAGS "-free -stand f08 -fpp") + set(CMAKE_Fortran_FLAGS_RELEASE "-O3 -diag-disable=5268") # Disable the line-length-extension warning #5268 + set(CMAKE_Fortran_FLAGS_DEBUG "-O0 -debug") +elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "PGI") + set(CMAKE_Fortran_FLAGS "-Mfreeform -Mextend -Mallocatable=03") # -Mallocatable=03: enable F2003+ assignment semantics + set(CMAKE_Fortran_FLAGS_RELEASE "-fast") + set(CMAKE_Fortran_FLAGS_DEBUG "-g") +elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "NAG") + set(CMAKE_Fortran_FLAGS "-f2008 -free -Warn=reallocation -Warn=subnormal") + set(CMAKE_Fortran_FLAGS_RELEASE "-O2") + set(CMAKE_Fortran_FLAGS_DEBUG "-g -C") + if (NOT OpenMP_FOUND) + set(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE} -gline") # -gline is only supported without OpenMP + set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -C=all") # some checks are not available with OpenMP + endif () +elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "Cray") + set(CMAKE_Fortran_FLAGS "-f free -M 3105") # -M 3105: hide a false-positive warning about modified loop variables due to loop fusing + set(CMAKE_Fortran_FLAGS_RELEASE "-O2") + set(CMAKE_Fortran_FLAGS_DEBUG "-g -h error_on_warning") + set(CMAKE_Fortran_MODOUT_FLAG "-ef") # override to get lower-case module file names +else () + message(WARNING "\ +Unknown Fortran compiler, trying without any additional (optimization) flags.\n\ +You will most likely have to specify extra options for free-form Fortran 2008 for your compiler!\n\ +Please open an issue at https://github.com/cp2k/dbcsr/issues with the reported compiler name and the required flags.") + message("-- CMAKE_Fortran_COMPILER_ID: " ${CMAKE_Fortran_COMPILER_ID}) + message("-- CMAKE_Fortran_COMPILER full path: " ${CMAKE_Fortran_COMPILER}) +endif () + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(CMAKE_CXX_FLAGS "-std=c++11") + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -funroll-loops") + set(CMAKE_CXX_FLAGS_COVERAGE "-O0 -fprofile-arcs -ftest-coverage") + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -ggdb") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(CMAKE_CXX_FLAGS "-std=c++11") + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -funroll-loops") + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + set(CMAKE_CXX_FLAGS "-std=c++11") + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -funroll-loops") + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") + + if (CMAKE_BUILD_TYPE MATCHES Coverage) + # when building with coverage suppport, shared libs/executables must be explicitly linked to avoid undefined symbols + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov --coverage") + endif () +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + set(CMAKE_CXX_FLAGS_RELEASE "-O3") + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -debug") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "PGI") + set(CMAKE_CXX_FLAGS_RELEASE "-fast") + set(CMAKE_CXX_FLAGS_DEBUG "-g") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Cray") + set(CMAKE_CXX_FLAGS_RELEASE "-O3") + set(CMAKE_CXX_FLAGS_DEBUG "-g") + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) + # prevent deallocation failures due to tcmalloc's free with glibc's aligned_alloc, see https://bugzilla.redhat.com/show_bug.cgi?id=1569391 + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -h system_alloc") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -h system_alloc") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -h system_alloc") + # since the detection of the implicitly linked libraries occurs bevore we can intervene, filter them out again + list(FILTER CMAKE_C_IMPLICIT_LINK_LIBRARIES EXCLUDE REGEX "tcmalloc") + list(FILTER CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES EXCLUDE REGEX "tcmalloc") + endif () +else () + message(WARNING "\ +Unknown C++ compiler, trying without any additional (optimization) flags.\n\ +You may have to specify flags for C++11/14 support manually.\n\ +Please open an issue at https://github.com/cp2k/dbcsr/issues with the reported compiler name and the required flags.") + message("-- CMAKE_CXX_COMPILER_ID: " ${CMAKE_CXX_COMPILER_ID}) + message("-- CMAKE_CXX_COMPILER full path: " ${CMAKE_CXX_COMPILER}) +endif () From fa126ce43a838336f52996565ca208fead275c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Fri, 28 Jun 2019 14:26:13 +0200 Subject: [PATCH 88/88] Bump version to 2.0.0-alpha2 --- VERSION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 493658d3a84..4deba8e6c0e 100644 --- a/VERSION +++ b/VERSION @@ -1,6 +1,6 @@ MAJOR = 2 MINOR = 0 -PATCH = 0-alpha1 +PATCH = 0-alpha2 # A specific DATE (YYYY-MM-DD) fixes an official release, otherwise # it is considered Development version. -DATE = 2019-04-16 +DATE = 2019-06-28