targets: Spack targets can now be fine-grained microarchitectures
Spack can now: - label ppc64, ppc64le, x86_64, etc. builds with specific microarchitecture-specific names, like 'haswell', 'skylake' or 'icelake'. - detect the host architecture of a machine from /proc/cpuinfo or similar tools. - Understand which microarchitectures are compatible with which (for binary reuse) - Understand which compiler flags are needed (for GCC, so far) to build binaries for particular microarchitectures. All of this is managed through a JSON file (microarchitectures.json) that contains detailed auto-detection, compiler flag, and compatibility information for specific microarchitecture targets. The `llnl.util.cpu` module implements a library that allows detection and comparison of microarchitectures based on the data in this file. The `target` part of Spack specs is now essentially a Microarchitecture object, and Specs' targets can be compared for compatibility as well. This allows us to label optimized binary packages at a granularity that enables them to be reused on compatible machines. Previously, we only knew that a package was built for x86_64, NOT which x86_64 machines it was usable on. Currently this feature supports Intel, Power, and AMD chips. Support for ARM is forthcoming. Specifics: - Add microarchitectures.json with descriptions of architectures - Relaxed semantic of compiler's "target" attribute. Before this change the semantic to check if a compiler could be viable for a given target was exact match. This made sense as the finest granularity of targets was architecture families. As now we can target micro-architectures, this commit changes the semantic by interpreting as the architecture family what is stored in the compiler's "target" attribute. A compiler is then a viable choice if the target being concretized belongs to the same family. Similarly when a new compiler is detected the architecture family is stored in the "target" attribute. - Make Spack's `cc` compiler wrapper inject target-specific flags on the command line - Architecture concretization updated to use the same algorithm as compiler concretization - Micro-architecture features, vendor, generation etc. are included in the package hash. Generic architectures, such as x86_64 or ppc64, are still dumped using the name only. - If the compiler for a target is not supported exit with an intelligible error message. If the compiler support is unknown don't try to use optimization flags. - Support and define feature aliases (e.g., sse3 -> ssse3) in microarchitectures.json and on Microarchitecture objects. Feature aliases are defined in targets.json and map a name (the "alias") to a list of rules that must be met for the test to be successful. The rules that are available can be extended later using a decorator. - Implement subset semantics for comparing microarchitectures (treat microarchitectures as a partial order, i.e. (a < b), (a == b) and (b < a) can all be false. - Implement logic to automatically demote the default target if the compiler being used is too old to optimize for it. Updated docs to make this behavior explicit. This avoids surprising the user if the default compiler is older than the host architecture. This commit adds unit tests to verify the semantics of target ranges and target lists in constraints. The implementation to allow target ranges and lists is minimal and doesn't add any new type. A more careful refactor that takes into account the type system might be due later. Co-authored-by: Gregory Becker <becker33.llnl.gov>
This commit is contained in:

committed by
Todd Gamblin

parent
dfabf5d6b1
commit
3c4322bf1a
@@ -848,18 +848,111 @@ that executables will run without the need to set ``LD_LIBRARY_PATH``.
|
||||
Architecture specifiers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The architecture can be specified by using the reserved
|
||||
words ``target`` and/or ``os`` (``target=x86-64 os=debian7``). You can also
|
||||
use the triplet form of platform, operating system and processor.
|
||||
Each node in the dependency graph of a spec has an architecture attribute.
|
||||
This attribute is a triplet of platform, operating system and processor.
|
||||
You can specify the elements either separately, by using
|
||||
the reserved keywords ``platform``, ``os`` and ``target``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ spack install libelf platform=linux
|
||||
$ spack install libelf os=ubuntu18.04
|
||||
$ spack install libelf target=broadwell
|
||||
|
||||
or together by using the reserved keyword ``arch``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ spack install libelf arch=cray-CNL10-haswell
|
||||
|
||||
Users on non-Cray systems won't have to worry about specifying the architecture.
|
||||
Spack will autodetect what kind of operating system is on your machine as well
|
||||
as the processor. For more information on how the architecture can be
|
||||
used on Cray machines, see :ref:`cray-support`
|
||||
Normally users don't have to bother specifying the architecture
|
||||
if they are installing software for their current host as in that case the
|
||||
values will be detected automatically.
|
||||
|
||||
.. admonition:: Cray machines
|
||||
|
||||
The situation is a little bit different for Cray machines and a detailed
|
||||
explanation on how the architecture can be set on them can be found at :ref:`cray-support`
|
||||
|
||||
.. _support-for-microarchitectures:
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""""
|
||||
Support for specific microarchitectures
|
||||
"""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Spack knows how to detect and optimize for many specific microarchitectures
|
||||
(including recent Intel, AMD and IBM chips) and encodes this information in
|
||||
the ``target`` portion of the architecture specification. A complete list of
|
||||
the microarchitectures known to Spack can be obtained in the following way:
|
||||
|
||||
.. command-output:: spack arch --known-targets
|
||||
|
||||
When a spec is installed Spack matches the compiler being used with the
|
||||
microarchitecture being targeted to inject appropriate optimization flags
|
||||
at compile time. Giving a command such as the following:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ spack install zlib%gcc@9.0.1 target=icelake
|
||||
|
||||
will produce compilation lines similar to:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ /usr/bin/gcc-9 -march=icelake-client -mtune=icelake-client -c ztest10532.c
|
||||
$ /usr/bin/gcc-9 -march=icelake-client -mtune=icelake-client -c -fPIC -O2 ztest10532.
|
||||
...
|
||||
|
||||
where the flags ``-march=icelake-client -mtune=icelake-client`` are injected
|
||||
by Spack based on the requested target and compiler.
|
||||
|
||||
If Spack knows that the requested compiler can't optimize for the current target
|
||||
or can't build binaries for that target at all, it will exit with a meaningful error message:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ spack install zlib%gcc@5.5.0 target=icelake
|
||||
==> Error: cannot produce optimized binary for micro-architecture "icelake" with gcc@5.5.0 [supported compiler versions are 8:]
|
||||
|
||||
When instead an old compiler is selected on a recent enough microarchitecture but there is
|
||||
no explicit ``target`` specification, Spack will optimize for the best match it can find instead
|
||||
of failing:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ spack arch
|
||||
linux-ubuntu18.04-broadwell
|
||||
|
||||
$ spack spec zlib%gcc@4.8
|
||||
Input spec
|
||||
--------------------------------
|
||||
zlib%gcc@4.8
|
||||
|
||||
Concretized
|
||||
--------------------------------
|
||||
zlib@1.2.11%gcc@4.8+optimize+pic+shared arch=linux-ubuntu18.04-haswell
|
||||
|
||||
$ spack spec zlib%gcc@9.0.1
|
||||
Input spec
|
||||
--------------------------------
|
||||
zlib%gcc@9.0.1
|
||||
|
||||
Concretized
|
||||
--------------------------------
|
||||
zlib@1.2.11%gcc@9.0.1+optimize+pic+shared arch=linux-ubuntu18.04-broadwell
|
||||
|
||||
In the snippet above, for instance, the microarchitecture was demoted to ``haswell`` when
|
||||
compiling with ``gcc@4.8`` since support to optimize for ``broadwell`` starts from ``gcc@4.9:``.
|
||||
|
||||
Finally if Spack has no information to match compiler and target, it will
|
||||
proceed with the installation but avoid injecting any microarchitecture
|
||||
specific flags.
|
||||
|
||||
.. warning::
|
||||
|
||||
Currently Spack doesn't print any warning to the user if it has no information
|
||||
on which optimization flags should be used for a given compiler. This behavior
|
||||
might change in the future.
|
||||
|
||||
.. _sec-virtual-dependencies:
|
||||
|
||||
|
@@ -3213,6 +3213,127 @@ the two functions is that ``satisfies()`` tests whether spec
|
||||
constraints overlap at all, while ``in`` tests whether a spec or any
|
||||
of its dependencies satisfy the provided spec.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Architecture specifiers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As mentioned in :ref:`support-for-microarchitectures` each node in a concretized spec
|
||||
object has an architecture attribute which is a triplet of ``platform``, ``os`` and ``target``.
|
||||
Each of these three items can be queried to take decisions when configuring, building or
|
||||
installing a package.
|
||||
|
||||
""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Querying the platform and the operating system
|
||||
""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Sometimes the actions to be taken to install a package might differ depending on the
|
||||
platform we are installing for. If that is the case we can use conditionals:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if spec.platform == 'darwin':
|
||||
# Actions that are specific to Darwin
|
||||
args.append('--darwin-specific-flag')
|
||||
|
||||
and branch based on the current spec platform. If we need to make a package directive
|
||||
conditional on the platform we can instead employ the usual spec syntax and pass the
|
||||
corresponding constraint to the appropriate argument of that directive:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Libnl(AutotoolsPackage):
|
||||
|
||||
conflicts('platform=darwin', msg='libnl requires FreeBSD or Linux')
|
||||
|
||||
Similar considerations are also valid for the ``os`` part of a spec's architecture.
|
||||
For instance:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Glib(AutotoolsPackage)
|
||||
|
||||
patch('old-kernels.patch', when='os=centos6')
|
||||
|
||||
will apply the patch only when the operating system is Centos 6.
|
||||
|
||||
.. note::
|
||||
|
||||
Even though experienced Python programmers might recognize that there are other ways
|
||||
to retrieve information on the platform:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
# Actions that are specific to Darwin
|
||||
args.append('--darwin-specific-flag')
|
||||
|
||||
querying the spec architecture's platform should be considered the preferred. The key difference
|
||||
is that a query on ``sys.platform``, or anything similar, is always bound to the host on which the
|
||||
interpreter running Spack is located and as such it won't work correctly in environments where
|
||||
cross-compilation is required.
|
||||
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
Querying the target microarchitecture
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
|
||||
The third item of the architecture tuple is the ``target`` which abstracts the information on the
|
||||
CPU microarchitecture. A list of all the targets known to Spack can be obtained via the
|
||||
command line:
|
||||
|
||||
.. command-output:: spack arch --known-targets
|
||||
|
||||
Within directives each of the names above can be used to match a particular target:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Julia(Package):
|
||||
# This patch is only applied on icelake microarchitectures
|
||||
patch("icelake.patch", when="target=icelake")
|
||||
|
||||
in a similar way to what we have seen before for ``platform`` and ``os``.
|
||||
Where ``target`` objects really shine though is when they are used in methods
|
||||
called at configure, build or install time. In that case we can test targets
|
||||
for supported features, for instance:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if 'avx512' in spec.target:
|
||||
args.append('--with-avx512')
|
||||
|
||||
The snippet above will append the ``--with-avx512`` item to a list of arguments only if the corresponding
|
||||
feature is supported by the current target. Sometimes we need to take different actions based
|
||||
on the architecture family and not on the specific microarchitecture. In those cases
|
||||
we can check the ``family`` attribute:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if spec.target.family == 'ppc64le':
|
||||
args.append('--enable-power')
|
||||
|
||||
Possible values for the ``family`` attribute are displayed by ``spack arch --known-targets``
|
||||
under the "Generic architectures (families)" header.
|
||||
Finally it's possible to perform actions based on whether the current microarchitecture
|
||||
is compatible with a known one:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if spec.target > 'haswell':
|
||||
args.append('--needs-at-least-haswell')
|
||||
|
||||
The snippet above will add an item to a list of configure options only if the current
|
||||
architecture is a superset of ``haswell`` or, said otherwise, only if the current
|
||||
architecture is a later microarchitecture still compatible with ``haswell``.
|
||||
|
||||
.. admonition:: Using Spack on unknown microarchitectures
|
||||
|
||||
If Spack is used on an unknown microarchitecture it will try to perform a best match
|
||||
of the features it detects and will select the closest microarchitecture it has
|
||||
information for. In case nothing matches, it will create on the fly a new generic
|
||||
architecture. This is done to allow users to still be able to use Spack
|
||||
for their work. The software built won't be probably as optimized as it could but just
|
||||
as you need a newer compiler to build for newer architectures, you may need newer
|
||||
versions of Spack for new architectures to be correctly labeled.
|
||||
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
Accessing Dependencies
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
Reference in New Issue
Block a user