diff --git a/eessi/testsuite/common_config.py b/eessi/testsuite/common_config.py index 1abfbe7c..2989b85d 100644 --- a/eessi/testsuite/common_config.py +++ b/eessi/testsuite/common_config.py @@ -100,8 +100,8 @@ def common_eessi_init(eessi_version=None): if eessi_cvmfs_repo is None: rflog.getlogger().warning(' '.join([ "Environment variable 'EESSI_CVMFS_REPO' is not defined.", - "If you do not intend to use the EESSI software stack, this is perfectly fine.", - "To use EESSI, initialize the EESSI environment before running the test suite.", + "If you plan to use the EESSI software stack,", + "make sure to initialize the EESSI environment before running the test suite.", ])) return '' diff --git a/eessi/testsuite/constants.py b/eessi/testsuite/constants.py index 6617bce1..c198e493 100644 --- a/eessi/testsuite/constants.py +++ b/eessi/testsuite/constants.py @@ -63,6 +63,7 @@ class _GPUVendors(NamedTuple): '1_core': {'num_nodes': 1, 'num_cpus_per_node': 1, 'num_gpus_per_node': 1}, '2_cores': {'num_nodes': 1, 'num_cpus_per_node': 2, 'num_gpus_per_node': 1}, '4_cores': {'num_nodes': 1, 'num_cpus_per_node': 4, 'num_gpus_per_node': 1}, + '8_cores': {'num_nodes': 1, 'num_cpus_per_node': 8, 'num_gpus_per_node': 1}, # renamed after v0.2.0 from 1_cpn_2_nodes to make more unique '1cpn_2nodes': {'num_nodes': 2, 'num_cpus_per_node': 1, 'num_gpus_per_node': 1}, # renamed after v0.2.0 from 1_cpn_4_nodes to make more unique diff --git a/eessi/testsuite/eessi_mixin.py b/eessi/testsuite/eessi_mixin.py index fcc56f08..ad2cc0d7 100644 --- a/eessi/testsuite/eessi_mixin.py +++ b/eessi/testsuite/eessi_mixin.py @@ -38,16 +38,20 @@ class EESSI_Mixin(RegressionMixin): - Init phase: time_limit, measure_memory_usage, bench_name_ci """ - # Set defaults for these class variables, can be overwritten by child class if desired + # Defaults for ReFrame variables that can be overwritten on the cmd line measure_memory_usage = variable(bool, value=False) exact_memory = variable(bool, value=False) user_executable_opts = variable(str, value='') + thread_binding = variable(str, value='false') + + # Set defaults for these class variables, can be overwritten by child class if desired scale = parameter(SCALES.keys()) bench_name = None bench_name_ci = None is_ci_test = False num_tasks_per_compute_unit = 1 always_request_gpus = None + require_buildenv_module = False require_internet = False # Create ReFrame variables for logging runtime environment information @@ -126,10 +130,20 @@ def EESSI_mixin_run_after_init(self): # Filter on which scales are supported by the partitions defined in the ReFrame configuration hooks.filter_supported_scales(self) - hooks.filter_valid_systems_by_device_type(self, required_device_type=self.device_type) - hooks.set_modules(self) + if self.require_buildenv_module: + hooks.add_buildenv_module(self) + + thread_binding = self.thread_binding.lower() + if thread_binding in ('true', 'compact'): + hooks.set_compact_thread_binding(self) + elif thread_binding != 'false': + err_msg = f"Invalid thread_binding value '{thread_binding}'. Valid values: 'true', 'compact', or 'false'." + raise ReframeFatalError(err_msg) + + hooks.filter_valid_systems_by_device_type(self, required_device_type=self.device_type) + # Set scales as tags hooks.set_tag_scale(self) @@ -210,10 +224,11 @@ def EESSI_mixin_log_runtime_info(self): path to the modulefile, EESSI software subdir, EESSI testsuite version""" self.postrun_cmds.append('echo "EESSI_CVMFS_REPO: $EESSI_CVMFS_REPO"') self.postrun_cmds.append('echo "EESSI_SOFTWARE_SUBDIR: $EESSI_SOFTWARE_SUBDIR"') - if self.module_name: - # Get full modulepath - get_full_modpath = f'echo "FULL_MODULEPATH: $(module --location show {self.module_name} 2>&1)"' - self.postrun_cmds.append(get_full_modpath) + if self.module_names: + for mod in self.module_names: + # Get full modulepath + get_full_modpath = f'echo "FULL_MODULEPATH: $(module --location show {mod} 2>&1)"' + self.postrun_cmds.append(get_full_modpath) @run_before('run', always_last=True) def EESSI_mixin_set_user_executable_opts(self): diff --git a/eessi/testsuite/hooks.py b/eessi/testsuite/hooks.py index bcd44b64..0c17b7ef 100644 --- a/eessi/testsuite/hooks.py +++ b/eessi/testsuite/hooks.py @@ -9,8 +9,12 @@ from eessi.testsuite.constants import (COMPUTE_UNITS, DEVICE_TYPES, EXTRAS, FEATURES, GPU_VENDORS, INVALID_SYSTEM, SCALES) -from eessi.testsuite.utils import (get_max_avail_gpus_per_node, is_cuda_required_module, log, - check_proc_attribute_defined, check_extras_key_defined) +from eessi.testsuite.utils import (check_extras_key_defined, check_proc_attribute_defined, find_modules, + get_max_avail_gpus_per_node, is_cuda_required_module, log, split_module, + select_matching_modules) + +# global variables +_buildenv_modules = [] def _assign_default_num_cpus_per_node(test: rfm.RegressionTest): @@ -463,7 +467,7 @@ def filter_valid_systems_by_device_type(test: rfm.RegressionTest, required_devic cause the valid_systems to be set to an empty string, and consequently the test.valid_systems to an invalid system name (eessi.testsuite.constants.INVALID_SYSTEM). """ - is_cuda_module = is_cuda_required_module(test.module_name) + is_cuda_module = is_cuda_required_module(test.module_names) if is_cuda_module and required_device_type == DEVICE_TYPES.GPU: # CUDA modules and when using a GPU require partitions with FEATURES.GPU feature and @@ -613,14 +617,25 @@ def req_memory_per_node(test: rfm.RegressionTest, app_mem_req: float): def set_modules(test: rfm.RegressionTest): """ - Skip current test if module_name is not among a list of modules, + Set modules test parameter via module_name, which can be a string or a list of strings + Skip current test if any of the module names is not present in the list of modules, specified with --setvar modules=. """ - if test.modules and test.module_name not in test.modules: - test.valid_systems = [] - log(f'valid_systems set to {test.valid_systems}') - - test.modules = [test.module_name] + if not test.module_name: + return + if isinstance(test.module_name, str): + test.module_names = [test.module_name] + elif isinstance(test.module_name, (list, tuple)): + test.module_names = test.module_name + else: + raise TypeError(f'module_name is a {type(test.module_name).__name__}, should be string, list, or tuple') + if test.modules: + for name in test.module_names: + if name not in test.modules: + test.valid_systems = [] + log(f'module {name} not in {test.modules}, valid_systems set to {test.valid_systems}') + + test.modules = test.module_names log(f'modules set to {test.modules}') @@ -765,3 +780,62 @@ def extract_memory_usage(self): return hooks.extract_memory_usage(self) """ return sn.extractsingle(r'^MAX_MEM_IN_MIB=(?P\S+)', test.stdout, 'memory', int) + + +def add_buildenv_module(test: rfm.RegressionTest, index=-1): + """ + Add a buildenv module that matches the reference module to the list of modules + + + Arguments: + - test: ReFrame test to which this hook should apply + - index: module index in test.modules to take as the reference (default is last); + note that the reference module’s toolchain should not be at the system + level: otherwise only buildenv modules at the system level can be added + + Requirements: + - recent enough easybuild python package + - a matching default buildenv module (e.g. buildenv/default-foss-2024a) available on the system + """ + for mod in test.modules: + if mod.split('/')[0] == 'buildenv': + # buildenv module already in the list + return + + # get list of buildenv modules on the system + # make global to avoid calculating _buildenv_modules multiple times + global _buildenv_modules + if not _buildenv_modules: + _buildenv_modules = set(find_modules('buildenv')) + to_remove = [] + for mod in _buildenv_modules: + mod_parts = split_module(mod) + if mod_parts[4] or mod_parts[1] != 'default': + # only consider default buildenv modules without versionsuffixes + to_remove.append(mod) + + _buildenv_modules = [x for x in _buildenv_modules if x not in to_remove] + + if not _buildenv_modules: + msg = 'No default buildenv modules without versionsuffixes found on the system.' + log(msg) + test.valid_systems = [INVALID_SYSTEM] + return + + ref_module = test.modules[index] + matching_modules = select_matching_modules(list(_buildenv_modules), ref_module) + + if not matching_modules: + msg = f'No matching buildenv module for {ref_module} found on the system.' + log(msg) + test.valid_systems = [INVALID_SYSTEM] + return + + if len(matching_modules) > 1: + msg = f'Multiple matching buildenv modules found, will use the first one: {_buildenv_modules}.' + log(msg) + + buildenv_mod = matching_modules[0] + # insert to keep the most important module last + test.modules.insert(0, buildenv_mod) + log(f'Module {buildenv_mod} added to list of modules') diff --git a/eessi/testsuite/tests/apps/MetalWalls.py b/eessi/testsuite/tests/apps/MetalWalls.py index 38de23a3..a483e4a3 100644 --- a/eessi/testsuite/tests/apps/MetalWalls.py +++ b/eessi/testsuite/tests/apps/MetalWalls.py @@ -63,6 +63,9 @@ def run_after_init(self): # Filter on which scales are supported by the partitions defined in the ReFrame configuration hooks.filter_supported_scales(self) + # Support selecting modules on the cmd line. + hooks.set_modules(self) + # Make sure that GPU tests run in partitions that support running on a GPU, # and that CPU-only tests run in partitions that support running CPU-only. # Also support setting valid_systems on the cmd line. @@ -71,9 +74,6 @@ def run_after_init(self): # Make sure the test is not run on offline partitions hooks.filter_valid_systems_for_offline_partitions(self) - # Support selecting modules on the cmd line. - hooks.set_modules(self) - # Support selecting scales on the cmd line via tags. hooks.set_tag_scale(self) diff --git a/eessi/testsuite/tests/libs/blas/__init__.py b/eessi/testsuite/tests/libs/blas/__init__.py new file mode 100644 index 00000000..d70db9d9 --- /dev/null +++ b/eessi/testsuite/tests/libs/blas/__init__.py @@ -0,0 +1,2 @@ +# WARNING: do not remove this file. +# It is needed to autogenerate documentation from this repo. diff --git a/eessi/testsuite/tests/libs/blas/blas.py b/eessi/testsuite/tests/libs/blas/blas.py new file mode 100644 index 00000000..6966a3d1 --- /dev/null +++ b/eessi/testsuite/tests/libs/blas/blas.py @@ -0,0 +1,213 @@ +""" +This test is adapted from the BLAS test included in the BLIS v1.1 sources at +https://github.com/flame/blis/tree/1.1/test/3 + +Customizations to the original BLAS test: +- adapted and simplified Makefile for FlexiBLAS support +- custom simplified run.sh script + +Notes: +- a buildenv module, which includes FlexiBLAS, and BLIS module must be loaded to run the test, + even if BLIS or OpenBLAS are not used. the buildenv module is automatically added by eessi_mixin + by setting "require_buildenv_module = True" +- by default OpenBLAS is already included as a dependency of FlexiBLAS. + this means that, if multiple OpenBLAS modules are present in the same toolchain, + the OpenBLAS versions not included in FlexiBLAS may fail to load due to a version conflict. + to fix this, you may want to run: `export LMOD_DISABLE_SAME_NAME_AUTOSWAP=no` before running the test. +- BLAS modules with hyphens in the version string are not supported. + +Supported tags in this ReFrame test (in addition to the common tags): +- BLAS implementation: `openblas`, `blis`, `aocl-blas`, `imkl` +- `CI` tag: runs only openblas +""" + + +import reframe as rfm +from reframe.core.backends import getlauncher +from reframe.core.builtins import parameter, run_after, run_before, sanity_function +import reframe.core.logging as rflog +import reframe.utility.sanity as sn + +from eessi.testsuite.constants import COMPUTE_UNITS, DEVICE_TYPES, SCALES +from eessi.testsuite.eessi_mixin import EESSI_Mixin +from eessi.testsuite.utils import find_modules, select_matching_modules, log + + +def multi_thread_scales(): + """Scales for multi-threaded BLAS tests""" + return parameter([ + k for (k, v) in SCALES.items() + if v['num_nodes'] == 1 + ]) + + +def get_blas_modules(blas_name): + """ + Find available blas_name modules and (latest) matching BLIS module + + Returns: a list of lists: each inner list has the blas_name module as first item, + and the matching BLIS module as second item. The BLIS module must + be second to avoid segmentation fault for AOCL-BLAS + """ + blas_modules = list(find_modules(rf'{blas_name}$')) + if blas_name == 'BLIS': + return [[x] for x in blas_modules] + + ml_lists = [] + blis_modules = list(find_modules('BLIS$')) + + for mod in blas_modules: + matching_blises = sorted(select_matching_modules(blis_modules, mod)) + if not matching_blises: + msg = f'Skipping BLAS module {mod}: no matching BLIS module found.' + rflog.getlogger().warning(msg) + continue + blis = matching_blises[-1] + ml_lists.append([mod, blis]) + + return ml_lists + + +def get_imkl_modules(): + """ + Find available imkl modules and (latest) BLIS module + Only imkl modules with SYSTEM toolchain are used + + Returns: a list of lists: each inner list has the imkl module as first item, + and the latest BLIS module as second item. + """ + ml_lists = [] + + blises = sorted(find_modules(r'BLIS$')) + if not blises: + log('no BLIS module found') + return ml_lists + blis = blises[-1] + + imkls = list(find_modules(r'imkl/[^-]*$', name_only=False)) + for imkl in imkls: + ml_lists.append([imkl, blis]) + + return ml_lists + + +class EESSI_BLAS_base(rfm.RunOnlyRegressionTest): + "base BLAS test" + device_type = DEVICE_TYPES.CPU + compute_unit = COMPUTE_UNITS.NODE + time_limit = '10m' + readonly_files = ['Makefile', 'run.sh', 'test_gemm.c', 'test_hemm.c', 'test_herk.c', 'test_trmm.c', 'test_trsm.c', + 'test_utils.c', 'test_utils.h'] + env_vars = { + 'CFLAGS': '"-O2 -ftree-vectorize -march=native -fno-math-errno -g"', # default CFLAGS used by EasyBuild + } + executable = './run.sh' + nrepeats = '5' + dts = ['s', 'd', 'c', 'z'] + ops = ['gemm_nn', 'hemm_ll', 'herk_ln', 'trmm_llnn', 'trsm_runn'] + size = ['200', '2000', '200'] + require_buildenv_module = True + threading = 'mt' + + def required_mem_per_node(self): + return self.num_cpus_per_task * 100 + 250 + + @run_after('init') + def set_prerun_cmds(self): + """Set prerun_cmds""" + self.prerun_cmds = [f'make flexiblas-{self.threading}'] + + @run_after('init') + def set_executable_opts(self): + """Set executable_opts""" + self.executable_opts = [ + self.threading, + self.nrepeats, + '"{}"'.format(' '.join(self.size)), + '"{}"'.format(' '.join(self.dts)), + '"{}"'.format(' '.join(self.ops)), + ] + + @run_after('init') + def set_flexiblas_blas_lib(self): + """Set FLEXIBLAS environment variable to selected BLAS lib""" + self.env_vars.update({ + 'FLEXIBLAS': self.flexiblas_blas_lib, + }) + + @run_after('setup') + def set_launcher(self): + """Select local launcher""" + self.job.launcher = getlauncher('local')() + + @sanity_function + def assert_sanity(self): + assert_backend = sn.assert_not_found( + r'BLAS backend\s+\S+\s+not found', + self.stderr, + f'FlexiBLAS BLAS backend not found ({self.flexiblas_blas_lib})' + ) + asserts_result = [ + sn.assert_found(r"data\S+_flexiblas", f'output/{x}{y}_flexiblas.m', f'output/{x}{y}_flexiblas.m') + for x in self.dts for y in self.ops + ] + return sn.all([assert_backend] + asserts_result) + + def _extract_perf(self, dt, op): + return sn.extractsingle( + rf'^data\S+_flexiblas.*{self.size[1]}\s+(?P\S+)\s+\];', + f'output/{dt}{op}_flexiblas.m', + 1, float + ) + + @run_before('performance') + def set_perf_vars(self): + """Set performance variables""" + self.perf_variables.update({ + f'{x}{y.split("_")[0]}': sn.make_performance_function(self._extract_perf(x, y), 'GFLOPS') + for x in self.dts for y in self.ops + }) + + +@rfm.simple_test +class EESSI_BLAS_OpenBLAS_mt(EESSI_BLAS_base, EESSI_Mixin): + "multi-threaded OpenBLAS test" + + scale = multi_thread_scales() + module_name = parameter(get_blas_modules('OpenBLAS')) + flexiblas_blas_lib = 'openblas' + tags = {'openblas'} + is_ci_test = True + thread_binding = 'compact' + + +@rfm.simple_test +class EESSI_BLAS_AOCLBLAS_mt(EESSI_BLAS_base, EESSI_Mixin): + "multi-threaded AOCL-BLAS test" + + scale = multi_thread_scales() + module_name = parameter(get_blas_modules('AOCL-BLAS')) + flexiblas_blas_lib = 'aocl_mt' + tags = {'aocl-blas'} + thread_binding = 'compact' + + +@rfm.simple_test +class EESSI_BLAS_imkl_mt(EESSI_BLAS_base, EESSI_Mixin): + "multi-threaded imkl test" + + scale = multi_thread_scales() + module_name = parameter(get_imkl_modules()) + flexiblas_blas_lib = 'imkl' + tags = {'imkl'} + thread_binding = 'compact' + + +class EESSI_BLAS_BLIS_mt(EESSI_BLAS_base, EESSI_Mixin): + "multi-threaded BLIS test" + + scale = multi_thread_scales() + module_name = parameter(get_blas_modules('BLIS')) + flexiblas_blas_lib = 'blis' + tags = {'blis'} + thread_binding = 'compact' diff --git a/eessi/testsuite/tests/libs/blas/src/Makefile b/eessi/testsuite/tests/libs/blas/src/Makefile new file mode 100644 index 00000000..4bd325aa --- /dev/null +++ b/eessi/testsuite/tests/libs/blas/src/Makefile @@ -0,0 +1,196 @@ +# +# BLIS +# An object-based framework for developing high-performance BLAS-like +# libraries. +# +# Copyright (C) 2014, The University of Texas at Austin +# Copyright (C) 2018, Advanced Micro Devices, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# - Neither the name(s) of the copyright holder(s) nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Makefile +# +# Field G. Van Zee +# +# Makefile for standalone BLIS test drivers. +# +# Makefile adapted for FlexiBLAS by Samuel Moors (Vrije Universiteit Brussel) +# + +REQUIRED_VARS = EBROOTBLIS EBROOTFLEXIBLAS CFLAGS + +$(foreach var_name,$(REQUIRED_VARS),\ + $(if $(value $(var_name)),,\ + $(error variable '$(var_name)' must be set)\ + )\ +) + +.PHONY: all all-st all-mt all-blis all-flexiblas flexiblas-st flexiblas-mt blis-st blis-mt clean + +LIB_PATH := $(EBROOTBLIS)/lib +INC_PATH := $(EBROOTBLIS)/include/blis + +LIBBLIS_LINK := $(LIB_PATH)/libblis.a +LDFLAGS += -lm -lpthread -fopenmp -lrt + +FLEXIBLAS_LIB := $(EBROOTFLEXIBLAS)/lib/libflexiblas.so + +TEST_SRC_PATH := . +TEST_OBJ_PATH := . + +# Gather all local object files. +TEST_OBJS := $(sort $(patsubst $(TEST_SRC_PATH)/%.c, \ + $(TEST_OBJ_PATH)/%.o, \ + $(wildcard $(TEST_SRC_PATH)/*.c))) + +# Override the value of CINCFLAGS so that the value of CFLAGS returned by +# get-user-cflags-for() is not cluttered up with include paths needed only +# while building BLIS. +CINCFLAGS := -I$(INC_PATH) + +# Add local header paths to CFLAGS. +CFLAGS += -I$(TEST_SRC_PATH) + +# Which library? +DEF_BLI := -DBLIS +DEF_BLA := -DBLAS + +# Implementation string. +STR_BLI := -DIMPL_STR=\"blis\" +STR_FBL := -DIMPL_STR=\"flexiblas\" + +# Single or multithreaded string. +STR_ST := -DTHR_STR=\"st\" +STR_MT := -DTHR_STR=\"mt\" + +# Targets/rules +all: all-st all-mt + +all-st: blis-st flexiblas-st +all-mt: blis-mt flexiblas-mt + +all-blis: blis-st blis-mt +all-flexiblas: flexiblas-st flexiblas-mt + +# Define the datatypes, operations, and implementations. +OPS := gemm hemm herk trmm trsm +BIMPLS := blis flexiblas + +# Define a function to construct object filenames from the operations +# given an implementation. +get-st-objs = $(foreach op,$(OPS),test_$(op)_$(1)_st.o) +get-mt-objs = $(foreach op,$(OPS),test_$(op)_$(1)_mt.o) + +# Construct object and binary names for single-threaded and multithreaded +# files for BLIS, OpenBLAS, Eigen, and a vendor library (e.g. MKL). +BLIS_ST_OBJS := $(call get-st-objs,blis) +BLIS_ST_BINS := $(patsubst %.o,%.x,$(BLIS_ST_OBJS)) + +BLIS_MT_OBJS := $(call get-mt-objs,blis) +BLIS_MT_BINS := $(patsubst %.o,%.x,$(BLIS_MT_OBJS)) + +FLEXIBLAS_ST_OBJS := $(call get-st-objs,flexiblas) +FLEXIBLAS_ST_BINS := $(patsubst %.o,%.x,$(FLEXIBLAS_ST_OBJS)) + +FLEXIBLAS_MT_OBJS := $(call get-mt-objs,flexiblas) +FLEXIBLAS_MT_BINS := $(patsubst %.o,%.x,$(FLEXIBLAS_MT_OBJS)) + +# List other miscellaneous object files +UTIL_OBJS := test_utils.o +UTIL_HDRS := test_utils.h + +# Define some targets associated with the above object/binary files. +blis-st: $(BLIS_ST_BINS) +blis-mt: $(BLIS_MT_BINS) +flexiblas-st: $(FLEXIBLAS_ST_BINS) +flexiblas-mt: $(FLEXIBLAS_MT_BINS) + +# Mark the object files as intermediate so that make will remove them +# automatically after building the binaries on which they depend. +.INTERMEDIATE: $(BLIS_ST_OBJS) $(BLIS_MT_OBJS) +.INTERMEDIATE: $(FLEXIBLAS_ST_OBJS) $(FLEXIBLAS_MT_OBJS) +.INTERMEDIATE: $(UTIL_OBJS) + +# -- Object file rules -- + +# A function to return other cpp macros that help the test driver +# identify the implementation. +get-bl-cpp = $(strip \ + $(if $(findstring blis,$(1)),$(STR_BLI) $(DEF_BLI),\ + $(if $(findstring flexiblas,$(1)),$(STR_FBL) $(DEF_BLA),\ + $(if $(and $(findstring eigen,$(1)),\ + $(findstring gemm,$(2))),\ + $(STR_EIG) $(DEF_EIG),\ + $(if $(findstring eigen,$(1)),\ + $(STR_EIG) $(DEF_BLA),\ + $(STR_VEN) $(DEF_BLA)))))) + +# Rules for miscellaneous files. +test_utils.o: test_utils.c test_utils.h + $(CC) $(CFLAGS) -c $< -o $@ + +# Rules for BLIS and BLAS libraries. +define make-st-rule +test_$(1)_$(2)_st.o: test_$(op).c Makefile + $(CC) $(CFLAGS) $(call get-bl-cpp,$(2),$(1)) $(STR_ST) -c $$< -o $$@ +endef + +define make-mt-rule +test_$(1)_$(2)_mt.o: test_$(op).c Makefile + $(CC) $(CFLAGS) $(call get-bl-cpp,$(2),$(1)) $(STR_MT) -c $$< -o $$@ +endef + +$(foreach op,$(OPS), \ +$(foreach im,$(BIMPLS),$(eval $(call make-st-rule,$(op),$(im))))) + +$(foreach op,$(OPS), \ +$(foreach im,$(BIMPLS),$(eval $(call make-mt-rule,$(op),$(im))))) + +# -- Executable file rules -- + +# NOTE: For the BLAS test drivers, we place the BLAS libraries before BLIS +# on the link command line in case BLIS was configured with the BLAS +# compatibility layer. This prevents BLIS from inadvertently getting called +# for the BLAS routines we are trying to test with. + +# Combine the miscellaneous objects with libblis for conciseness (since all +# driver binaries depend on these objects). +COMMON_OBJS := $(UTIL_OBJS) $(LIBBLIS_LINK) + +test_%_blis_st.x: test_%_blis_st.o $(COMMON_OBJS) + $(CC) $(strip $< $(COMMON_OBJS) $(LDFLAGS) -o $@) + +test_%_blis_mt.x: test_%_blis_mt.o $(COMMON_OBJS) + $(CC) $(strip $< $(COMMON_OBJS) $(LDFLAGS) -o $@) + +test_%_flexiblas_st.x: test_%_flexiblas_st.o $(COMMON_OBJS) + $(CC) $(strip $< $(FLEXIBLAS_LIB) $(COMMON_OBJS) $(LDFLAGS) -o $@) + +test_%_flexiblas_mt.x: test_%_flexiblas_mt.o $(COMMON_OBJS) + $(CC) $(strip $< $(FLEXIBLAS_LIB) $(COMMON_OBJS) $(LDFLAGS) -o $@) + +clean: + - rm -f *.o *.x + diff --git a/eessi/testsuite/tests/libs/blas/src/__init__.py b/eessi/testsuite/tests/libs/blas/src/__init__.py new file mode 100644 index 00000000..d70db9d9 --- /dev/null +++ b/eessi/testsuite/tests/libs/blas/src/__init__.py @@ -0,0 +1,2 @@ +# WARNING: do not remove this file. +# It is needed to autogenerate documentation from this repo. diff --git a/eessi/testsuite/tests/libs/blas/src/run.sh b/eessi/testsuite/tests/libs/blas/src/run.sh new file mode 100755 index 00000000..1aa305c9 --- /dev/null +++ b/eessi/testsuite/tests/libs/blas/src/run.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +# threading: st or mt +tsuf=$1 + +# Number of repeats per problem size. +nrepeats=$2 + +# Problem size range for single- and multithreaded execution. +# psr_st="100 1000 100" +# psr_mt="200 2000 200" +psr=$3 + +# Datatypes to test. +# test_dts="s d c z" +test_dts=$4 + +# Operations to test. +# test_ops="gemm_nn hemm_ll herk_ln trmm_llnn trsm_runn" +test_ops=$5 + +# The induced method to use ('auto', 'native', or '1m') for executing +# complex-domain level-3 operations. +ind="auto" + +delay=0.1 + +# Implementations to test. +im=flexiblas + +mkdir -p output + +# Iterate over the datatypes. +for dt in ${test_dts}; do + + # Iterate over the operations. + for op in ${test_ops}; do + + # Strip everything before the understore so that what remains is + # the operation parameter string. + oppars=${op##*_}; + + # Strip everything after the understore so that what remains is + # the operation name (sans parameter encoding). + opname=${op%%_*} + + # Problem size range + # psrvar="psr_${tsuf}" + # psr=${!psrvar} + + # Construct the name of the test executable. + exec_name="test_${opname}_${im}_${tsuf}.x" + + # Construct the name of the output file. + out_file="output/${dt}${opname}_${oppars}_${im}.m" + + set -x + ./${exec_name} -d ${dt} -c ${oppars} -i ${ind} -p "${psr}" -r ${nrepeats} ${qv} > ${out_file} + set +x + + # Bedtime! + sleep ${delay} + + done +done + diff --git a/eessi/testsuite/tests/libs/blas/src/test_gemm.c b/eessi/testsuite/tests/libs/blas/src/test_gemm.c new file mode 100644 index 00000000..287069b2 --- /dev/null +++ b/eessi/testsuite/tests/libs/blas/src/test_gemm.c @@ -0,0 +1,455 @@ +/* + + BLIS + An object-based framework for developing high-performance BLAS-like + libraries. + + Copyright (C) 2014, The University of Texas at Austin + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of The University of Texas nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#ifdef EIGEN + #define BLIS_DISABLE_BLAS_DEFS + #include "blis.h" + #include "test_utils.h" + #include + #include + using namespace Eigen; +#else + #include "blis.h" + #include "test_utils.h" +#endif + +//#define PRINT + +static const char* LOCAL_OPNAME_STR = "gemm"; +static const char* LOCAL_PC_STR = "nn"; + +int main( int argc, char** argv ) +{ + obj_t a, b, c; + obj_t c_save; + obj_t alpha, beta; + dim_t m, n, k; + dim_t p; + dim_t p_begin, p_max, p_inc; + int m_input, n_input, k_input; + ind_t ind; + num_t dt; + char dt_ch; + int r, n_repeats; + trans_t transa; + trans_t transb; + f77_char f77_transa; + f77_char f77_transb; + + double dtime; + double dtime_save; + double gflops; + + params_t params; + + // Supress compiler warnings about unused variable 'ind'. + ( void )ind; + + + //bli_init(); + + //bli_error_checking_level_set( BLIS_NO_ERROR_CHECKING ); + + // Parse the command line options into strings, integers, enums, + // and doubles, as appropriate. + parse_cl_params( argc, argv, init_def_params, ¶ms ); + + dt = params.dt; + + ind = params.im; + + p_begin = params.sta; + p_max = params.end; + p_inc = params.inc; + + m_input = params.m; + n_input = params.n; + k_input = params.k; + + n_repeats = params.nr; + + + // Map the datatype to its corresponding char. + bli_param_map_blis_to_char_dt( dt, &dt_ch ); + + // Map the parameter chars to their corresponding BLIS enum type values. + bli_param_map_char_to_blis_trans( params.pc_str[0], &transa ); + bli_param_map_char_to_blis_trans( params.pc_str[1], &transb ); + + // Map the BLIS enum type values to their corresponding BLAS chars. + bli_param_map_blis_to_netlib_trans( transa, &f77_transa ); + bli_param_map_blis_to_netlib_trans( transb, &f77_transb ); + + // Begin with initializing the last entry to zero so that + // matlab allocates space for the entire array once up-front. + for ( p = p_begin; p + p_inc <= p_max; p += p_inc ) ; + + printf( "data_%s_%cgemm_%s", THR_STR, dt_ch, IMPL_STR ); + printf( "( %4lu, 1:4 ) = [ %5lu %5lu %5lu %8.2f ];\n", + ( unsigned long )(p - p_begin)/p_inc + 1, + ( unsigned long )0, + ( unsigned long )0, + ( unsigned long )0, 0.0 ); + + + //for ( p = p_begin; p <= p_max; p += p_inc ) + for ( p = p_max; p_begin <= p; p -= p_inc ) + { + + if ( m_input < 0 ) m = p / ( dim_t )abs(m_input); + else m = ( dim_t ) m_input; + if ( n_input < 0 ) n = p / ( dim_t )abs(n_input); + else n = ( dim_t ) n_input; + if ( k_input < 0 ) k = p / ( dim_t )abs(k_input); + else k = ( dim_t ) k_input; + + bli_obj_create( dt, 1, 1, 0, 0, &alpha ); + bli_obj_create( dt, 1, 1, 0, 0, &beta ); + + // Choose the storage of each matrix based on the corresponding + // char in the params_t struct. Note that the expected order of + // storage specifers in sc_str is CAB (not ABC). + if ( params.sc_str[1] == 'c' ) bli_obj_create( dt, m, k, 0, 0, &a ); + else bli_obj_create( dt, m, k, k, 1, &a ); + + if ( params.sc_str[2] == 'c' ) bli_obj_create( dt, k, n, 0, 0, &b ); + else bli_obj_create( dt, k, n, n, 1, &b ); + + if ( params.sc_str[0] == 'c' ) bli_obj_create( dt, m, n, 0, 0, &c ); + else bli_obj_create( dt, m, n, n, 1, &c ); + + if ( params.sc_str[0] == 'c' ) bli_obj_create( dt, m, n, 0, 0, &c_save ); + else bli_obj_create( dt, m, n, n, 1, &c_save ); + + bli_randm( &a ); + bli_randm( &b ); + bli_randm( &c ); + + bli_obj_set_conjtrans( transa, &a ); + bli_obj_set_conjtrans( transb, &b ); + + //bli_setsc( (2.0/1.0), 0.0, &alpha ); + //bli_setsc( (1.0/1.0), 0.0, &beta ); + bli_setsc( params.alpha, 0.0, &alpha ); + bli_setsc( params.beta, 0.0, &beta ); + + //bli_printm( "alpha:", &alpha, "%7.4e", "" ); + //bli_printm( "beta: ", &beta, "%7.4e", "" ); + + bli_copym( &c, &c_save ); + +#ifdef BLIS + // Switch to the induced method specified by ind, unless the 'auto' + // option was given, in which case we leave the induced method + // unchanged. + if ( params.im_is_auto == FALSE ) + { + bli_ind_disable_all_dt( dt ); + bli_ind_enable_dt( ind, dt ); + } +#endif + +#ifdef EIGEN + double alpha_r, alpha_i; + + bli_getsc( &alpha, &alpha_r, &alpha_i ); + + void* ap = bli_obj_buffer_at_off( &a ); + void* bp = bli_obj_buffer_at_off( &b ); + void* cp = bli_obj_buffer_at_off( &c ); + + int os_a, os_b, os_c; + + if ( params.sc_str[0] == 'c' ) + { + os_a = bli_obj_col_stride( &a ); + os_b = bli_obj_col_stride( &b ); + os_c = bli_obj_col_stride( &c ); + } + else + { + os_a = bli_obj_row_stride( &a ); + os_b = bli_obj_row_stride( &b ); + os_c = bli_obj_row_stride( &c ); + } + + Stride stride_a( os_a, 1 ); + Stride stride_b( os_b, 1 ); + Stride stride_c( os_c, 1 ); + + typedef Matrix MatrixXs_c; + typedef Matrix MatrixXd_c; + typedef Matrix, Dynamic, Dynamic, ColMajor> MatrixXc_c; + typedef Matrix, Dynamic, Dynamic, ColMajor> MatrixXz_c; + + typedef Matrix MatrixXs_r; + typedef Matrix MatrixXd_r; + typedef Matrix, Dynamic, Dynamic, RowMajor> MatrixXc_r; + typedef Matrix, Dynamic, Dynamic, RowMajor> MatrixXz_r; + + Map > As_c( ( float* )ap, m, k, stride_a ); + Map > Bs_c( ( float* )bp, k, n, stride_b ); + Map > Cs_c( ( float* )cp, m, n, stride_c ); + + Map > Ad_c( ( double* )ap, m, k, stride_a ); + Map > Bd_c( ( double* )bp, k, n, stride_b ); + Map > Cd_c( ( double* )cp, m, n, stride_c ); + + Map > Ac_c( ( std::complex* )ap, m, k, stride_a ); + Map > Bc_c( ( std::complex* )bp, k, n, stride_b ); + Map > Cc_c( ( std::complex* )cp, m, n, stride_c ); + + Map > Az_c( ( std::complex* )ap, m, k, stride_a ); + Map > Bz_c( ( std::complex* )bp, k, n, stride_b ); + Map > Cz_c( ( std::complex* )cp, m, n, stride_c ); + + Map > As_r( ( float* )ap, m, k, stride_a ); + Map > Bs_r( ( float* )bp, k, n, stride_b ); + Map > Cs_r( ( float* )cp, m, n, stride_c ); + + Map > Ad_r( ( double* )ap, m, k, stride_a ); + Map > Bd_r( ( double* )bp, k, n, stride_b ); + Map > Cd_r( ( double* )cp, m, n, stride_c ); + + Map > Ac_r( ( std::complex* )ap, m, k, stride_a ); + Map > Bc_r( ( std::complex* )bp, k, n, stride_b ); + Map > Cc_r( ( std::complex* )cp, m, n, stride_c ); + + Map > Az_r( ( std::complex* )ap, m, k, stride_a ); + Map > Bz_r( ( std::complex* )bp, k, n, stride_b ); + Map > Cz_r( ( std::complex* )cp, m, n, stride_c ); +#endif + + dtime_save = DBL_MAX; + + for ( r = 0; r < n_repeats; ++r ) + { + bli_copym( &c_save, &c ); + + dtime = bli_clock(); + +#ifdef PRINT + bli_printm( "a", &a, "%4.1f", "" ); + bli_printm( "b", &b, "%4.1f", "" ); + bli_printm( "c", &c, "%4.1f", "" ); +#endif + +#if defined(BLIS) + + bli_gemm( &alpha, + &a, + &b, + &beta, + &c ); + +#elif defined(EIGEN) + + //C.noalias() += alpha_r * A * B; + + if ( params.sc_str[0] == 'c' ) + { + if ( params.dt_str[0] == 's' ) Cs_c.noalias() += alpha_r * As_c * Bs_c; + else if ( params.dt_str[0] == 'd' ) Cd_c.noalias() += alpha_r * Ad_c * Bd_c; + else if ( params.dt_str[0] == 'c' ) Cc_c.noalias() += alpha_r * Ac_c * Bc_c; + else if ( params.dt_str[0] == 'z' ) Cz_c.noalias() += alpha_r * Az_c * Bz_c; + } + else // if ( params.sc_str[0] == 'r' ) + { + if ( params.dt_str[0] == 's' ) Cs_r.noalias() += alpha_r * As_r * Bs_r; + else if ( params.dt_str[0] == 'd' ) Cd_r.noalias() += alpha_r * Ad_r * Bd_r; + else if ( params.dt_str[0] == 'c' ) Cc_r.noalias() += alpha_r * Ac_r * Bc_r; + else if ( params.dt_str[0] == 'z' ) Cz_r.noalias() += alpha_r * Az_r * Bz_r; + } + +#else // if defined(BLAS) + + if ( bli_is_float( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width_after_trans( &a ); + f77_int nn = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldb = bli_obj_col_stride( &b ); + f77_int ldc = bli_obj_col_stride( &c ); + float* alphap = ( float* )bli_obj_buffer( &alpha ); + float* ap = ( float* )bli_obj_buffer( &a ); + float* bp = ( float* )bli_obj_buffer( &b ); + float* betap = ( float* )bli_obj_buffer( &beta ); + float* cp = ( float* )bli_obj_buffer( &c ); + + sgemm_( &f77_transa, + &f77_transb, + &mm, + &nn, + &kk, + alphap, + ap, &lda, + bp, &ldb, + betap, + cp, &ldc ); + } + else if ( bli_is_double( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width_after_trans( &a ); + f77_int nn = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldb = bli_obj_col_stride( &b ); + f77_int ldc = bli_obj_col_stride( &c ); + double* alphap = ( double* )bli_obj_buffer( &alpha ); + double* ap = ( double* )bli_obj_buffer( &a ); + double* bp = ( double* )bli_obj_buffer( &b ); + double* betap = ( double* )bli_obj_buffer( &beta ); + double* cp = ( double* )bli_obj_buffer( &c ); + + dgemm_( &f77_transa, + &f77_transb, + &mm, + &nn, + &kk, + alphap, + ap, &lda, + bp, &ldb, + betap, + cp, &ldc ); + } + else if ( bli_is_scomplex( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width_after_trans( &a ); + f77_int nn = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldb = bli_obj_col_stride( &b ); + f77_int ldc = bli_obj_col_stride( &c ); + scomplex* alphap = ( scomplex* )bli_obj_buffer( &alpha ); + scomplex* ap = ( scomplex* )bli_obj_buffer( &a ); + scomplex* bp = ( scomplex* )bli_obj_buffer( &b ); + scomplex* betap = ( scomplex* )bli_obj_buffer( &beta ); + scomplex* cp = ( scomplex* )bli_obj_buffer( &c ); + + cgemm_( &f77_transa, + &f77_transb, + &mm, + &nn, + &kk, + alphap, + ap, &lda, + bp, &ldb, + betap, + cp, &ldc ); + } + else if ( bli_is_dcomplex( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width_after_trans( &a ); + f77_int nn = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldb = bli_obj_col_stride( &b ); + f77_int ldc = bli_obj_col_stride( &c ); + dcomplex* alphap = ( dcomplex* )bli_obj_buffer( &alpha ); + dcomplex* ap = ( dcomplex* )bli_obj_buffer( &a ); + dcomplex* bp = ( dcomplex* )bli_obj_buffer( &b ); + dcomplex* betap = ( dcomplex* )bli_obj_buffer( &beta ); + dcomplex* cp = ( dcomplex* )bli_obj_buffer( &c ); + + zgemm_( &f77_transa, + &f77_transb, + &mm, + &nn, + &kk, + alphap, + ap, &lda, + bp, &ldb, + betap, + cp, &ldc ); + } +#endif + +#ifdef PRINT + bli_printm( "c after", &c, "%4.1f", "" ); + exit(1); +#endif + + dtime_save = bli_clock_min_diff( dtime_save, dtime ); + } + + gflops = ( 2.0 * m * k * n ) / ( dtime_save * 1.0e9 ); + + if ( bli_is_complex( dt ) ) gflops *= 4.0; + + printf( "data_%s_%cgemm_%s", THR_STR, dt_ch, IMPL_STR ); + printf( "( %4lu, 1:4 ) = [ %5lu %5lu %5lu %8.2f ];\n", + ( unsigned long )(p - p_begin)/p_inc + 1, + ( unsigned long )m, + ( unsigned long )k, + ( unsigned long )n, gflops ); + fflush( stdout ); + + bli_obj_free( &alpha ); + bli_obj_free( &beta ); + + bli_obj_free( &a ); + bli_obj_free( &b ); + bli_obj_free( &c ); + bli_obj_free( &c_save ); + } + + //bli_finalize(); + + return 0; +} + +void init_def_params( params_t* params ) +{ + params->opname = LOCAL_OPNAME_STR; + params->impl = IMPL_STR; + + params->pc_str = LOCAL_PC_STR; + params->dt_str = GLOB_DEF_DT_STR; + params->sc_str = GLOB_DEF_SC_STR; + + params->im_str = GLOB_DEF_IM_STR; + + params->ps_str = GLOB_DEF_PS_STR; + params->m_str = GLOB_DEF_M_STR; + params->n_str = GLOB_DEF_N_STR; + params->k_str = GLOB_DEF_K_STR; + + params->nr_str = GLOB_DEF_NR_STR; + + params->alpha_str = GLOB_DEF_ALPHA_STR; + params->beta_str = GLOB_DEF_BETA_STR; +} + diff --git a/eessi/testsuite/tests/libs/blas/src/test_hemm.c b/eessi/testsuite/tests/libs/blas/src/test_hemm.c new file mode 100644 index 00000000..bf60d7a3 --- /dev/null +++ b/eessi/testsuite/tests/libs/blas/src/test_hemm.c @@ -0,0 +1,373 @@ +/* + + BLIS + An object-based framework for developing high-performance BLAS-like + libraries. + + Copyright (C) 2014, The University of Texas at Austin + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of The University of Texas nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include "blis.h" +#include "test_utils.h" + +//#define PRINT + +static const char* LOCAL_OPNAME_STR = "hemm"; +static const char* LOCAL_PC_STR = "ll"; + +int main( int argc, char** argv ) +{ + obj_t a, b, c; + obj_t c_save; + obj_t alpha, beta; + dim_t m, n; + dim_t p; + dim_t p_begin, p_max, p_inc; + int m_input, n_input; + ind_t ind; + num_t dt; + char dt_ch; + int r, n_repeats; + side_t side; + uplo_t uploa; + f77_char f77_side; + f77_char f77_uploa; + + double dtime; + double dtime_save; + double gflops; + + params_t params; + + // Supress compiler warnings about unused variable 'ind'. + ( void )ind; + + + //bli_init(); + + //bli_error_checking_level_set( BLIS_NO_ERROR_CHECKING ); + + // Parse the command line options into strings, integers, enums, + // and doubles, as appropriate. + parse_cl_params( argc, argv, init_def_params, ¶ms ); + + dt = params.dt; + + ind = params.im; + + p_begin = params.sta; + p_max = params.end; + p_inc = params.inc; + + m_input = params.m; + n_input = params.n; + + n_repeats = params.nr; + + + // Map the datatype to its corresponding char. + bli_param_map_blis_to_char_dt( dt, &dt_ch ); + + // Map the parameter chars to their corresponding BLIS enum type values. + bli_param_map_char_to_blis_side( params.pc_str[0], &side ); + bli_param_map_char_to_blis_uplo( params.pc_str[1], &uploa ); + + // Map the BLIS enum type values to their corresponding BLAS chars. + bli_param_map_blis_to_netlib_side( side, &f77_side ); + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + + // Begin with initializing the last entry to zero so that + // matlab allocates space for the entire array once up-front. + for ( p = p_begin; p + p_inc <= p_max; p += p_inc ) ; + + printf( "data_%s_%chemm_%s", THR_STR, dt_ch, IMPL_STR ); + printf( "( %4lu, 1:3 ) = [ %5lu %5lu %8.2f ];\n", + ( unsigned long )(p - p_begin)/p_inc + 1, + ( unsigned long )0, + ( unsigned long )0, 0.0 ); + + + //for ( p = p_begin; p <= p_max; p += p_inc ) + for ( p = p_max; p_begin <= p; p -= p_inc ) + { + + if ( m_input < 0 ) m = p / ( dim_t )abs(m_input); + else m = ( dim_t ) m_input; + if ( n_input < 0 ) n = p / ( dim_t )abs(n_input); + else n = ( dim_t ) n_input; + + bli_obj_create( dt, 1, 1, 0, 0, &alpha ); + bli_obj_create( dt, 1, 1, 0, 0, &beta ); + + // Choose the storage of each matrix based on the corresponding + // char in the params_t struct. Note that the expected order of + // storage specifers in sc_str is CAB (not ABC). + if ( params.sc_str[1] == 'c' ) + { + if ( bli_is_left( side ) ) bli_obj_create( dt, m, m, 0, 0, &a ); + else bli_obj_create( dt, n, n, 0, 0, &a ); + } + else // if ( params.sc_str[1] == 'r' ) + { + if ( bli_is_left( side ) ) bli_obj_create( dt, m, m, m, 1, &a ); + else bli_obj_create( dt, n, n, n, 1, &a ); + } + + if ( params.sc_str[2] == 'c' ) bli_obj_create( dt, m, n, 0, 0, &b ); + else bli_obj_create( dt, m, n, n, 1, &b ); + + if ( params.sc_str[0] == 'c' ) bli_obj_create( dt, m, n, 0, 0, &c ); + else bli_obj_create( dt, m, n, n, 1, &c ); + + if ( params.sc_str[0] == 'c' ) bli_obj_create( dt, m, n, 0, 0, &c_save ); + else bli_obj_create( dt, m, n, n, 1, &c_save ); + + bli_randm( &a ); + bli_randm( &b ); + bli_randm( &c ); + + bli_obj_set_struc( BLIS_HERMITIAN, &a ); + bli_obj_set_uplo( uploa, &a ); + + // Make A densely Hermitian, and zero the unstored triangle to + // ensure the implementation reads only from the stored region. + bli_mkherm( &a ); + bli_mktrim( &a ); + + //bli_setsc( (2.0/1.0), 0.0, &alpha ); + //bli_setsc( (1.0/1.0), 0.0, &beta ); + bli_setsc( params.alpha, 0.0, &alpha ); + bli_setsc( params.beta, 0.0, &beta ); + + bli_copym( &c, &c_save ); + +#ifdef BLIS + // Switch to the induced method specified by ind, unless the 'auto' + // option was given, in which case we leave the induced method + // unchanged. + if ( params.im_is_auto == FALSE ) + { + bli_ind_disable_all_dt( dt ); + bli_ind_enable_dt( ind, dt ); + } +#endif + + dtime_save = DBL_MAX; + + for ( r = 0; r < n_repeats; ++r ) + { + bli_copym( &c_save, &c ); + + dtime = bli_clock(); + +#ifdef PRINT + bli_printm( "a", &a, "%4.1f", "" ); + bli_printm( "b", &b, "%4.1f", "" ); + bli_printm( "c", &c, "%4.1f", "" ); +#endif + +#ifdef BLIS + + bli_hemm( side, + &alpha, + &a, + &b, + &beta, + &c ); + +#else + + if ( bli_is_float( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int nn = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldb = bli_obj_col_stride( &b ); + f77_int ldc = bli_obj_col_stride( &c ); + float* alphap = ( float* )bli_obj_buffer( &alpha ); + float* ap = ( float* )bli_obj_buffer( &a ); + float* bp = ( float* )bli_obj_buffer( &b ); + float* betap = ( float* )bli_obj_buffer( &beta ); + float* cp = ( float* )bli_obj_buffer( &c ); + + ssymm_( &f77_side, + &f77_uploa, + &mm, + &nn, + alphap, + ap, &lda, + bp, &ldb, + betap, + cp, &ldc ); + } + else if ( bli_is_double( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int nn = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldb = bli_obj_col_stride( &b ); + f77_int ldc = bli_obj_col_stride( &c ); + double* alphap = ( double* )bli_obj_buffer( &alpha ); + double* ap = ( double* )bli_obj_buffer( &a ); + double* bp = ( double* )bli_obj_buffer( &b ); + double* betap = ( double* )bli_obj_buffer( &beta ); + double* cp = ( double* )bli_obj_buffer( &c ); + + dsymm_( &f77_side, + &f77_uploa, + &mm, + &nn, + alphap, + ap, &lda, + bp, &ldb, + betap, + cp, &ldc ); + } + else if ( bli_is_scomplex( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int nn = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldb = bli_obj_col_stride( &b ); + f77_int ldc = bli_obj_col_stride( &c ); +#ifdef EIGEN + float* alphap = ( float* )bli_obj_buffer( &alpha ); + float* ap = ( float* )bli_obj_buffer( &a ); + float* bp = ( float* )bli_obj_buffer( &b ); + float* betap = ( float* )bli_obj_buffer( &beta ); + float* cp = ( float* )bli_obj_buffer( &c ); +#else + scomplex* alphap = ( scomplex* )bli_obj_buffer( &alpha ); + scomplex* ap = ( scomplex* )bli_obj_buffer( &a ); + scomplex* bp = ( scomplex* )bli_obj_buffer( &b ); + scomplex* betap = ( scomplex* )bli_obj_buffer( &beta ); + scomplex* cp = ( scomplex* )bli_obj_buffer( &c ); +#endif + + chemm_( &f77_side, + &f77_uploa, + &mm, + &nn, + alphap, + ap, &lda, + bp, &ldb, + betap, + cp, &ldc ); + } + else if ( bli_is_dcomplex( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int nn = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldb = bli_obj_col_stride( &b ); + f77_int ldc = bli_obj_col_stride( &c ); +#ifdef EIGEN + double* alphap = ( double* )bli_obj_buffer( &alpha ); + double* ap = ( double* )bli_obj_buffer( &a ); + double* bp = ( double* )bli_obj_buffer( &b ); + double* betap = ( double* )bli_obj_buffer( &beta ); + double* cp = ( double* )bli_obj_buffer( &c ); +#else + dcomplex* alphap = ( dcomplex* )bli_obj_buffer( &alpha ); + dcomplex* ap = ( dcomplex* )bli_obj_buffer( &a ); + dcomplex* bp = ( dcomplex* )bli_obj_buffer( &b ); + dcomplex* betap = ( dcomplex* )bli_obj_buffer( &beta ); + dcomplex* cp = ( dcomplex* )bli_obj_buffer( &c ); +#endif + + zhemm_( &f77_side, + &f77_uploa, + &mm, + &nn, + alphap, + ap, &lda, + bp, &ldb, + betap, + cp, &ldc ); + } +#endif + +#ifdef PRINT + bli_printm( "c after", &c, "%4.1f", "" ); + exit(1); +#endif + + dtime_save = bli_clock_min_diff( dtime_save, dtime ); + } + + if ( bli_is_left( side ) ) + gflops = ( 2.0 * m * m * n ) / ( dtime_save * 1.0e9 ); + else + gflops = ( 2.0 * m * n * n ) / ( dtime_save * 1.0e9 ); + + if ( bli_is_complex( dt ) ) gflops *= 4.0; + + printf( "data_%s_%chemm_%s", THR_STR, dt_ch, IMPL_STR ); + printf( "( %4lu, 1:3 ) = [ %5lu %5lu %8.2f ];\n", + ( unsigned long )(p - p_begin)/p_inc + 1, + ( unsigned long )m, + ( unsigned long )n, gflops ); + fflush( stdout ); + + bli_obj_free( &alpha ); + bli_obj_free( &beta ); + + bli_obj_free( &a ); + bli_obj_free( &b ); + bli_obj_free( &c ); + bli_obj_free( &c_save ); + } + + //bli_finalize(); + + return 0; +} + +void init_def_params( params_t* params ) +{ + params->opname = LOCAL_OPNAME_STR; + params->impl = IMPL_STR; + + params->pc_str = LOCAL_PC_STR; + params->dt_str = GLOB_DEF_DT_STR; + params->sc_str = GLOB_DEF_SC_STR; + + params->im_str = GLOB_DEF_IM_STR; + + params->ps_str = GLOB_DEF_PS_STR; + params->m_str = GLOB_DEF_M_STR; + params->n_str = GLOB_DEF_N_STR; + params->k_str = GLOB_DEF_K_STR; + + params->nr_str = GLOB_DEF_NR_STR; + + params->alpha_str = GLOB_DEF_ALPHA_STR; + params->beta_str = GLOB_DEF_BETA_STR; +} + diff --git a/eessi/testsuite/tests/libs/blas/src/test_herk.c b/eessi/testsuite/tests/libs/blas/src/test_herk.c new file mode 100644 index 00000000..9eda317c --- /dev/null +++ b/eessi/testsuite/tests/libs/blas/src/test_herk.c @@ -0,0 +1,352 @@ +/* + + BLIS + An object-based framework for developing high-performance BLAS-like + libraries. + + Copyright (C) 2014, The University of Texas at Austin + Copyright (C) 2018 - 2019, Advanced Micro Devices, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of The University of Texas nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include "blis.h" +#include "test_utils.h" + +//#define PRINT + +static const char* LOCAL_OPNAME_STR = "herk"; +static const char* LOCAL_PC_STR = "ln"; + +int main( int argc, char** argv ) +{ + obj_t a, c; + obj_t c_save; + obj_t alpha, beta; + dim_t m, k; + dim_t p; + dim_t p_begin, p_max, p_inc; + int m_input, k_input; + ind_t ind; + num_t dt, dt_real; + char dt_ch; + int r, n_repeats; + uplo_t uploc; + trans_t transa; + f77_char f77_uploc; + f77_char f77_transa; + + double dtime; + double dtime_save; + double gflops; + + params_t params; + + // Supress compiler warnings about unused variable 'ind'. + ( void )ind; + + + //bli_init(); + + //bli_error_checking_level_set( BLIS_NO_ERROR_CHECKING ); + + // Parse the command line options into strings, integers, enums, + // and doubles, as appropriate. + parse_cl_params( argc, argv, init_def_params, ¶ms ); + + dt = params.dt; + dt_real = bli_dt_proj_to_real( dt ); + + ind = params.im; + + p_begin = params.sta; + p_max = params.end; + p_inc = params.inc; + + m_input = params.m; + k_input = params.k; + + n_repeats = params.nr; + + + // Map the datatype to its corresponding char. + bli_param_map_blis_to_char_dt( dt, &dt_ch ); + + // Map the parameter chars to their corresponding BLIS enum type values. + bli_param_map_char_to_blis_uplo( params.pc_str[0], &uploc ); + bli_param_map_char_to_blis_trans( params.pc_str[1], &transa ); + + // Map the BLIS enum type values to their corresponding BLAS chars. + bli_param_map_blis_to_netlib_uplo( uploc, &f77_uploc ); + bli_param_map_blis_to_netlib_trans( transa, &f77_transa ); + + // Begin with initializing the last entry to zero so that + // matlab allocates space for the entire array once up-front. + for ( p = p_begin; p + p_inc <= p_max; p += p_inc ) ; + + printf( "data_%s_%cherk_%s", THR_STR, dt_ch, IMPL_STR ); + printf( "( %4lu, 1:3 ) = [ %5lu %5lu %8.2f ];\n", + ( unsigned long )(p - p_begin)/p_inc + 1, + ( unsigned long )0, + ( unsigned long )0, 0.0 ); + + + //for ( p = p_begin; p <= p_max; p += p_inc ) + for ( p = p_max; p_begin <= p; p -= p_inc ) + { + + if ( m_input < 0 ) m = p / ( dim_t )abs(m_input); + else m = ( dim_t ) m_input; + if ( k_input < 0 ) k = p / ( dim_t )abs(k_input); + else k = ( dim_t ) k_input; + + bli_obj_create( dt_real, 1, 1, 0, 0, &alpha ); + bli_obj_create( dt, 1, 1, 0, 0, &beta ); + + // Choose the storage of each matrix based on the corresponding + // char in the params_t struct. Note that the expected order of + // storage specifers in sc_str is CA (not AC). + if ( params.sc_str[1] == 'c' ) + { + if ( bli_does_trans( transa ) ) bli_obj_create( dt, k, m, 0, 0, &a ); + else bli_obj_create( dt, m, k, 0, 0, &a ); + } + else // if ( params.sc_str[1] == 'r' ) + { + if ( bli_does_trans( transa ) ) bli_obj_create( dt, k, m, m, 1, &a ); + else bli_obj_create( dt, m, k, k, 1, &a ); + } + + if ( params.sc_str[0] == 'c' ) bli_obj_create( dt, m, m, 0, 0, &c ); + else bli_obj_create( dt, m, m, m, 1, &c ); + + if ( params.sc_str[0] == 'c' ) bli_obj_create( dt, m, m, 0, 0, &c_save ); + else bli_obj_create( dt, m, m, m, 1, &c_save ); + + bli_randm( &a ); + bli_randm( &c ); + + bli_obj_set_struc( BLIS_HERMITIAN, &c ); + bli_obj_set_uplo( uploc, &c ); + + // Make C densely Hermitian, and zero the unstored triangle to + // ensure the implementation reads only from the stored region. + bli_mkherm( &c ); + bli_mktrim( &c ); + + bli_obj_set_conjtrans( transa, &a ); + + //bli_setsc( (2.0/1.0), 0.0, &alpha ); + //bli_setsc( (1.0/1.0), 0.0, &beta ); + bli_setsc( params.alpha, 0.0, &alpha ); + bli_setsc( params.beta, 0.0, &beta ); + + bli_copym( &c, &c_save ); + +#ifdef BLIS + // Switch to the induced method specified by ind, unless the 'auto' + // option was given, in which case we leave the induced method + // unchanged. + if ( params.im_is_auto == FALSE ) + { + bli_ind_disable_all_dt( dt ); + bli_ind_enable_dt( ind, dt ); + } +#endif + + dtime_save = DBL_MAX; + + for ( r = 0; r < n_repeats; ++r ) + { + bli_copym( &c_save, &c ); + + dtime = bli_clock(); + +#ifdef PRINT + bli_printm( "a", &a, "%4.1f", "" ); + bli_printm( "c", &c, "%4.1f", "" ); +#endif + +#ifdef BLIS + + bli_herk( &alpha, + &a, + &beta, + &c ); + +#else + + if ( bli_is_float( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width_after_trans( &a ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldc = bli_obj_col_stride( &c ); + float* alphap = ( float* )bli_obj_buffer( &alpha ); + float* ap = ( float* )bli_obj_buffer( &a ); + float* betap = ( float* )bli_obj_buffer( &beta ); + float* cp = ( float* )bli_obj_buffer( &c ); + + ssyrk_( &f77_uploc, + &f77_transa, + &mm, + &kk, + alphap, + ap, &lda, + betap, + cp, &ldc ); + } + else if ( bli_is_double( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width_after_trans( &a ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldc = bli_obj_col_stride( &c ); + double* alphap = ( double* )bli_obj_buffer( &alpha ); + double* ap = ( double* )bli_obj_buffer( &a ); + double* betap = ( double* )bli_obj_buffer( &beta ); + double* cp = ( double* )bli_obj_buffer( &c ); + + dsyrk_( &f77_uploc, + &f77_transa, + &mm, + &kk, + alphap, + ap, &lda, + betap, + cp, &ldc ); + } + else if ( bli_is_scomplex( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width_after_trans( &a ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldc = bli_obj_col_stride( &c ); +#ifdef EIGEN + float* alphap = ( float* )bli_obj_buffer( &alpha ); + float* ap = ( float* )bli_obj_buffer( &a ); + float* betap = ( float* )bli_obj_buffer( &beta ); + float* cp = ( float* )bli_obj_buffer( &c ); +#else + float* alphap = ( float* )bli_obj_buffer( &alpha ); + scomplex* ap = ( scomplex* )bli_obj_buffer( &a ); + float* betap = ( float* )bli_obj_buffer( &beta ); + scomplex* cp = ( scomplex* )bli_obj_buffer( &c ); +#endif + + cherk_( &f77_uploc, + &f77_transa, + &mm, + &kk, + alphap, + ap, &lda, + betap, + cp, &ldc ); + } + else if ( bli_is_dcomplex( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width_after_trans( &a ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldc = bli_obj_col_stride( &c ); +#ifdef EIGEN + double* alphap = ( double* )bli_obj_buffer( &alpha ); + double* ap = ( double* )bli_obj_buffer( &a ); + double* betap = ( double* )bli_obj_buffer( &beta ); + double* cp = ( double* )bli_obj_buffer( &c ); +#else + double* alphap = ( double* )bli_obj_buffer( &alpha ); + dcomplex* ap = ( dcomplex* )bli_obj_buffer( &a ); + double* betap = ( double* )bli_obj_buffer( &beta ); + dcomplex* cp = ( dcomplex* )bli_obj_buffer( &c ); +#endif + + zherk_( &f77_uploc, + &f77_transa, + &mm, + &kk, + alphap, + ap, &lda, + betap, + cp, &ldc ); + } +#endif + +#ifdef PRINT + bli_printm( "c after", &c, "%4.1f", "" ); + exit(1); +#endif + + dtime_save = bli_clock_min_diff( dtime_save, dtime ); + } + + gflops = ( 1.0 * m * k * m ) / ( dtime_save * 1.0e9 ); + + if ( bli_is_complex( dt ) ) gflops *= 4.0; + + printf( "data_%s_%cherk_%s", THR_STR, dt_ch, IMPL_STR ); + printf( "( %4lu, 1:3 ) = [ %5lu %5lu %8.2f ];\n", + ( unsigned long )(p - p_begin)/p_inc + 1, + ( unsigned long )m, + ( unsigned long )k, gflops ); + fflush( stdout ); + + bli_obj_free( &alpha ); + bli_obj_free( &beta ); + + bli_obj_free( &a ); + bli_obj_free( &c ); + bli_obj_free( &c_save ); + } + + //bli_finalize(); + + return 0; +} + +void init_def_params( params_t* params ) +{ + params->opname = LOCAL_OPNAME_STR; + params->impl = IMPL_STR; + + params->pc_str = LOCAL_PC_STR; + params->dt_str = GLOB_DEF_DT_STR; + params->sc_str = GLOB_DEF_SC_STR; + + params->im_str = GLOB_DEF_IM_STR; + + params->ps_str = GLOB_DEF_PS_STR; + params->m_str = GLOB_DEF_M_STR; + params->n_str = GLOB_DEF_N_STR; + params->k_str = GLOB_DEF_K_STR; + + params->nr_str = GLOB_DEF_NR_STR; + + params->alpha_str = GLOB_DEF_ALPHA_STR; + params->beta_str = GLOB_DEF_BETA_STR; +} + diff --git a/eessi/testsuite/tests/libs/blas/src/test_trmm.c b/eessi/testsuite/tests/libs/blas/src/test_trmm.c new file mode 100644 index 00000000..2aa17c29 --- /dev/null +++ b/eessi/testsuite/tests/libs/blas/src/test_trmm.c @@ -0,0 +1,355 @@ +/* + + BLIS + An object-based framework for developing high-performance BLAS-like + libraries. + + Copyright (C) 2014, The University of Texas at Austin + Copyright (C) 2018 - 2019, Advanced Micro Devices, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of The University of Texas nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include "blis.h" +#include "test_utils.h" + +//#define PRINT + +static const char* LOCAL_OPNAME_STR = "trmm"; +static const char* LOCAL_PC_STR = "llnn"; + +int main( int argc, char** argv ) +{ + obj_t a, c; + obj_t c_save; + obj_t alpha; + dim_t m, n; + dim_t p; + dim_t p_begin, p_max, p_inc; + int m_input, n_input; + ind_t ind; + num_t dt; + char dt_ch; + int r, n_repeats; + side_t side; + uplo_t uploa; + trans_t transa; + diag_t diaga; + f77_char f77_side; + f77_char f77_uploa; + f77_char f77_transa; + f77_char f77_diaga; + + double dtime; + double dtime_save; + double gflops; + + params_t params; + + // Supress compiler warnings about unused variable 'ind'. + ( void )ind; + + + //bli_init(); + + //bli_error_checking_level_set( BLIS_NO_ERROR_CHECKING ); + + // Parse the command line options into strings, integers, enums, + // and doubles, as appropriate. + parse_cl_params( argc, argv, init_def_params, ¶ms ); + + dt = params.dt; + + ind = params.im; + + p_begin = params.sta; + p_max = params.end; + p_inc = params.inc; + + m_input = params.m; + n_input = params.n; + + n_repeats = params.nr; + + + // Map the datatype to its corresponding char. + bli_param_map_blis_to_char_dt( dt, &dt_ch ); + + // Map the parameter chars to their corresponding BLIS enum type values. + bli_param_map_char_to_blis_side( params.pc_str[0], &side ); + bli_param_map_char_to_blis_uplo( params.pc_str[1], &uploa ); + bli_param_map_char_to_blis_trans( params.pc_str[2], &transa ); + bli_param_map_char_to_blis_diag( params.pc_str[3], &diaga ); + + // Map the BLIS enum type values to their corresponding BLAS chars. + bli_param_map_blis_to_netlib_side( side, &f77_side ); + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + bli_param_map_blis_to_netlib_trans( transa, &f77_transa ); + bli_param_map_blis_to_netlib_diag( diaga, &f77_diaga ); + + // Begin with initializing the last entry to zero so that + // matlab allocates space for the entire array once up-front. + for ( p = p_begin; p + p_inc <= p_max; p += p_inc ) ; + + printf( "data_%s_%ctrmm_%s", THR_STR, dt_ch, IMPL_STR ); + printf( "( %4lu, 1:3 ) = [ %5lu %5lu %8.2f ];\n", + ( unsigned long )(p - p_begin)/p_inc + 1, + ( unsigned long )0, + ( unsigned long )0, 0.0 ); + + + //for ( p = p_begin; p <= p_max; p += p_inc ) + for ( p = p_max; p_begin <= p; p -= p_inc ) + { + + if ( m_input < 0 ) m = p / ( dim_t )abs(m_input); + else m = ( dim_t ) m_input; + if ( n_input < 0 ) n = p / ( dim_t )abs(n_input); + else n = ( dim_t ) n_input; + + bli_obj_create( dt, 1, 1, 0, 0, &alpha ); + + // Choose the storage of each matrix based on the corresponding + // char in the params_t struct. Note that the expected order of + // storage specifers in sc_str is CA (not AC). Also note that + // C plays the role of matrix B. + if ( params.sc_str[1] == 'c' ) + { + if ( bli_is_left( side ) ) bli_obj_create( dt, m, m, 0, 0, &a ); + else bli_obj_create( dt, n, n, 0, 0, &a ); + } + else // if ( params.sc_str[1] == 'r' ) + { + if ( bli_is_left( side ) ) bli_obj_create( dt, m, m, m, 1, &a ); + else bli_obj_create( dt, n, n, n, 1, &a ); + } + + if ( params.sc_str[0] == 'c' ) bli_obj_create( dt, m, n, 0, 0, &c ); + else bli_obj_create( dt, m, n, n, 1, &c ); + + if ( params.sc_str[0] == 'c' ) bli_obj_create( dt, m, n, 0, 0, &c_save ); + else bli_obj_create( dt, m, n, n, 1, &c_save ); + + bli_randm( &a ); + bli_randm( &c ); + + bli_obj_set_struc( BLIS_TRIANGULAR, &a ); + bli_obj_set_uplo( uploa, &a ); + bli_obj_set_conjtrans( transa, &a ); + bli_obj_set_diag( diaga, &a ); + + // Zero the unstored triangle. + bli_mktrim( &a ); + + //bli_setsc( (2.0/1.0), 0.0, &alpha ); + bli_setsc( params.alpha, 0.0, &alpha ); + + bli_copym( &c, &c_save ); + +#ifdef BLIS + // Switch to the induced method specified by ind, unless the 'auto' + // option was given, in which case we leave the induced method + // unchanged. + if ( params.im_is_auto == FALSE ) + { + bli_ind_disable_all_dt( dt ); + bli_ind_enable_dt( ind, dt ); + } +#endif + + dtime_save = DBL_MAX; + + for ( r = 0; r < n_repeats; ++r ) + { + bli_copym( &c_save, &c ); + + dtime = bli_clock(); + +#ifdef PRINT + bli_printm( "a", &a, "%4.1f", "" ); + bli_printm( "c", &c, "%4.1f", "" ); +#endif + +#ifdef BLIS + + bli_trmm( side, + &alpha, + &a, + &c ); + +#else + + if ( bli_is_float( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldc = bli_obj_col_stride( &c ); + float* alphap = ( float* )bli_obj_buffer( &alpha ); + float* ap = ( float* )bli_obj_buffer( &a ); + float* cp = ( float* )bli_obj_buffer( &c ); + + strmm_( &f77_side, + &f77_uploa, + &f77_transa, + &f77_diaga, + &mm, + &kk, + alphap, + ap, &lda, + cp, &ldc ); + } + else if ( bli_is_double( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldc = bli_obj_col_stride( &c ); + double* alphap = ( double* )bli_obj_buffer( &alpha ); + double* ap = ( double* )bli_obj_buffer( &a ); + double* cp = ( double* )bli_obj_buffer( &c ); + + dtrmm_( &f77_side, + &f77_uploa, + &f77_transa, + &f77_diaga, + &mm, + &kk, + alphap, + ap, &lda, + cp, &ldc ); + } + else if ( bli_is_scomplex( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldc = bli_obj_col_stride( &c ); +#ifdef EIGEN + float* alphap = ( float* )bli_obj_buffer( &alpha ); + float* ap = ( float* )bli_obj_buffer( &a ); + float* cp = ( float* )bli_obj_buffer( &c ); +#else + scomplex* alphap = ( scomplex* )bli_obj_buffer( &alpha ); + scomplex* ap = ( scomplex* )bli_obj_buffer( &a ); + scomplex* cp = ( scomplex* )bli_obj_buffer( &c ); +#endif + + ctrmm_( &f77_side, + &f77_uploa, + &f77_transa, + &f77_diaga, + &mm, + &kk, + alphap, + ap, &lda, + cp, &ldc ); + } + else if ( bli_is_dcomplex( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldc = bli_obj_col_stride( &c ); +#ifdef EIGEN + double* alphap = ( double* )bli_obj_buffer( &alpha ); + double* ap = ( double* )bli_obj_buffer( &a ); + double* cp = ( double* )bli_obj_buffer( &c ); +#else + dcomplex* alphap = ( dcomplex* )bli_obj_buffer( &alpha ); + dcomplex* ap = ( dcomplex* )bli_obj_buffer( &a ); + dcomplex* cp = ( dcomplex* )bli_obj_buffer( &c ); +#endif + + ztrmm_( &f77_side, + &f77_uploa, + &f77_transa, + &f77_diaga, + &mm, + &kk, + alphap, + ap, &lda, + cp, &ldc ); + } +#endif + +#ifdef PRINT + bli_printm( "c after", &c, "%4.1f", "" ); + exit(1); +#endif + + dtime_save = bli_clock_min_diff( dtime_save, dtime ); + } + + if ( bli_is_left( side ) ) + gflops = ( 1.0 * m * m * n ) / ( dtime_save * 1.0e9 ); + else + gflops = ( 1.0 * m * n * n ) / ( dtime_save * 1.0e9 ); + + if ( bli_is_complex( dt ) ) gflops *= 4.0; + + printf( "data_%s_%ctrmm_%s", THR_STR, dt_ch, IMPL_STR ); + printf( "( %4lu, 1:3 ) = [ %5lu %5lu %8.2f ];\n", + ( unsigned long )(p - p_begin)/p_inc + 1, + ( unsigned long )m, + ( unsigned long )n, gflops ); + fflush( stdout ); + + bli_obj_free( &alpha ); + + bli_obj_free( &a ); + bli_obj_free( &c ); + bli_obj_free( &c_save ); + } + + //bli_finalize(); + + return 0; +} + +void init_def_params( params_t* params ) +{ + params->opname = LOCAL_OPNAME_STR; + params->impl = IMPL_STR; + + params->pc_str = LOCAL_PC_STR; + params->dt_str = GLOB_DEF_DT_STR; + params->sc_str = GLOB_DEF_SC_STR; + + params->im_str = GLOB_DEF_IM_STR; + + params->ps_str = GLOB_DEF_PS_STR; + params->m_str = GLOB_DEF_M_STR; + params->n_str = GLOB_DEF_N_STR; + params->k_str = GLOB_DEF_K_STR; + + params->nr_str = GLOB_DEF_NR_STR; + + params->alpha_str = GLOB_DEF_ALPHA_STR; + params->beta_str = GLOB_DEF_BETA_STR; +} + diff --git a/eessi/testsuite/tests/libs/blas/src/test_trsm.c b/eessi/testsuite/tests/libs/blas/src/test_trsm.c new file mode 100644 index 00000000..33a45f5b --- /dev/null +++ b/eessi/testsuite/tests/libs/blas/src/test_trsm.c @@ -0,0 +1,358 @@ +/* + + BLIS + An object-based framework for developing high-performance BLAS-like + libraries. + + Copyright (C) 2014, The University of Texas at Austin + Copyright (C) 2018 - 2019, Advanced Micro Devices, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of The University of Texas nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include "blis.h" +#include "test_utils.h" + +//#define PRINT + +static const char* LOCAL_OPNAME_STR = "trsm"; +static const char* LOCAL_PC_STR = "llnn"; + +int main( int argc, char** argv ) +{ + obj_t a, c; + obj_t c_save; + obj_t alpha; + dim_t m, n; + dim_t p; + dim_t p_begin, p_max, p_inc; + int m_input, n_input; + ind_t ind; + num_t dt; + char dt_ch; + int r, n_repeats; + side_t side; + uplo_t uploa; + trans_t transa; + diag_t diaga; + f77_char f77_side; + f77_char f77_uploa; + f77_char f77_transa; + f77_char f77_diaga; + + double dtime; + double dtime_save; + double gflops; + + params_t params; + + // Supress compiler warnings about unused variable 'ind'. + ( void )ind; + + + //bli_init(); + + //bli_error_checking_level_set( BLIS_NO_ERROR_CHECKING ); + + // Parse the command line options into strings, integers, enums, + // and doubles, as appropriate. + parse_cl_params( argc, argv, init_def_params, ¶ms ); + + dt = params.dt; + + ind = params.im; + + p_begin = params.sta; + p_max = params.end; + p_inc = params.inc; + + m_input = params.m; + n_input = params.n; + + n_repeats = params.nr; + + + // Map the datatype to its corresponding char. + bli_param_map_blis_to_char_dt( dt, &dt_ch ); + + // Map the parameter chars to their corresponding BLIS enum type values. + bli_param_map_char_to_blis_side( params.pc_str[0], &side ); + bli_param_map_char_to_blis_uplo( params.pc_str[1], &uploa ); + bli_param_map_char_to_blis_trans( params.pc_str[2], &transa ); + bli_param_map_char_to_blis_diag( params.pc_str[3], &diaga ); + + // Map the BLIS enum type values to their corresponding BLAS chars. + bli_param_map_blis_to_netlib_side( side, &f77_side ); + bli_param_map_blis_to_netlib_uplo( uploa, &f77_uploa ); + bli_param_map_blis_to_netlib_trans( transa, &f77_transa ); + bli_param_map_blis_to_netlib_diag( diaga, &f77_diaga ); + + // Begin with initializing the last entry to zero so that + // matlab allocates space for the entire array once up-front. + for ( p = p_begin; p + p_inc <= p_max; p += p_inc ) ; + + printf( "data_%s_%ctrsm_%s", THR_STR, dt_ch, IMPL_STR ); + printf( "( %4lu, 1:3 ) = [ %5lu %5lu %8.2f ];\n", + ( unsigned long )(p - p_begin)/p_inc + 1, + ( unsigned long )0, + ( unsigned long )0, 0.0 ); + + + //for ( p = p_begin; p <= p_max; p += p_inc ) + for ( p = p_max; p_begin <= p; p -= p_inc ) + { + + if ( m_input < 0 ) m = p / ( dim_t )abs(m_input); + else m = ( dim_t ) m_input; + if ( n_input < 0 ) n = p / ( dim_t )abs(n_input); + else n = ( dim_t ) n_input; + + bli_obj_create( dt, 1, 1, 0, 0, &alpha ); + + // Choose the storage of each matrix based on the corresponding + // char in the params_t struct. Note that the expected order of + // storage specifers in sc_str is CA (not AC). Also note that + // C plays the role of matrix B. + if ( params.sc_str[1] == 'c' ) + { + if ( bli_is_left( side ) ) bli_obj_create( dt, m, m, 0, 0, &a ); + else bli_obj_create( dt, n, n, 0, 0, &a ); + } + else // if ( params.sc_str[1] == 'r' ) + { + if ( bli_is_left( side ) ) bli_obj_create( dt, m, m, m, 1, &a ); + else bli_obj_create( dt, n, n, n, 1, &a ); + } + + if ( params.sc_str[0] == 'c' ) bli_obj_create( dt, m, n, 0, 0, &c ); + else bli_obj_create( dt, m, n, n, 1, &c ); + + if ( params.sc_str[0] == 'c' ) bli_obj_create( dt, m, n, 0, 0, &c_save ); + else bli_obj_create( dt, m, n, n, 1, &c_save ); + + bli_randm( &a ); + bli_randm( &c ); + + bli_obj_set_struc( BLIS_TRIANGULAR, &a ); + bli_obj_set_uplo( uploa, &a ); + bli_obj_set_conjtrans( transa, &a ); + bli_obj_set_diag( diaga, &a ); + + // Zero the unstored triangle. + bli_mktrim( &a ); + + // Load the diagonal of A to make it more likely to be invertible. + bli_shiftd( &BLIS_TWO, &a ); + + //bli_setsc( (2.0/1.0), 0.0, &alpha ); + bli_setsc( params.alpha, 0.0, &alpha ); + + bli_copym( &c, &c_save ); + +#ifdef BLIS + // Switch to the induced method specified by ind, unless the 'auto' + // option was given, in which case we leave the induced method + // unchanged. + if ( params.im_is_auto == FALSE ) + { + bli_ind_disable_all_dt( dt ); + bli_ind_enable_dt( ind, dt ); + } +#endif + + dtime_save = DBL_MAX; + + for ( r = 0; r < n_repeats; ++r ) + { + bli_copym( &c_save, &c ); + + dtime = bli_clock(); + +#ifdef PRINT + bli_printm( "a", &a, "%4.1f", "" ); + bli_printm( "c", &c, "%4.1f", "" ); +#endif + +#ifdef BLIS + + bli_trsm( side, + &alpha, + &a, + &c ); + +#else + + if ( bli_is_float( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldc = bli_obj_col_stride( &c ); + float* alphap = ( float* )bli_obj_buffer( &alpha ); + float* ap = ( float* )bli_obj_buffer( &a ); + float* cp = ( float* )bli_obj_buffer( &c ); + + strsm_( &f77_side, + &f77_uploa, + &f77_transa, + &f77_diaga, + &mm, + &kk, + alphap, + ap, &lda, + cp, &ldc ); + } + else if ( bli_is_double( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldc = bli_obj_col_stride( &c ); + double* alphap = ( double* )bli_obj_buffer( &alpha ); + double* ap = ( double* )bli_obj_buffer( &a ); + double* cp = ( double* )bli_obj_buffer( &c ); + + dtrsm_( &f77_side, + &f77_uploa, + &f77_transa, + &f77_diaga, + &mm, + &kk, + alphap, + ap, &lda, + cp, &ldc ); + } + else if ( bli_is_scomplex( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldc = bli_obj_col_stride( &c ); +#ifdef EIGEN + float* alphap = ( float* )bli_obj_buffer( &alpha ); + float* ap = ( float* )bli_obj_buffer( &a ); + float* cp = ( float* )bli_obj_buffer( &c ); +#else + scomplex* alphap = ( scomplex* )bli_obj_buffer( &alpha ); + scomplex* ap = ( scomplex* )bli_obj_buffer( &a ); + scomplex* cp = ( scomplex* )bli_obj_buffer( &c ); +#endif + + ctrsm_( &f77_side, + &f77_uploa, + &f77_transa, + &f77_diaga, + &mm, + &kk, + alphap, + ap, &lda, + cp, &ldc ); + } + else if ( bli_is_dcomplex( dt ) ) + { + f77_int mm = bli_obj_length( &c ); + f77_int kk = bli_obj_width( &c ); + f77_int lda = bli_obj_col_stride( &a ); + f77_int ldc = bli_obj_col_stride( &c ); +#ifdef EIGEN + double* alphap = ( double* )bli_obj_buffer( &alpha ); + double* ap = ( double* )bli_obj_buffer( &a ); + double* cp = ( double* )bli_obj_buffer( &c ); +#else + dcomplex* alphap = ( dcomplex* )bli_obj_buffer( &alpha ); + dcomplex* ap = ( dcomplex* )bli_obj_buffer( &a ); + dcomplex* cp = ( dcomplex* )bli_obj_buffer( &c ); +#endif + + ztrsm_( &f77_side, + &f77_uploa, + &f77_transa, + &f77_diaga, + &mm, + &kk, + alphap, + ap, &lda, + cp, &ldc ); + } +#endif + +#ifdef PRINT + bli_printm( "c after", &c, "%4.1f", "" ); + exit(1); +#endif + + dtime_save = bli_clock_min_diff( dtime_save, dtime ); + } + + if ( bli_is_left( side ) ) + gflops = ( 1.0 * m * m * n ) / ( dtime_save * 1.0e9 ); + else + gflops = ( 1.0 * m * n * n ) / ( dtime_save * 1.0e9 ); + + if ( bli_is_complex( dt ) ) gflops *= 4.0; + + printf( "data_%s_%ctrsm_%s", THR_STR, dt_ch, IMPL_STR ); + printf( "( %4lu, 1:3 ) = [ %5lu %5lu %8.2f ];\n", + ( unsigned long )(p - p_begin)/p_inc + 1, + ( unsigned long )m, + ( unsigned long )n, gflops ); + fflush( stdout ); + + bli_obj_free( &alpha ); + + bli_obj_free( &a ); + bli_obj_free( &c ); + bli_obj_free( &c_save ); + } + + //bli_finalize(); + + return 0; +} + +void init_def_params( params_t* params ) +{ + params->opname = LOCAL_OPNAME_STR; + params->impl = IMPL_STR; + + params->pc_str = LOCAL_PC_STR; + params->dt_str = GLOB_DEF_DT_STR; + params->sc_str = GLOB_DEF_SC_STR; + + params->im_str = GLOB_DEF_IM_STR; + + params->ps_str = GLOB_DEF_PS_STR; + params->m_str = GLOB_DEF_M_STR; + params->n_str = GLOB_DEF_N_STR; + params->k_str = GLOB_DEF_K_STR; + + params->nr_str = GLOB_DEF_NR_STR; + + params->alpha_str = GLOB_DEF_ALPHA_STR; + params->beta_str = GLOB_DEF_BETA_STR; +} + diff --git a/eessi/testsuite/tests/libs/blas/src/test_utils.c b/eessi/testsuite/tests/libs/blas/src/test_utils.c new file mode 100644 index 00000000..3bdb3ec4 --- /dev/null +++ b/eessi/testsuite/tests/libs/blas/src/test_utils.c @@ -0,0 +1,694 @@ +/* + + BLIS + An object-based framework for developing high-performance BLAS-like + libraries. + + Copyright (C) 2022, The University of Texas at Austin + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name(s) of the copyright holder(s) nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "blis.h" +#include "test_utils.h" + +// Global string constants. +const char* GLOB_DEF_DT_STR = "d"; +const char* GLOB_DEF_SC_STR = "ccc"; +const char* GLOB_DEF_IM_STR = "auto"; + +const char* GLOB_DEF_PS_STR = "50 1000 50"; +const char* GLOB_DEF_M_STR = "-1"; +const char* GLOB_DEF_N_STR = "-1"; +const char* GLOB_DEF_K_STR = "-1"; + +const char* GLOB_DEF_NR_STR = "3"; + +const char* GLOB_DEF_ALPHA_STR = "1.0"; +const char* GLOB_DEF_BETA_STR = "1.0"; + + +void parse_cl_params( int argc, char** argv, init_fp fp, params_t* params ) +{ + bool gave_option_c = FALSE; + bool gave_option_d = FALSE; + bool gave_option_s = FALSE; + + bool gave_option_i = FALSE; + + bool gave_option_p = FALSE; + bool gave_option_m = FALSE; + bool gave_option_n = FALSE; + bool gave_option_k = FALSE; + + bool gave_option_r = FALSE; + + bool gave_option_a = FALSE; + bool gave_option_b = FALSE; + + int opt; + char opt_ch; + + getopt_t state; + + // Initialize the params_t struct with the caller-supplied function. + fp( params ); + + // Copy the binary name pointer so we can use it later. + params->bin = argv[0]; + + // Alias the binary name for conciseness. + const char* bin = params->bin; + + // Initialize the state for running bli_getopt(). Here, 0 is the + // initial value for opterr, which suppresses error messages. + bli_getopt_init_state( 0, &state ); + + // Process all option arguments until we get a -1, which means we're done. + while( (opt = bli_getopt( argc, ( const char* const * )argv, "c:d:s:i:p:m:n:k:r:a:b:qvh", &state )) != -1 ) + // while( (opt = bli_getopt( argc, argv, "c:d:s:i:p:m:n:k:r:a:b:qvh", &state )) != -1 ) + { + // Explicitly typecast opt, which is an int, to a char. (Failing to + // typecast resulted in at least one user-reported problem whereby + // opt was being filled with garbage.) + opt_ch = ( char )opt; + + switch( opt_ch ) + { + case 'c': + params->pc_str = state.optarg; + gave_option_c = TRUE; + break; + + case 'd': + params->dt_str = state.optarg; + gave_option_d = TRUE; + break; + + case 's': + params->sc_str = state.optarg; + gave_option_s = TRUE; + break; + + + case 'i': + params->im_str = state.optarg; + gave_option_i = TRUE; + break; + + + case 'p': + params->ps_str = state.optarg; + gave_option_p = TRUE; + break; + + case 'm': + params->m_str = state.optarg; + gave_option_m = TRUE; + break; + + case 'n': + params->n_str = state.optarg; + gave_option_n = TRUE; + break; + + case 'k': + params->k_str = state.optarg; + gave_option_k = TRUE; + break; + + + case 'r': + params->nr_str = state.optarg; + gave_option_r = TRUE; + break; + + + case 'a': + params->alpha_str = state.optarg; + gave_option_a = TRUE; + break; + + case 'b': + params->beta_str = state.optarg; + gave_option_b = TRUE; + break; + + + case 'q': + params->verbose = FALSE; + break; + + case 'v': + params->verbose = TRUE; + break; + + case 'h': + { + bool has_trans = FALSE; + bool has_side = FALSE; + bool has_uplo = FALSE; + bool has_unit = FALSE; + + if ( is_gemm( params ) || + is_herk( params ) || + is_trmm( params ) || + is_trsm( params ) ) has_trans = TRUE; + + if ( is_hemm( params ) || + is_trmm( params ) || + is_trsm( params ) ) has_side = TRUE; + + if ( is_hemm( params ) || + is_herk( params ) || + is_trmm( params ) || + is_trsm( params ) ) has_uplo = TRUE; + + if ( is_trmm( params ) || + is_trsm( params ) ) has_unit = TRUE; + + printf( "\n" ); + printf( " %s performance driver\n", params->opname ); + printf( " -----------------------\n" ); + printf( " (part of the BLIS framework)\n" ); + printf( "\n" ); + printf( " Measure performance of the '%s' implementation of the '%s' operation:\n", params->impl, params->opname ); + printf( "\n" ); + if ( is_gemm( params ) ) + { + printf( " C := beta * C + alpha * trans(A) * trans(B)\n" ); + printf( "\n" ); + printf( " where C is an m x n matrix, trans(A) is an m x k matrix, and\n" ); + printf( " trans(B) is a k x n matrix.\n" ); + } + else if ( is_hemm( params ) ) + { + printf( " C := beta * C + alpha * uplo(A) * B (side = left)\n" ); + printf( " C := beta * C + alpha * B * uplo(A) (side = right)\n" ); + printf( "\n" ); + printf( " where C and B are m x n matrices and A is a Hermitian matrix stored\n" ); + printf( " in the lower or upper triangle, as specified by uplo(A). When side =\n" ); + printf( " left, A is m x m, and when side = right, A is n x n.\n" ); + } + else if ( is_herk( params ) ) + { + printf( " uplo(C) := beta * uplo(C) + alpha * trans(A) * trans(A)^H\n" ); + printf( "\n" ); + printf( " where C is an m x m Hermitian matrix stored in the lower or upper\n" ); + printf( " triangle, as specified by uplo(C), and trans(A) is an m x k matrix.\n" ); + } + else if ( is_trmm( params ) ) + { + printf( " B := alpha * trans(uplo(A)) * B (side = left)\n" ); + printf( " B := alpha * B * trans(uplo(A)) (side = right)\n" ); + printf( "\n" ); + printf( " where B is an m x n matrix and A is a triangular matrix stored in\n" ); + printf( " the lower or upper triangle, as specified by uplo(A), with unit/non-unit\n" ); + printf( " diagonal specified by diag(A). When side = left, A is m x m, and when\n" ); + printf( " side = right, A is n x n.\n" ); + } + else if ( is_trsm( params ) ) + { + printf( " B := alpha * trans(uplo(A))^{-1} * B (side = left)\n" ); + printf( " B := alpha * B * trans(uplo(A))^{-1} (side = right)\n" ); + printf( "\n" ); + printf( " where B is an m x n matrix and A is a triangular matrix stored in\n" ); + printf( " the lower or upper triangle, as specified by uplo(A), with unit/non-unit\n" ); + printf( " diagonal specified by diag(A). When side = left, A is m x m, and when\n" ); + printf( " side = right, A is n x n. Note that while ^{-1} indicates inversion,\n" ); + printf( " trsm does not explicitly invert A, but rather solves for an m x n\n" ); + printf( " solution matrix X, which then overwrites the original contents of B.\n" ); + } + printf( "\n" ); + printf( " Performance measurements are taken for a range of problem sizes with a fixed\n" ); + printf( " set of parameters, and results are printed to stdout in a matlab/octave-\n" ); + printf( " friendly format.\n" ); + printf( "\n" ); + printf( " Usage:\n" ); + printf( "\n" ); + printf( " %s [options]\n", bin ); + printf( "\n" ); + printf( " The following computational options are supported:\n" ); + printf( "\n" ); + printf( " -c pc\n" ); + printf( " Use the operation-specific parameter combination specified by\n" ); + printf( " the 'pc' string. The following tables list expected parameters\n" ); + printf( " for the '%s' operation and the valid values for each parameter.\n", params->opname ); + printf( "\n" ); + printf( " Operation List (order) of parameters Example\n" ); + printf( " -------------------------------------------------------\n" ); + if ( is_gemm( params ) ) + { + printf( " gemm trans(A) trans(A) -c tn\n" ); + } + else if ( is_hemm( params ) ) + { + printf( " hemm/symm side(A) uplo(A) -c rl\n" ); + } + else if ( is_herk( params ) ) + { + printf( " herk/syrk uplo(C) trans(A) -c ln\n" ); + } + else if ( is_trmm( params ) ) + { + printf( " trmm side(A) uplo(A) trans(A) unit(A) -c lutn\n" ); + } + else if ( is_trsm( params ) ) + { + printf( " trsm side(A) uplo(A) trans(A) unit(A) -c rlnn\n" ); + } + printf( "\n" ); + printf( " Valid\n" ); + printf( " Param chars Interpretation\n" ); + printf( " ---------------------------------------\n" ); + if ( has_trans ) + { + printf( " trans n No transpose\n" ); + printf( " t Transpose only\n" ); + printf( " c Conjugate only*\n" ); + printf( " h Hermitian transpose\n" ); + printf( "\n" ); + } + if ( has_side ) + { + printf( " side l Left\n" ); + printf( " r Right\n" ); + printf( "\n" ); + } + if ( has_uplo ) + { + printf( " uplo l Lower-stored\n" ); + printf( " u Upper-stored\n" ); + printf( "\n" ); + } + if ( has_unit ) + { + printf( " unit u Unit diagonal\n" ); + printf( " n Non-unit diagonal\n" ); + printf( "\n" ); + } + if ( has_trans ) + { + printf( " *This option is supported by BLIS but not by classic BLAS.\n" ); + } + printf( "\n" ); + printf( " -d dt\n" ); + printf( " Allocate matrix elements using the datatype character specified\n" ); + printf( " by dt, and also perform computation in that same datatype. Valid\n" ); + printf( " char values for dt are:\n" ); + printf( "\n" ); + printf( " Valid\n" ); + printf( " chars Interpretation\n" ); + printf( " -----------------------------------------\n" ); + printf( " s single-precision real domain\n" ); + printf( " d double-precision real domain\n" ); + printf( " c single-precision complex domain\n" ); + printf( " z double-precision complex domain\n" ); + printf( "\n" ); + printf( " -s sc\n" ); + printf( " Use the characters in sc to determine the storage formats\n" ); + printf( " of each operand matrix used in the performance measurements.\n" ); + printf( " Valid chars are 'r' (row storage) and 'c' (column storage).\n" ); + printf( " The characters encode the storage format for each operand\n" ); + printf( " used by %s, with the mapping of chars to operand interpreted\n", params->opname ); + printf( " in the following order:\n" ); + printf( "\n" ); + printf( " Order of\n" ); + printf( " operand \n" ); + printf( " Operation mapping Example Interpretation\n" ); + printf( " ----------------------------------------------------------\n" ); + if ( is_gemm( params ) ) + { + printf( " gemm C A B -s crr C is col-stored;\n" ); + printf( " A and B are row-stored.\n" ); + } + else if ( is_hemm( params ) ) + { + printf( " hemm/symm C A B -s rcc C is row-stored;\n" ); + printf( " A and B are col-stored.\n" ); + } + else if ( is_herk( params ) ) + { + printf( " herk/syrk C A -s rc C is row-stored;\n" ); + printf( " A is col-stored.\n" ); + } + else if ( is_trmm( params ) ) + { + printf( " trmm B A -s cr B is col-stored;\n" ); + printf( " A is row-stored.\n" ); + } + else if ( is_trsm( params ) ) + { + printf( " trsm B A -s cc B and A are col-stored.\n" ); + } + printf( "\n" ); + printf( " -i im\n" ); + printf( " Use native execution if im is 'native' (or 'nat'). If im is '1m',\n" ); + printf( " use the 1m method to induce complex computation using the\n" ); + printf( " equivalent real-domain microkernels. If im is 'auto', do not\n" ); + printf( " explicitly set the induced method and instead use the default.\n" ); + printf( "\n" ); + printf( " -p 'lo hi in'\n" ); + printf( " Perform a sweep of measurements of problem sizes ranging from \n" ); + printf( " 'lo' to 'hi' in increments of 'in'. Note that measurements will\n" ); + printf( " be taken in descending order, starting from 'hi', and so 'lo'\n" ); + printf( " will act as a floor and may not be measured (see 2nd example).\n" ); + printf( "\n" ); + printf( " Example Interpretation\n" ); + printf( " -------------------------------------------------------\n" ); + printf( " -p '40 400 40' Measure performance from 40 to 400\n" ); + printf( " (inclusive) in increments of 40.\n" ); + printf( " -p '40 400 80' Measure performance for problem sizes\n" ); + printf( " {80,160,240,320,400}.\n" ); + printf( "\n" ); + printf( " Note that unlike the other option arguments, quotes are required\n" ); + printf( " around the 'lo hi in' string in order to facilitate parsing.\n" ); + printf( "\n" ); + printf( " -m M\n" ); + if ( is_gemm( params ) || is_hemm( params ) || is_trmm( params ) || is_trsm( params ) ) + printf( " -n N\n" ); + if ( is_gemm( params ) || is_herk( params ) ) + printf( " -k K\n" ); + if ( is_gemm( params ) ) + { + printf( " Bind the m, n, or k dimensions to M, N, or K, respectively.\n" ); + printf( " Binding of matrix dimensions takes place as follows:\n" ); + } + else if ( is_herk( params ) ) + { + printf( " Bind the m or k dimensions to M or K, respectively. Binding\n" ); + printf( " of matrix dimensions takes place as follows:\n" ); + } + else if ( is_hemm( params ) || is_trmm( params ) || is_trsm( params ) ) + { + printf( " Bind the m or n dimensions to M or N, respectively. Binding\n" ); + printf( " of matrix dimensions takes place as follows:\n" ); + } + printf( "\n" ); + printf( " if 0 < X: Bind the x dimension to X and hold it constant.\n" ); + printf( " if X = -1: Bind the x dimension to p.\n" ); + printf( " if X < -1: Bind the x dimension to p / abs(x).\n" ); + printf( "\n" ); + printf( " where p is the current problem size. Note: X = 0 is undefined.\n" ); + printf( "\n" ); + printf( " Examples Interpretation\n" ); + printf( " ---------------------------------------------------------\n" ); + if ( is_gemm( params ) ) + { + printf( " -m -1 -n -1 -k -1 Bind m, n, and k to the problem size\n" ); + printf( " to keep all matrices square.\n" ); + printf( " -m -1 -n -1 -k 100 Bind m and n to the problem size, but\n" ); + printf( " hold k = 100 constant.\n" ); + } + else if ( is_hemm( params ) ) + { + printf( " -m -1 -n -1 Bind m and n to the problem size to\n" ); + printf( " keep all matrices square.\n" ); + printf( " -m -1 -n 500 Bind m to the problem size, but hold\n" ); + printf( " n = 500 constant.\n" ); + } + else if ( is_herk( params ) ) + { + printf( " -m -1 -k -1 Bind m and k to the problem size to\n" ); + printf( " keep both matrices square.\n" ); + printf( " -m -1 -k 200 Bind m to the problem size, but hold\n" ); + printf( " k = 200 constant.\n" ); + } + else if ( is_trmm( params ) || is_trsm( params ) ) + { + printf( " -m -1 -n -1 Bind m and n to the problem size to\n" ); + printf( " keep both matrices square.\n" ); + printf( " -m -1 -n 300 Bind m to the problem size, but hold\n" ); + printf( " n = 300 constant.\n" ); + } + printf( "\n" ); + printf( " -r num\n" ); + printf( " When measuring performance for a given problem size, perform num\n" ); + printf( " repetitions and report performance using the best timing.\n" ); + printf( "\n" ); + if ( is_gemm( params ) || is_hemm( params ) || is_herk( params ) ) + { + printf( " -a alpha\n" ); + printf( " -b beta\n" ); + printf( " Specify the value to use for the alpha and/or beta scalars.\n" ); + } + else // if ( is_trmm( params ) || is_trsm( params ) ) + { + printf( " -a alpha\n" ); + printf( " Specify the value to use for the alpha scalar.\n" ); + } + printf( "\n" ); + printf( " If any of the computational options is not specified, its default value will\n" ); + printf( " be used. (Please use the -v option to see how the driver is interpreting each\n" ); + printf( " option.)\n" ); + printf( "\n" ); + printf( " The following IO options are also supported:\n" ); + printf( "\n" ); + printf( " -q\n" ); + printf( " -v\n" ); + printf( " Enable quiet or verbose output. (By default, output is quiet.)\n" ); + printf( " The verbose option is useful if you are unsure whether your options\n" ); + printf( " are being interpreted as you intended.\n" ); + printf( "\n" ); + printf( " -h\n" ); + printf( " Display this help and exit.\n" ); + printf( "\n" ); + printf( "\n" ); + + exit(0); + + break; + } + + + case '?': + printf( "%s: unexpected option '%c' given or missing option argument\n", bin, state.optopt ); + exit(1); + break; + + default: + printf( "%s: unexpected option chararcter returned from getopt: %c\n", bin, opt_ch ); + exit(1); + } + } + + // Process the command line options from strings to integers/enums/doubles, + // as appropriate. + proc_params( params ); + + // Inform the user of the values that were chosen (or defaulted to). + if ( params->verbose ) + { + const char* def_str = " (default)"; + const char* nul_str = " "; + + printf( "%%\n" ); + printf( "%% operation: %s\n", params->opname ); + printf( "%% parameter combination: %s%s\n", params->pc_str, ( gave_option_c ? nul_str : def_str ) ); + printf( "%% datatype: %s%s\n", params->dt_str, ( gave_option_d ? nul_str : def_str ) ); + printf( "%% storage combination: %s%s\n", params->sc_str, ( gave_option_s ? nul_str : def_str ) ); + printf( "%% induced method: %s%s\n", params->im_str, ( gave_option_i ? nul_str : def_str ) ); + printf( "%% problem size range: %s%s\n", params->ps_str, ( gave_option_p ? nul_str : def_str ) ); + printf( "%% m dim specifier: %s%s\n", params->m_str, ( gave_option_m ? nul_str : def_str ) ); + if ( is_gemm( params ) || is_hemm( params ) || is_trmm( params ) || is_trsm( params ) ) + printf( "%% n dim specifier: %s%s\n", params->n_str, ( gave_option_n ? nul_str : def_str ) ); + if ( is_gemm( params ) || is_herk( params ) ) + printf( "%% k dim specifier: %s%s\n", params->k_str, ( gave_option_k ? nul_str : def_str ) ); + printf( "%% number of repeats: %s%s\n", params->nr_str, ( gave_option_r ? nul_str : def_str ) ); + printf( "%% alpha scalar: %s%s\n", params->alpha_str, ( gave_option_a ? nul_str : def_str ) ); + if ( is_gemm( params ) || is_hemm( params ) || is_herk( params ) ) + printf( "%% beta scalar: %s%s\n", params->beta_str, ( gave_option_b ? nul_str : def_str ) ); + printf( "%% ---\n" ); + printf( "%% implementation: %s\n", params->impl ); + if ( params->nt == -1 ) + printf( "%% number of threads: %s\n", "unset (defaults to 1)" ); + else + printf( "%% number of threads: %ld\n", params->nt ); + printf( "%% thread affinity: %s\n", ( params->af_str == NULL ? "unset" : params->af_str ) ); + printf( "%%\n" ); + } + + + // If there are still arguments remaining after getopt() processing is + // complete, print an error. + if ( state.optind < argc ) + { + printf( "%s: encountered unexpected non-option argument: %s\n", bin, argv[ state.optind ] ); + exit(1); + } +} + +// ----------------------------------------------------------------------------- + +void proc_params( params_t* params ) +{ + dim_t nt; + + // Binary name doesn't need any conversion. + + // Operation name doesn't need any conversion. + + // Implementation name doesn't need any conversion. + + // Query the multithreading strings and convert them to integers. + if ( strncmp( params->impl, "blis", MAX_STRING_SIZE ) == 0 ) + { + nt = bli_thread_get_num_threads(); + } + else if ( strncmp( params->impl, "mkl", MAX_STRING_SIZE ) == 0 ) + { + nt = bli_env_get_var( "OMP_NUM_THREADS", -1 ); + + if ( nt == -1 ) nt = bli_env_get_var( "MKL_NUM_THREADS", -1 ); + } + else if ( strncmp( params->impl, "openblas", MAX_STRING_SIZE ) == 0 ) + { + nt = bli_env_get_var( "OMP_NUM_THREADS", -1 ); + + if ( nt == -1 ) nt = bli_env_get_var( "OPENBLAS_NUM_THREADS", -1 ); + } + else + { + nt = bli_env_get_var( "OMP_NUM_THREADS", -1 ); + } + + // Store nt to the params_t struct. + params->nt = ( long int )nt; + + // Store the affinity string pointer to the params_t struct. + params->af_str = getenv( "GOMP_CPU_AFFINITY" ); + +#if 0 + dim_t nt = bli_thread_get_num_threads(); + dim_t jc_nt = bli_thread_get_jc_nt(); + dim_t pc_nt = bli_thread_get_pc_nt(); + dim_t ic_nt = bli_thread_get_ic_nt(); + dim_t jr_nt = bli_thread_get_jr_nt(); + dim_t ir_nt = bli_thread_get_ir_nt(); + + if ( nt == -1 ) nt = 1; + if ( jc_nt == -1 ) jc_nt = 1; + if ( pc_nt == -1 ) pc_nt = 1; + if ( ic_nt == -1 ) ic_nt = 1; + if ( jr_nt == -1 ) jr_nt = 1; + if ( ir_nt == -1 ) ir_nt = 1; + + params->nt = ( long int )nt; + params->jc_nt = ( long int )jc_nt; + params->pc_nt = ( long int )pc_nt; + params->ic_nt = ( long int )ic_nt; + params->jr_nt = ( long int )jr_nt; + params->ir_nt = ( long int )ir_nt; +#endif + + // Parameter combinations, datatype, and operand storage combination, + // need no conversion. + + // Convert the datatype to a num_t. + bli_param_map_char_to_blis_dt( params->dt_str[0], ¶ms->dt ); + + // Parse the induced method to the corresponding ind_t. + if ( strncmp( params->im_str, "native", 6 ) == 0 || + strncmp( params->im_str, "nat", 3 ) == 0 ) + { + params->im = BLIS_NAT; + params->im_is_auto = FALSE; + } + else if ( strncmp( params->im_str, "1m", 2 ) == 0 ) + { + params->im = BLIS_1M; + params->im_is_auto = FALSE; + } + else if ( strncmp( params->im_str, "auto", 4 ) == 0 ) + { + params->im = BLIS_1M; + params->im_is_auto = TRUE; + } + else + { + printf( "%s: invalid induced method '%s'.\n", params->bin, params->im_str ); + exit(1); + } + + // Convert the problem size range and dimension specifier strings to + // integers. + sscanf( params->ps_str, "%ld %ld %ld", &(params->sta), + &(params->end), + &(params->inc) ); + sscanf( params->m_str, "%ld", &(params->m) ); + sscanf( params->n_str, "%ld", &(params->n) ); + sscanf( params->k_str, "%ld", &(params->k) ); + + // Convert the number of repeats to an integer. + sscanf( params->nr_str, "%ld", &(params->nr) ); + + // Convert the alpha and beta strings to doubles. + //params->alpha = ( double )atof( params->alpha_str ); + //params->beta = ( double )atof( params->beta_str ); + //sscanf( params->alpha_str, "%lf", &(params->alpha) ); + //sscanf( params->beta_str, "%lf", &(params->beta) ); + params->alpha = strtod( params->alpha_str, NULL ); + params->beta = strtod( params->beta_str, NULL ); +} + +// ----------------------------------------------------------------------------- + +bool is_match( const char* str1, const char* str2 ) +{ + if ( strncmp( str1, str2, MAX_STRING_SIZE ) == 0 ) return TRUE; + return FALSE; +} + +bool is_gemm( params_t* params ) +{ + if ( is_match( params->opname, "gemm" ) ) return TRUE; + return FALSE; +} + +bool is_hemm( params_t* params ) +{ + if ( is_match( params->opname, "hemm" ) ) return TRUE; + return FALSE; +} + +bool is_herk( params_t* params ) +{ + if ( is_match( params->opname, "herk" ) ) return TRUE; + return FALSE; +} + +bool is_trmm( params_t* params ) +{ + if ( is_match( params->opname, "trmm" ) ) return TRUE; + return FALSE; +} + +bool is_trsm( params_t* params ) +{ + if ( is_match( params->opname, "trsm" ) ) return TRUE; + return FALSE; +} + diff --git a/eessi/testsuite/tests/libs/blas/src/test_utils.h b/eessi/testsuite/tests/libs/blas/src/test_utils.h new file mode 100644 index 00000000..aa75b44e --- /dev/null +++ b/eessi/testsuite/tests/libs/blas/src/test_utils.h @@ -0,0 +1,143 @@ +/* + + BLIS + An object-based framework for developing high-performance BLAS-like + libraries. + + Copyright (C) 2022, The University of Texas at Austin + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name(s) of the copyright holder(s) nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "blis.h" + +#ifndef TEST_UTILS_H +#define TEST_UTILS_H + +// Allow C++ users to include this header file in their source code. However, +// we make the extern "C" conditional on whether we're using a C++ compiler, +// since regular C compilers don't understand the extern "C" construct. +#ifdef __cplusplus +extern "C" { +#endif + +// String arrays allocated using this constant will always add 1 to +// the value defined below, and so the total allocated will still be +// a nice power of two. +#define MAX_STRING_SIZE 31 + + +extern const char* GLOB_DEF_DT_STR; +extern const char* GLOB_DEF_SC_STR; +extern const char* GLOB_DEF_IM_STR; + +extern const char* GLOB_DEF_PS_STR; +extern const char* GLOB_DEF_M_STR; +extern const char* GLOB_DEF_N_STR; +extern const char* GLOB_DEF_K_STR; + +extern const char* GLOB_DEF_NR_STR; + +extern const char* GLOB_DEF_ALPHA_STR; +extern const char* GLOB_DEF_BETA_STR; + + +typedef struct params_s +{ + // Binary name. + const char* bin; + + // Operation name. + const char* opname; + + // Implementation name. + const char* impl; + + // Multithreading parameters: number of threads and affinity string. + const char* nt_str; + long int nt; + const char* af_str; + + // Parameter combinations, datatype, operand storage combination, + // and induced method. + const char* pc_str; + const char* dt_str; + const char* sc_str; + num_t dt; + + const char* im_str; + ind_t im; + bool im_is_auto; + + // Problem size range and dimension specifiers. + const char* ps_str; + const char* m_str; + const char* n_str; + const char* k_str; + long int sta; + long int end; + long int inc; + long int m; + long int n; + long int k; + + // Number of repeats. + const char* nr_str; + long int nr; + + // Value of alpha and beta. + const char* alpha_str; + const char* beta_str; + double alpha; + double beta; + + // A flag controlling whether to print informational messages. + bool verbose; + +} params_t; + +typedef void (*init_fp)( params_t* params ); + +// ----------------------------------------------------------------------------- + +void init_def_params( params_t* params ); +void parse_cl_params( int argc, char** argv, init_fp fp, params_t* params ); +void proc_params( params_t* params ); + +// ----------------------------------------------------------------------------- + +bool is_match( const char* str1, const char* str2 ); +bool is_gemm( params_t* params ); +bool is_hemm( params_t* params ); +bool is_herk( params_t* params ); +bool is_trmm( params_t* params ); +bool is_trsm( params_t* params ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/eessi/testsuite/utils.py b/eessi/testsuite/utils.py index bea2e3ce..99f1835d 100644 --- a/eessi/testsuite/utils.py +++ b/eessi/testsuite/utils.py @@ -2,18 +2,36 @@ Utility functions for ReFrame tests """ +import os import re import sys -from typing import Iterator +from typing import Iterator, List import reframe as rfm +from reframe.core.exceptions import ReframeFatalError +import reframe.core.logging as rflog import reframe.core.runtime as rt from reframe.frontend.printer import PrettyPrinter -from eessi.testsuite.constants import * +from eessi.testsuite.constants import DEVICE_TYPES printer = PrettyPrinter() +# global variables +_available_modules = [] +_eb_is_available = False +_eb_avail_warning_is_printed = False + +try: + from easybuild.framework.easyconfig.easyconfig import get_toolchain_hierarchy + from easybuild.tools.options import set_up_configuration + # avoid checking index + os.environ['EASYBUILD_IGNORE_INDEX'] = '1' + set_up_configuration(args='') + _eb_is_available = True +except ImportError: + pass + def log(msg, logger=printer.debug): funcname = sys._getframe().f_back.f_code.co_name @@ -48,14 +66,71 @@ def is_gpu_present(test: rfm.RegressionTest) -> bool: return len(_get_gpu_list(test)) >= 1 -def is_cuda_required_module(module_name: str) -> bool: +def is_cuda_required_module(module_names: list) -> bool: '''Checks if CUDA seems to be required by given module''' requires_cuda = False - if re.search("(?i)cuda", module_name): - requires_cuda = True + for module in module_names: + if re.search("(?i)cuda", module): + requires_cuda = True return requires_cuda +def split_module(module: str) -> tuple: + """ + Split a full module name into (name, version, toolchain_name, toolchain_version, versionsuffix) + Assumptions: + 1) the module is formatted as any of the following: + - / + - /-- + - /-- + The following format is NOT supported unless exceptions are added: + - / + + 2) there are no hyphens in the version, toolchain_name, or toolchain_version unless exceptions are added + Exceptions: + - toolchain_name 'intel-compilers' + + Arguments: + - module: the full module name + """ + name, modversion = module.split('/') + parts = modversion.split('-') + version = parts[0] + versionsuffix = '' + + if len(parts) == 1: # system toolchain, no versionsuffix + parts.extend(['system', 'system']) + + # special casing intel-compilers: + if parts[1] == 'intel' and parts[2] == 'compilers': + parts = [parts[0], '-'.join(parts[1:3])] + parts[3:] + + tcname = parts[1] + tcversion = parts[2] + + if len(parts) >= 4: + versionsuffix = '-'.join(parts[3:]) + + return name, version, tcname, tcversion, versionsuffix + + +def get_avail_modules() -> List[str]: + "get all available modules in the system" + # use global to avoid recalculating the list of available modules multiple times + global _available_modules + if not _available_modules: + ms = rt.runtime().modules_system + # Returns e.g. ['Bison/', 'Bison/3.7.6-GCCcore-10.3.0', 'BLIS/', 'BLIS/0.8.1-GCC-10.3.0'] + _available_modules = ms.available_modules('') + # Exclude anything without version, i.e. ending with / (e.g. Bison/) + _available_modules = [mod for mod in _available_modules if not mod.endswith('/')] + log(f"Total number of available modules: {len(_available_modules)}") + if not _available_modules: + msg = 'No available modules found on the system.' + raise ReframeFatalError(msg) + return _available_modules + + def find_modules(regex: str, name_only=True) -> Iterator[str]: """ Return all modules matching the regular expression regex. Note that since we use re.search, @@ -91,15 +166,9 @@ def find_modules(regex: str, name_only=True) -> Iterator[str]: if not isinstance(regex, str): raise TypeError("'substr' argument must be a string") - ms = rt.runtime().modules_system - # Returns e.g. ['Bison/', 'Bison/3.7.6-GCCcore-10.3.0', 'BLIS/', 'BLIS/0.8.1-GCC-10.3.0'] - modules = ms.available_modules('') seen = set() dupes = [] - for mod in modules: - # Exclude anything without version, i.e. ending with / (e.g. Bison/) - if re.search('.*/$', mod): - continue + for mod in get_avail_modules(): # The thing we yield should always be the original module name (orig_mod), including version orig_mod = mod if name_only: @@ -124,6 +193,62 @@ def find_modules(regex: str, name_only=True) -> Iterator[str]: raise ValueError(err_msg) +def get_tc_hierarchy(tcdict): + """ + Set up EasyBuild configuration and get toolchain hierarchy from a toolchain dict + """ + global _eb_avail_warning_is_printed + if _eb_is_available: + hierarchy = get_toolchain_hierarchy(tcdict) + if not hierarchy: + msg = (f'cannot determine toolchain hierarchy for {tcdict}. ' + ' You may have to update the easybuild python package.') + rflog.getlogger().warning(msg) + return hierarchy + else: + if not _eb_avail_warning_is_printed: + msg = ("EasyBuild is not available, so cannot determine toolchain hierarchy." + " Make sure the easybuild python package is installed.") + rflog.getlogger().warning(msg) + _eb_avail_warning_is_printed = True + + +def select_matching_modules(modules: List[str], ref_module: str) -> List[str]: + """ + Return from a list of modules all modules that match the + toolchain of a reference module. + + Arguments: + - modules: list of modules from which a selection is made + - ref_module: the reference module + + Requirements: + - recent enough easybuild Python package + """ + + selected_mods = [] + + ref_tcname, ref_tcversion = split_module(ref_module)[2:4] + ref_tcdict = {'name': ref_tcname, 'version': ref_tcversion} + ref_hierarchy = get_tc_hierarchy(ref_tcdict) + if not ref_hierarchy: + return [] + + for mod in modules: + mod_tcname, mod_tcversion = split_module(mod)[2:4] + mod_tcdict = {'name': mod_tcname, 'version': mod_tcversion} + + mod_hierarchy = get_tc_hierarchy(mod_tcdict) + if not mod_hierarchy: + return [] + + # toolchain hierarchy does not contain super-toolchains, only sub-toolchains + if ref_tcdict in mod_hierarchy or mod_tcdict in ref_hierarchy: + selected_mods.append(mod) + + return selected_mods + + def check_proc_attribute_defined(test: rfm.RegressionTest, attribute) -> bool: """ Checks if a processor feature is defined (i.e. if test.current_partition.processor. is defined) diff --git a/pyproject.toml b/pyproject.toml index 616b63ff..ae53c4ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,9 @@ classifiers = [ requires-python = ">=3.6" dynamic = ["version"] +[project.optional-dependencies] +compilation = ["easybuild"] + [project.urls] "Homepage" = "https://eessi.io/docs/test-suite" "Bug Tracker" = "https://github.com/EESSI/test-suite/issues" diff --git a/setup.cfg b/setup.cfg index 3ef11c6e..4f27853a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,6 +17,10 @@ python_requires = >=3.6 packages = find: namespace_packages = eessi +[options.extras_require] +compilation = + easybuild + [options.packages.find] include = eessi*