Skip to content

Add intel-oneapi-runtime, allow injecting virtual dependencies#42062

Merged
tgamblin merged 17 commits intospack:developfrom
alalazo:features/gcc-runtime-and-languages
Mar 25, 2024
Merged

Add intel-oneapi-runtime, allow injecting virtual dependencies#42062
tgamblin merged 17 commits intospack:developfrom
alalazo:features/gcc-runtime-and-languages

Conversation

@alalazo
Copy link
Copy Markdown
Member

@alalazo alalazo commented Jan 12, 2024

This PR adds:

  • A new runtime for %oneapi compilers, called intel-oneapi-runtime
  • Information to both gcc-runtime and intel-oneapi-runtime, to ensure that we don't mix compilers using different soname for either libgfortran or libifcore

To do so, the following internal mechanisms have been implemented:

  • Possibility to inject virtual dependencies from the runtime_constraints callback on packages

Information has been added to gcc-runtime to provide the correct soname under different conditions on its %gcc.

Rules injected into the solver looks like:

Injection of virtual dependency
% Add a dependency on 'gfortran@5' for nodes compiled with gcc@=13.2.0 and using the 'fortran' language
attr("dependency_holds", node(ID, Package), "gfortran", "link") :-
  attr("node", node(ID, Package)),
  attr("node_compiler", node(ID, Package), "gcc"),
  attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"),
  not external(node(ID, Package)),
  not runtime(Package),
  attr("language", node(ID, Package), "fortran").

attr("virtual_node", node(RuntimeID, "gfortran")) :-
  attr("depends_on", node(ID, Package), ProviderNode, "link"),
  provider(ProviderNode, node(RuntimeID, "gfortran")),
  attr("node", node(ID, Package)),
  attr("node_compiler", node(ID, Package), "gcc"),
  attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"),
  not external(node(ID, Package)),
  not runtime(Package),
  attr("language", node(ID, Package), "fortran").

attr("node_version_satisfies", node(RuntimeID, "gfortran"), "5") :-
  attr("depends_on", node(ID, Package), ProviderNode, "link"),
  provider(ProviderNode, node(RuntimeID, "gfortran")),
  attr("node", node(ID, Package)),
  attr("node_compiler", node(ID, Package), "gcc"),
  attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"),
  not external(node(ID, Package)),
  not runtime(Package),
  attr("language", node(ID, Package), "fortran").

@spackbot-app
Copy link
Copy Markdown

spackbot-app bot commented Jan 12, 2024

@byrnHDF can you review this PR?

This PR modifies the following package(s), for which you are listed as a maintainer:

  • gcc
  • gcc-runtime
  • hdf5

@alalazo alalazo force-pushed the features/gcc-runtime-and-languages branch from 579d704 to e026365 Compare January 12, 2024 11:06
@tgamblin tgamblin self-assigned this Jan 18, 2024
@alalazo alalazo force-pushed the features/gcc-runtime-and-languages branch from e026365 to 3ad227e Compare January 22, 2024 16:59
@alalazo alalazo force-pushed the features/gcc-runtime-and-languages branch from 3ad227e to 661f908 Compare January 22, 2024 21:50
@alalazo
Copy link
Copy Markdown
Member Author

alalazo commented Jan 23, 2024

First benchmark as of 661f908

  • Spack: 0.22.0.dev0 (2f935b325f80c159655c757837c218aa7e750760)
  • Python: 3.11.5
  • Platform: linux-ubuntu20.04-icelake
  • Concretizer: clingo

develop: 6681346
PR: 661f908

radiuss.develop.csv
radiuss.pr.csv
radiuss.txt

radiuss

Adding more rules to the runtimes impacts the solve time

@alalazo
Copy link
Copy Markdown
Member Author

alalazo commented Jan 23, 2024

On the other hand, with 661f908 we have:

spack:
  specs:
  - hdf5%oneapi+fortran~mpi
  packages:
    cmake:
      require: "%gcc"
  view: true
  concretizer:
    unify: true

hdf5

Not that in the graph we introduced intel-oneapi-runtime, with a dependency on gcc-runtime (which in principle allows the selection of the underlying gcc).

@alalazo alalazo marked this pull request as ready for review January 23, 2024 11:56
@alalazo alalazo force-pushed the features/gcc-runtime-and-languages branch from 661f908 to eef4c70 Compare January 23, 2024 11:59
@alalazo alalazo changed the title gcc-runtime: add constraints on language runtimes Add intel-oneapi-runtime and language directive Jan 23, 2024
@alalazo
Copy link
Copy Markdown
Member Author

alalazo commented Jan 23, 2024

@spackbot run pipeline

@spackbot-app
Copy link
Copy Markdown

spackbot-app bot commented Jan 23, 2024

I've started that pipeline for you!

@alalazo
Copy link
Copy Markdown
Member Author

alalazo commented Jan 24, 2024

@spackbot run pipeline

@alalazo alalazo force-pushed the features/gcc-runtime-and-languages branch from 9f1ce19 to 4511679 Compare March 15, 2024 12:54
@alalazo alalazo changed the title Add intel-oneapi-runtime and language directive Add intel-oneapi-runtime, allow injecting virtual dependencies Mar 18, 2024
Copy link
Copy Markdown
Member

@tgamblin tgamblin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alalazo I'm merging this as it is far enough along that I think we can merge without hurting anything, and it can be improved once compilers are full packages.

I am still not sure that the interface is 100% yet. Specifically, so we don't forget:

  1. The deptype of a depends_on() on a language either shouldn't matter or should just be default. The deptype(s) is (are) entirely up to the compiler package... not the caller, so I don't see any point in forcing an arbitrary convention. Omitting is better.
  2. pkg("*").depends_on() shouldn't take a language parameter and we should not make languages special. They're just virtuals like any other... all we're adding is a means for dependencies to inject additional dependencies on their dependents. There is some fanciness I think we will need to do in solver setup to enable this but we should not force callers to know these special cases -- it should just work.
  3. I think likewise we shouldn't need to special case languages in the solve, at least not after compilers are proper dependencies. That can change later, too.

More comments/details below, but this is looking better and is moving along!


"""
if spack.spec.Spec(spec).name in SUPPORTED_LANGUAGES:
assert type == "build", "languages must be of 'build' type"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? It's not really a build dependency; it's a build dependency on the compiler and (maybe) a link dependency on the runtime, depending on what the compiler decides to do.

Languages should just omit the deptype. In practice, it doesn't matter b/c it's determined by the compiler's package (and some may not need a runtime). The caller of depends_on should not have to do anything based on how the language is implemented. they should just say they depend on it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? It's not really a build dependency; it's a build dependency on the compiler and (maybe) a link dependency on the runtime, depending on what the compiler decides to do.

Depending on a language just brings in the compiler, so as you say above it's a build dependency on the compiler. Then the compiler may or may not decide to inject further dependencies. If we don't make the language dependency a build dependency we won't be able to have duplicates (since link dependency virtuals are unified, whereas build are not).

Languages should just omit the deptype.

But depends_on have that argument, so a user may specify it. That was one of the reason for which I started implementing this using another directive. If we use depends_on I think people need to get used to adding type="build" as they do e.g. for cmake. If we use a wrapper directive we can take control of the type, and avoid exposing it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think people need to get used to adding type="build" as they do e.g. for cmake. If we use a wrapper directive we can take control of the type, and avoid exposing it.

You can just do that with depends_on. No extra directive, no extra convention. Don't have them specify the deptype. It's the "default" deptype, and the machinery underneath should take care of the rest.

provided_together: Dict["spack.spec.Spec", List[Set[str]]]
patches: Dict["spack.spec.Spec", List["spack.patch.Patch"]]
variants: Dict[str, Tuple["spack.variant.Variant", "spack.spec.Spec"]]
languages: Dict["spack.spec.Spec", Set[str]]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be TODO to remove later.

if compiler.name not in ("gcc", "oneapi"):
continue

compiler_with_different_cls_names = {"oneapi": "intel-oneapi-compilers"}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still needs to be consolidated - it should come from compilers/__init__.py

Comment on lines +2913 to +2916
if languages:
body_str += ",\n"
for language in languages:
body_str += f' attr("language", {node_variable}, "{language}")'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this, like depends_on() should be using SUPPORTED_LANGUAGES an not forcing extra parameters on the caller.

TODO: the final implementation of this should not have an extra languages parameter and should not have any special cases for "languages". There should be one general mechanism for packages to inject additional dependencies, and that's all we should need.

Comment on lines +165 to +167
attr("language", node(X, Package), Language) :-
condition_holds(ConditionID, node(X, Package)),
pkg_fact(Package,language(ConditionID, Language)).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should eventually be unnecessary once we have compilers modeled as packages.

Comment on lines +28 to +34
runtime_pkgs = spack.repo.PATH.packages_with_tags("runtime")
runtime_virtuals = set()
for x in runtime_pkgs:
pkg_class = spack.repo.PATH.get_pkg_class(x)
runtime_virtuals.update(pkg_class.provided_virtual_names())

self.specs = specs + [spack.spec.Spec(x) for x in runtime_pkgs]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires too much effort from the packager. They shouldn't have to tag runtimes.

I'm assuming the reason we do this is because we need the full set of possible virtuals before the solve, but you have that after you call runtime_constraints() on all the packages that have that method. You can just see if any of the global dependencies added with pkg("*").depends_on() are virtual, and get the same (actually, possibly smaller) list of runtime_virtuals that you have here. So this logic should go away and we should just look at what was injected.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can remove the tags for an extra method in e.g. gcc that returns what the package could possibly inject.

gfortran_str = "libgfortran@4"

for fortran_virtual in ("fortran-rt", gfortran_str):
pkg("*").depends_on(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This depends_on() is the one that should alert the solver to the fact that libgfortran and fortran-rt are possible virtuals. I think the pkg object should keep track of that and we should use it once all runtime_constraints() have been called in setup.

Comment on lines +36 to +37
depends_on("cxx", type="build", when="+cxx")
depends_on("fortran", type="build", when="+fortran")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alalazo Why do these have to be build dependencies? I think a default deptype is fine here, as this will eventually resolve to a build dependency on a compiler and link dependencies on libraries.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed in DMs. We might want to consider a new deptype. Currently the dependency maps to the compiler only, which needs to be build due to unification rules (once compilers are real dependencies).

@tgamblin tgamblin merged commit 0c9a53b into spack:develop Mar 25, 2024
@alalazo alalazo deleted the features/gcc-runtime-and-languages branch March 25, 2024 07:35
mathomp4 pushed a commit to mathomp4/spack that referenced this pull request Mar 27, 2024
…ck#42062)

This PR adds:
- A new runtime for `%oneapi` compilers, called `intel-oneapi-runtime`
- Information to both `gcc-runtime`  and `intel-oneapi-runtime`, to ensure
  that we don't mix compilers using different soname for either `libgfortran`
  or `libifcore`

To do so, the following internal mechanisms have been implemented:
- Possibility to inject virtual dependencies from the `runtime_constraints`
  callback on packages

Information has been added to `gcc-runtime` to provide the correct soname
under different conditions on its `%gcc`.

Rules injected into the solver looks like:

```prolog
% Add a dependency on 'gfortran@5' for nodes compiled with gcc@=13.2.0 and using the 'fortran' language
attr("dependency_holds", node(ID, Package), "gfortran", "link") :-
  attr("node", node(ID, Package)),
  attr("node_compiler", node(ID, Package), "gcc"),
  attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"),
  not external(node(ID, Package)),
  not runtime(Package),
  attr("language", node(ID, Package), "fortran").

attr("virtual_node", node(RuntimeID, "gfortran")) :-
  attr("depends_on", node(ID, Package), ProviderNode, "link"),
  provider(ProviderNode, node(RuntimeID, "gfortran")),
  attr("node", node(ID, Package)),
  attr("node_compiler", node(ID, Package), "gcc"),
  attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"),
  not external(node(ID, Package)),
  not runtime(Package),
  attr("language", node(ID, Package), "fortran").

attr("node_version_satisfies", node(RuntimeID, "gfortran"), "5") :-
  attr("depends_on", node(ID, Package), ProviderNode, "link"),
  provider(ProviderNode, node(RuntimeID, "gfortran")),
  attr("node", node(ID, Package)),
  attr("node_compiler", node(ID, Package), "gcc"),
  attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"),
  not external(node(ID, Package)),
  not runtime(Package),
  attr("language", node(ID, Package), "fortran").
```
@balay
Copy link
Copy Markdown
Contributor

balay commented Apr 2, 2024

I'm seeing the following behavior with this PR change.

svcpetsc@petsc-gpu-02:/scratch/svcpetsc/spack.a$ ./bin/spack spec [email protected] %[email protected] |grep hdf5@
 -           ^[email protected]%[email protected]~cxx+fortran+hl~ipo~java~map+mpi+shared~subfiling~szip~threadsafe+tools api=default build_system=cmake build_type=Release generator=make arch=linux-ubuntu22.04-x86_64

Note the [email protected]%[email protected] above. This breaks dependent pkgs that attempt to use hdf5 as the f90 module is no-longer compatible [with ifc].

With the snapshot prior to this change - I get:

svcpetsc@petsc-gpu-02:/scratch/svcpetsc/spack.a$ ./bin/spack spec [email protected] %[email protected] ^hdf5 %[email protected] |grep hdf5@
 -           ^[email protected]%[email protected]~cxx+fortran+hl~ipo~java~map+mpi+shared~subfiling~szip~threadsafe+tools api=default build_system=cmake build_type=Release generator=make arch=linux-ubuntu22.04-x86_64

Any idea if hdf5 needs a fix to restore this behavior?

Alternative I'm having to use now:

svcpetsc@petsc-gpu-02:/scratch/svcpetsc/spack.a$ ./bin/spack spec [email protected] %[email protected] ^hdf5 %[email protected] |grep hdf5@
 -           ^[email protected]%[email protected]~cxx+fortran+hl~ipo~java~map+mpi+shared~subfiling~szip~threadsafe+tools api=default build_system=cmake build_type=Release generator=make arch=linux-ubuntu22.04-x86_64

@alalazo
Copy link
Copy Markdown
Member Author

alalazo commented Apr 3, 2024

@balay I'll check if I can reproduce this default behavior, and why it happens. Thanks for reporting.

tldahlgren pushed a commit to tldahlgren/spack that referenced this pull request Apr 23, 2024
…ck#42062)

This PR adds:
- A new runtime for `%oneapi` compilers, called `intel-oneapi-runtime`
- Information to both `gcc-runtime`  and `intel-oneapi-runtime`, to ensure
  that we don't mix compilers using different soname for either `libgfortran`
  or `libifcore`

To do so, the following internal mechanisms have been implemented:
- Possibility to inject virtual dependencies from the `runtime_constraints`
  callback on packages

Information has been added to `gcc-runtime` to provide the correct soname
under different conditions on its `%gcc`.

Rules injected into the solver looks like:

```prolog
% Add a dependency on 'gfortran@5' for nodes compiled with gcc@=13.2.0 and using the 'fortran' language
attr("dependency_holds", node(ID, Package), "gfortran", "link") :-
  attr("node", node(ID, Package)),
  attr("node_compiler", node(ID, Package), "gcc"),
  attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"),
  not external(node(ID, Package)),
  not runtime(Package),
  attr("language", node(ID, Package), "fortran").

attr("virtual_node", node(RuntimeID, "gfortran")) :-
  attr("depends_on", node(ID, Package), ProviderNode, "link"),
  provider(ProviderNode, node(RuntimeID, "gfortran")),
  attr("node", node(ID, Package)),
  attr("node_compiler", node(ID, Package), "gcc"),
  attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"),
  not external(node(ID, Package)),
  not runtime(Package),
  attr("language", node(ID, Package), "fortran").

attr("node_version_satisfies", node(RuntimeID, "gfortran"), "5") :-
  attr("depends_on", node(ID, Package), ProviderNode, "link"),
  provider(ProviderNode, node(RuntimeID, "gfortran")),
  attr("node", node(ID, Package)),
  attr("node_compiler", node(ID, Package), "gcc"),
  attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"),
  not external(node(ID, Package)),
  not runtime(Package),
  attr("language", node(ID, Package), "fortran").
```
teaguesterling pushed a commit to teaguesterling/spack that referenced this pull request Jun 15, 2024
…ck#42062)

This PR adds:
- A new runtime for `%oneapi` compilers, called `intel-oneapi-runtime`
- Information to both `gcc-runtime`  and `intel-oneapi-runtime`, to ensure
  that we don't mix compilers using different soname for either `libgfortran`
  or `libifcore`

To do so, the following internal mechanisms have been implemented:
- Possibility to inject virtual dependencies from the `runtime_constraints`
  callback on packages

Information has been added to `gcc-runtime` to provide the correct soname
under different conditions on its `%gcc`.

Rules injected into the solver looks like:

```prolog
% Add a dependency on 'gfortran@5' for nodes compiled with gcc@=13.2.0 and using the 'fortran' language
attr("dependency_holds", node(ID, Package), "gfortran", "link") :-
  attr("node", node(ID, Package)),
  attr("node_compiler", node(ID, Package), "gcc"),
  attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"),
  not external(node(ID, Package)),
  not runtime(Package),
  attr("language", node(ID, Package), "fortran").

attr("virtual_node", node(RuntimeID, "gfortran")) :-
  attr("depends_on", node(ID, Package), ProviderNode, "link"),
  provider(ProviderNode, node(RuntimeID, "gfortran")),
  attr("node", node(ID, Package)),
  attr("node_compiler", node(ID, Package), "gcc"),
  attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"),
  not external(node(ID, Package)),
  not runtime(Package),
  attr("language", node(ID, Package), "fortran").

attr("node_version_satisfies", node(RuntimeID, "gfortran"), "5") :-
  attr("depends_on", node(ID, Package), ProviderNode, "link"),
  provider(ProviderNode, node(RuntimeID, "gfortran")),
  attr("node", node(ID, Package)),
  attr("node_compiler", node(ID, Package), "gcc"),
  attr("node_compiler_version", node(ID, Package), "gcc", "13.2.0"),
  not external(node(ID, Package)),
  not runtime(Package),
  attr("language", node(ID, Package), "fortran").
```
@mabraham
Copy link
Copy Markdown
Contributor

Ah, working now: #47806

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants