Use Spack target architecture to determine OpenBLAS target (#14380)
Openblas target is now determined automatically upon inspection of `TargetList.txt`. If the spack target is a generic architecture family (like x86_64 or aarch64) the DYNAMIC_ARCH setting is used instead of targeting a specific microarchitecture.
This commit is contained in:
parent
b0fce56d5b
commit
ca6e75c9f6
@ -278,3 +278,14 @@ def test_version_components(version, expected_number, expected_suffix):
|
|||||||
number, suffix = llnl.util.cpu.version_components(version)
|
number, suffix = llnl.util.cpu.version_components(version)
|
||||||
assert number == expected_number
|
assert number == expected_number
|
||||||
assert suffix == expected_suffix
|
assert suffix == expected_suffix
|
||||||
|
|
||||||
|
|
||||||
|
def test_invalid_family():
|
||||||
|
targets = llnl.util.cpu.targets
|
||||||
|
multi_parents = Microarchitecture(
|
||||||
|
name='chimera', parents=[targets['pentium4'], targets['power7']],
|
||||||
|
vendor='Imagination', features=[], compilers={}, generation=0
|
||||||
|
)
|
||||||
|
with pytest.raises(AssertionError,
|
||||||
|
matches='a target is expected to belong'):
|
||||||
|
multi_parents.family
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
from spack import *
|
from spack import *
|
||||||
from spack.package_test import compare_output_file, compile_c_and_execute
|
from spack.package_test import compare_output_file, compile_c_and_execute
|
||||||
import spack.architecture
|
|
||||||
|
|
||||||
|
|
||||||
class Openblas(MakefilePackage):
|
class Openblas(MakefilePackage):
|
||||||
@ -33,17 +33,9 @@ class Openblas(MakefilePackage):
|
|||||||
version('0.2.16', sha256='766f350d0a4be614812d535cead8c816fc3ad3b9afcd93167ea5e4df9d61869b')
|
version('0.2.16', sha256='766f350d0a4be614812d535cead8c816fc3ad3b9afcd93167ea5e4df9d61869b')
|
||||||
version('0.2.15', sha256='73c40ace5978282224e5e122a41c8388c5a19e65a6f2329c2b7c0b61bacc9044')
|
version('0.2.15', sha256='73c40ace5978282224e5e122a41c8388c5a19e65a6f2329c2b7c0b61bacc9044')
|
||||||
|
|
||||||
variant(
|
variant('ilp64', default=False, description='Force 64-bit Fortran native integers')
|
||||||
'shared',
|
|
||||||
default=True,
|
|
||||||
description='Build shared libraries as well as static libs.'
|
|
||||||
)
|
|
||||||
variant('ilp64', default=False, description='64 bit integers')
|
|
||||||
variant('pic', default=True, description='Build position independent code')
|
variant('pic', default=True, description='Build position independent code')
|
||||||
|
variant('shared', default=True, description='Build shared libraries')
|
||||||
variant('cpu_target', default='auto',
|
|
||||||
description='Set CPU target architecture (leave empty for '
|
|
||||||
'autodetection; GENERIC, SSE_GENERIC, NEHALEM, ...)')
|
|
||||||
|
|
||||||
variant(
|
variant(
|
||||||
'threads', default='none',
|
'threads', default='none',
|
||||||
@ -52,24 +44,6 @@ class Openblas(MakefilePackage):
|
|||||||
multi=False
|
multi=False
|
||||||
)
|
)
|
||||||
|
|
||||||
variant(
|
|
||||||
'virtual_machine',
|
|
||||||
default=False,
|
|
||||||
description="Adding options to build openblas on Linux virtual machine"
|
|
||||||
)
|
|
||||||
|
|
||||||
variant(
|
|
||||||
'avx2',
|
|
||||||
default=True,
|
|
||||||
description='Enable use of AVX2 instructions'
|
|
||||||
)
|
|
||||||
|
|
||||||
variant(
|
|
||||||
'avx512',
|
|
||||||
default=False,
|
|
||||||
description='Enable use of AVX512 instructions'
|
|
||||||
)
|
|
||||||
|
|
||||||
# virtual dependency
|
# virtual dependency
|
||||||
provides('blas')
|
provides('blas')
|
||||||
provides('lapack')
|
provides('lapack')
|
||||||
@ -142,14 +116,80 @@ def check_compilers(self):
|
|||||||
'OpenBLAS @:0.2.19 does not support OpenMP with clang!'
|
'OpenBLAS @:0.2.19 does not support OpenMP with clang!'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _read_targets(target_file):
|
||||||
|
"""Parse a list of available targets from the OpenBLAS/TargetList.txt
|
||||||
|
file.
|
||||||
|
"""
|
||||||
|
micros = []
|
||||||
|
re_target = re.compile(r'^[A-Z0-9_]+$')
|
||||||
|
for line in target_file:
|
||||||
|
match = re_target.match(line)
|
||||||
|
if match is not None:
|
||||||
|
micros.append(line.strip().lower())
|
||||||
|
|
||||||
|
return micros
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _microarch_target_args(microarch, available_targets):
|
||||||
|
"""Given a spack microarchitecture and a list of targets found in
|
||||||
|
OpenBLAS' TargetList.txt, determine the best command-line arguments.
|
||||||
|
"""
|
||||||
|
args = [] # Return value
|
||||||
|
|
||||||
|
# List of available architectures, and possible aliases
|
||||||
|
openblas_arch = set(['alpha', 'arm', 'ia64', 'mips', 'mips64',
|
||||||
|
'power', 'sparc', 'zarch'])
|
||||||
|
openblas_arch_map = {
|
||||||
|
'amd64': 'x86_64',
|
||||||
|
'powerpc64': 'power',
|
||||||
|
'i386': 'x86',
|
||||||
|
'aarch64': 'arm64',
|
||||||
|
}
|
||||||
|
openblas_arch.update(openblas_arch_map.keys())
|
||||||
|
openblas_arch.update(openblas_arch_map.values())
|
||||||
|
|
||||||
|
# Add spack-only microarchitectures to list
|
||||||
|
skylake = set(["skylake", "skylake_avx512"])
|
||||||
|
available_targets = set(available_targets) | skylake | openblas_arch
|
||||||
|
|
||||||
|
# Find closest ancestor that is known to build in blas
|
||||||
|
if microarch.name not in available_targets:
|
||||||
|
for microarch in microarch.ancestors:
|
||||||
|
if microarch.name in available_targets:
|
||||||
|
break
|
||||||
|
|
||||||
|
arch_name = microarch.family.name
|
||||||
|
if arch_name in openblas_arch:
|
||||||
|
# Apply possible spack->openblas arch name mapping
|
||||||
|
arch_name = openblas_arch_map.get(arch_name, arch_name)
|
||||||
|
args.append('ARCH=' + arch_name)
|
||||||
|
|
||||||
|
if microarch.vendor == 'generic':
|
||||||
|
# User requested a generic platform, or we couldn't find a good
|
||||||
|
# match for the requested one. Allow OpenBLAS to determine
|
||||||
|
# an optimized kernel at run time.
|
||||||
|
args.append('DYNAMIC_ARCH=1')
|
||||||
|
elif microarch.name in skylake:
|
||||||
|
# Special case for renaming skylake family
|
||||||
|
args.append('TARGET=SKYLAKEX')
|
||||||
|
if microarch.name == "skylake":
|
||||||
|
# Special case for disabling avx512 instructions
|
||||||
|
args.append('NO_AVX512=1')
|
||||||
|
else:
|
||||||
|
args.append('TARGET=' + microarch.name.upper())
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def make_defs(self):
|
def make_defs(self):
|
||||||
|
spec = self.spec
|
||||||
|
|
||||||
# Configure fails to pick up fortran from FC=/abs/path/to/fc, but
|
# Configure fails to pick up fortran from FC=/abs/path/to/fc, but
|
||||||
# works fine with FC=/abs/path/to/gfortran.
|
# works fine with FC=/abs/path/to/gfortran.
|
||||||
# When mixing compilers make sure that
|
# When mixing compilers make sure that
|
||||||
# $SPACK_ROOT/lib/spack/env/<compiler> have symlinks with reasonable
|
# $SPACK_ROOT/lib/spack/env/<compiler> have symlinks with reasonable
|
||||||
# names and hack them inside lib/spack/spack/compilers/<compiler>.py
|
# names and hack them inside lib/spack/spack/compilers/<compiler>.py
|
||||||
|
|
||||||
make_defs = [
|
make_defs = [
|
||||||
'CC={0}'.format(spack_cc),
|
'CC={0}'.format(spack_cc),
|
||||||
'FC={0}'.format(spack_fc),
|
'FC={0}'.format(spack_fc),
|
||||||
@ -161,23 +201,15 @@ def make_defs(self):
|
|||||||
else:
|
else:
|
||||||
make_defs.append('MAKE_NB_JOBS=0') # flag provided by OpenBLAS
|
make_defs.append('MAKE_NB_JOBS=0') # flag provided by OpenBLAS
|
||||||
|
|
||||||
if self.spec.variants['virtual_machine'].value:
|
# Add target and architecture flags
|
||||||
make_defs += [
|
targetlist_name = join_path(self.stage.source_path, "TargetList.txt")
|
||||||
'DYNAMIC_ARCH=1',
|
if os.path.exists(targetlist_name):
|
||||||
'NUM_THREADS=64', # OpenBLAS stores present no of CPUs as max
|
with open(targetlist_name) as f:
|
||||||
]
|
avail_targets = self._read_targets(f)
|
||||||
|
else:
|
||||||
|
avail_targets = []
|
||||||
|
make_defs += self._microarch_target_args(spec.target, avail_targets)
|
||||||
|
|
||||||
if self.spec.variants['cpu_target'].value != 'auto':
|
|
||||||
make_defs += [
|
|
||||||
'TARGET={0}'.format(self.spec.variants['cpu_target'].value)
|
|
||||||
]
|
|
||||||
# invoke make with the correct TARGET for aarch64
|
|
||||||
elif 'aarch64' in spack.architecture.sys_type():
|
|
||||||
make_defs += [
|
|
||||||
'TARGET=ARMV8'
|
|
||||||
]
|
|
||||||
if self.spec.satisfies('%gcc@:4.8.4'):
|
|
||||||
make_defs += ['NO_AVX2=1']
|
|
||||||
if '~shared' in self.spec:
|
if '~shared' in self.spec:
|
||||||
if '+pic' in self.spec:
|
if '+pic' in self.spec:
|
||||||
make_defs.extend([
|
make_defs.extend([
|
||||||
@ -201,11 +233,9 @@ def make_defs(self):
|
|||||||
if '+ilp64' in self.spec:
|
if '+ilp64' in self.spec:
|
||||||
make_defs += ['INTERFACE64=1']
|
make_defs += ['INTERFACE64=1']
|
||||||
|
|
||||||
if self.spec.target.family == 'x86_64':
|
# Prevent errors in `as` assembler from newer instructions
|
||||||
if '~avx2' in self.spec:
|
if self.spec.satisfies('%gcc@:4.8.4'):
|
||||||
make_defs += ['NO_AVX2=1']
|
make_defs.append('NO_AVX2=1')
|
||||||
if '~avx512' in self.spec:
|
|
||||||
make_defs += ['NO_AVX512=1']
|
|
||||||
|
|
||||||
return make_defs
|
return make_defs
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user