Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow optarch values to be partial maps including vector extensions #3797

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
10 changes: 6 additions & 4 deletions easybuild/toolchains/compiler/inteliccifort.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,15 @@ class IntelIccIfort(Compiler):

# used when 'optarch' toolchain option is enabled (and --optarch is not specified)
COMPILER_OPTIMAL_ARCHITECTURE_OPTION = {
(systemtools.X86_64, systemtools.AMD): 'xHost',
(systemtools.X86_64, systemtools.INTEL): 'xHost',
systemtools.X86_64: 'xHost',
# Intel compilers don't auto-detect AMD features and default to SSE2
(systemtools.X86_64, systemtools.AMD, systemtools.SSSE3): 'mssse3',
(systemtools.X86_64, systemtools.AMD, systemtools.SSE4_2): 'msse4.2',
(systemtools.X86_64, systemtools.AMD, systemtools.AVX2): 'march=core-avx2',
}
# used with --optarch=GENERIC
COMPILER_GENERIC_OPTION = {
(systemtools.X86_64, systemtools.AMD): 'xSSE2',
(systemtools.X86_64, systemtools.INTEL): 'xSSE2',
systemtools.X86_64: 'xSSE2',
}

COMPILER_CC = 'icc'
Expand Down
31 changes: 6 additions & 25 deletions easybuild/tools/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
import pwd

import easybuild.tools.environment as env
from easybuild.base import fancylogger # build_log should always stay there, to ensure EasyBuildLog
from easybuild.base import fancylogger
from easybuild.base.fancylogger import setLogLevel
from easybuild.base.generaloption import GeneralOption
from easybuild.framework.easyblock import MODULE_ONLY_STEPS, SOURCE_STEP, FETCH_STEP, EasyBlock
Expand Down Expand Up @@ -101,7 +101,7 @@
from easybuild.tools.robot import det_robot_path
from easybuild.tools.run import run_cmd
from easybuild.tools.package.utilities import avail_package_naming_schemes
from easybuild.tools.toolchain.compiler import DEFAULT_OPT_LEVEL, OPTARCH_MAP_CHAR, OPTARCH_SEP, Compiler
from easybuild.tools.toolchain.compiler import DEFAULT_OPT_LEVEL, Compiler, parse_optarch_string
from easybuild.tools.toolchain.toolchain import SYSTEM_TOOLCHAIN_NAME
from easybuild.tools.repository.repository import avail_repositories
from easybuild.tools.systemtools import UNKNOWN, check_python_version, get_cpu_architecture, get_cpu_family
Expand Down Expand Up @@ -987,29 +987,10 @@ def postprocess(self):
cleanup_and_exit(self.tmpdir)

def _postprocess_optarch(self):
"""Postprocess --optarch option."""
optarch_parts = self.options.optarch.split(OPTARCH_SEP)

# we expect to find a ':' in every entry in optarch, in case optarch is specified on a per-compiler basis
n_parts = len(optarch_parts)
map_char_cnts = [p.count(OPTARCH_MAP_CHAR) for p in optarch_parts]
if (n_parts > 1 and any(c != 1 for c in map_char_cnts)) or (n_parts == 1 and map_char_cnts[0] > 1):
raise EasyBuildError("The optarch option has an incorrect syntax: %s", self.options.optarch)
else:
# if there are options for different compilers, we set up a dict
if OPTARCH_MAP_CHAR in optarch_parts[0]:
optarch_dict = {}
for compiler, compiler_opt in [p.split(OPTARCH_MAP_CHAR) for p in optarch_parts]:
if compiler in optarch_dict:
raise EasyBuildError("The optarch option contains duplicated entries for compiler %s: %s",
compiler, self.options.optarch)
else:
optarch_dict[compiler] = compiler_opt
self.options.optarch = optarch_dict
self.log.info("Transforming optarch into a dict: %s", self.options.optarch)
# if optarch is not in mapping format, we do nothing and just keep the string
else:
self.log.info("Keeping optarch raw: %s", self.options.optarch)
"""
Postprocess --optarch option.
"""
self.options.optarch = parse_optarch_string(self.options.optarch, include_compiler=True)

def _postprocess_close_pr_reasons(self):
"""Postprocess --close-pr-reasons options"""
Expand Down
50 changes: 50 additions & 0 deletions easybuild/tools/systemtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@

ARCH_KEY_PREFIX = 'arch='

# Vector extension constants
SSE = 'sse'
SSE2 = 'sse2'
SSSE3 = 'ssse3'
SSE4_1 = 'sse4_1'
SSE4_2 = 'sse4_2'
AVX = 'avx'
AVX2 = 'avx2'
AVX512F = 'avx512f'

# Vendor constants
AMD = 'AMD'
APM = 'Applied Micro'
Expand Down Expand Up @@ -133,6 +143,8 @@
CPU_ARCHITECTURES = [AARCH32, AARCH64, POWER, RISCV32, RISCV64, X86_64]
CPU_FAMILIES = [AMD, ARM, INTEL, POWER, POWER_LE, RISCV]
CPU_VENDORS = [AMD, APM, APPLE, ARM, BROADCOM, CAVIUM, DEC, IBM, INTEL, MARVELL, MOTOROLA, NVIDIA, QUALCOMM]
# Vector extensions of CPUs in ascending order (later => better)
CPU_VECTOR_EXTS = [SSE, SSE2, SSSE3, SSE4_1, SSE4_2, AVX, AVX2, AVX512F]
# ARM implementer IDs (i.e., the hexadeximal keys) taken from ARMv8-A Architecture Reference Manual
# (ARM DDI 0487A.j, Section G6.2.102, Page G6-4493)
VENDOR_IDS = {
Expand Down Expand Up @@ -610,6 +622,13 @@ def get_cpu_features():
return cpu_feat


def get_cpu_vector_exts():
"""Get list of (relevant) CPU vector extensions"""
cpu_features = set(get_cpu_features())
# Values of the vector extension constants purposely match the cpu feature value
return [i for i in CPU_VECTOR_EXTS if i in cpu_features]


def get_gpu_info():
"""
Get the GPU info
Expand Down Expand Up @@ -1322,6 +1341,37 @@ def pick_dep_version(dep_version):
return result


def pick_opt_arch(options, arch, cpu_family, vector_exts):
"""
Pick the best matching entry from the options dict based on CPU arch and features

:param options: Dictionary mapping tuples of system specs to optarchs.
E.g. (X86_64, AMD, AVX2): 'mavx2'
:param arch: Current CPU arch
:param cpu_family: Current CPU family
:param vector_exts: Vector extensions supported by current CPU
"""
def create_possible_keys():
"""Yield a list of possible keys from most specific to least specific"""
for ext in vector_exts:
yield (arch, cpu_family, ext)
yield (arch, cpu_family)
yield (arch, )
# Also allow single string entry
yield arch
# Default fallback for any arch
yield None

result = None
for key in create_possible_keys():
try:
result = options[key]
break
except KeyError:
pass
return result


def det_pypkg_version(pkg_name, imported_pkg, import_name=None):
"""Determine version of a Python package."""

Expand Down
Loading
Loading