-
Notifications
You must be signed in to change notification settings - Fork 58
DO NOT MERGE feat[next-dace]: Use nanobind Interface of DaCe
#2721
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
Open
philip-paul-mueller
wants to merge
64
commits into
GridTools:main
Choose a base branch
from
philip-paul-mueller:dace_nanobind_integration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
64 commits
Select commit
Hold shift + click to select a range
fa798a2
Not fully working, but a huge step forward.
philip-paul-mueller ed76225
Let's try this.
philip-paul-mueller 75c1945
Next small addition.
philip-paul-mueller 88a3929
This works for one sdfg, but it is probably not fast and it has the p…
philip-paul-mueller 0c151fa
This works it is not nice but it works.
philip-paul-mueller 0e58c03
Now there are errors.
philip-paul-mueller c46e4e5
Merge remote-tracking branch 'gt4py/main' into dace_nanobind_integration
philip-paul-mueller f407074
Let's test that thing.
philip-paul-mueller 63ecac9
Updated the test.
philip-paul-mueller 516088b
First version that uses the new calling convention, but it ignores th…
philip-paul-mueller 9e7b5ea
This works, but is not fully nice.
philip-paul-mueller 4ad8c88
Updated DaCe.
philip-paul-mueller a96749f
A bit nicer.
philip-paul-mueller 96a09bc
Updated.
philip-paul-mueller 1558b42
Merge remote-tracking branch 'gt4py/main' into dace_nanobind_integration
philip-paul-mueller 822fabd
Newest DaCe needs C++20.
philip-paul-mueller a3e1e7c
Newest DaCe needs C++20.
philip-paul-mueller 94a0033
Removed an redundant import.
philip-paul-mueller 113adb4
Revert "Newest DaCe needs C++20."
philip-paul-mueller bcc3992
Merge remote-tracking branch 'fork/dace_cpp20' into dace_nanobind_int…
philip-paul-mueller f171934
Small error
philip-paul-mueller 2ca2ae9
Forgot to undo the hack.
philip-paul-mueller 2a48a7e
A further divergence.
philip-paul-mueller cd79a91
Updated DaCe.
philip-paul-mueller 002a151
Fixed an issue in a test, but we have to rework that anyway.
philip-paul-mueller f4ae459
Added new function to get the origin in a DaCe compatiple format.
philip-paul-mueller 73e302f
Not yet tested, but I think we can now use it, in some way.
philip-paul-mueller 7bb8760
I am not proud of it but seems to work.
philip-paul-mueller 9dbe0c9
Let's see what CI sais to the newest version.
philip-paul-mueller dd15370
Forcing types on a typeless language is like like (whatever you think…
philip-paul-mueller 84d60da
Relocated the code for generating `user_args`. Not ideal but currentl…
philip-paul-mueller d7fd2b6
Fixed a small bug.
philip-paul-mueller 5a6c241
Merge remote-tracking branch 'gt4py/main' into dace_nanobind_integration
philip-paul-mueller 8e8e91f
Updated dace.
philip-paul-mueller c2136e2
This makes the bindings code a bit nicer.
philip-paul-mueller 8b2b2bb
Updated the decorator.
philip-paul-mueller 8673709
Small Fix.
philip-paul-mueller 703c577
Small cleanup.
philip-paul-mueller 82163b6
Fixed a small bug.
philip-paul-mueller dde7a4e
Fixed a bug.
philip-paul-mueller e109d84
Had to place the function better.
philip-paul-mueller ffa095f
Deleted the old files that are no longer needed.
philip-paul-mueller be33c8a
Fixed some unit tests.
philip-paul-mueller 1268843
Had to put them back to make the orecstrator happy.
philip-paul-mueller 304f931
Fixed the test.
philip-paul-mueller a516466
Updated the unit tests.
philip-paul-mueller 1fbd203
Fixed some tests.
philip-paul-mueller fdd509b
Addressed some review comments.
philip-paul-mueller 3c1b44f
Some more changes and now it is also filtered out.
philip-paul-mueller 35aa61d
Addressed some reviews comments, but some unit tests need updating an…
philip-paul-mueller 6cf1c1b
Relocated the bindings code to the bindings module, i.e. the call.
philip-paul-mueller 334084b
Small modifications.
philip-paul-mueller 64fedd6
Updated the tests.
philip-paul-mueller 3256fcd
Merge remote-tracking branch 'gt4py/main' into dace_nanobind_integration
philip-paul-mueller 0d6a831
Updated DaCe.
philip-paul-mueller b424d10
Update.
philip-paul-mueller 45afe7d
Now use the proper eve code thing.
philip-paul-mueller 59eac19
Merge remote-tracking branch 'gt4py/main' into dace_nanobind_integration
philip-paul-mueller 9577ea3
Updated DaCe.
philip-paul-mueller c2d9337
Let's hope that this helps.
philip-paul-mueller 6318226
Merge remote-tracking branch 'gt4py/main' into dace_nanobind_integration
philip-paul-mueller e68f8e9
Fixed the unit tests.
philip-paul-mueller f6d341c
The file has become empty, so removing it.
philip-paul-mueller 26476f5
Merge remote-tracking branch 'gt4py/main' into dace_nanobind_integration
philip-paul-mueller File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -1018,7 +1018,13 @@ def _add_sdfg_params( | |||||
| transient=True, | ||||||
| ) | ||||||
|
|
||||||
| # the list of all sdfg arguments (aka non-transient arrays) which include tuple-element fields | ||||||
| # The list of the SDFG arguments, i.e. the global arrays, scalars and free symbols. | ||||||
| # Note that tuple arguments are flattened and their name is mangled and no longer | ||||||
| # matches the name in the signature of the field operator / program. | ||||||
| # Also note that some scalar arguments (which are lowered to symbols) listed in | ||||||
| # this signature might not be part of the generated C-API, as unused symbols are | ||||||
| # pruned from the SDFG during optimization. | ||||||
| # NOTE: The dispatch code does not use it, instead the `user_args` are used. | ||||||
| return [arg_name for arg_name, _ in sdfg_args] | ||||||
|
|
||||||
| def visit_Program(self, node: gtir.Program) -> dace.SDFG: | ||||||
|
|
@@ -1064,10 +1070,10 @@ def visit_Program(self, node: gtir.Program) -> dace.SDFG: | |||||
| assert isinstance(nsdfg.arrays[data], dace.data.Array) | ||||||
| nsdfg.arrays.pop(data) | ||||||
|
|
||||||
| # Create the call signature for the SDFG. | ||||||
| # Only the arguments required by the GT4Py program, i.e. `node.params`, are added | ||||||
| # as positional arguments. The implicit arguments, such as the offset providers or | ||||||
| # the arguments created by the translation process, must be passed as keyword arguments. | ||||||
| # NOTE: A program uses the `user_args` mechanism to perform the call. So, | ||||||
| # technically these arguments is not needed. However, the orchestrator needs | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| # it to work. Note that in the following list, tuple arguments to the | ||||||
| # program/fieldop are expanded. | ||||||
| sdfg.arg_names = sdfg_arg_names | ||||||
|
|
||||||
| return sdfg | ||||||
|
|
||||||
455 changes: 205 additions & 250 deletions
455
src/gt4py/next/program_processors/runners/dace/workflow/bindings.py
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -9,12 +9,10 @@ | |||||
| from __future__ import annotations | ||||||
|
|
||||||
| import dataclasses | ||||||
| import json | ||||||
| import os | ||||||
| import pathlib | ||||||
| import warnings | ||||||
| from collections.abc import Callable, MutableSequence, Sequence | ||||||
| from typing import Any, Final, TypeAlias | ||||||
| from collections.abc import Callable | ||||||
| from typing import Any, Final, Sequence, TypeAlias | ||||||
|
|
||||||
| import dace | ||||||
| import dace.codegen.compiler as dace_compiler | ||||||
|
|
@@ -126,61 +124,71 @@ def _validate_external_workspace( | |||||
| class CompiledDaceProgram: | ||||||
| sdfg_program: dace.CompiledSDFG | ||||||
|
|
||||||
| # Sorted list of SDFG arguments as they appear in program ABI and corresponding data type; | ||||||
| # scalar arguments that are not used in the SDFG will not be present. | ||||||
| sdfg_argtypes: list[dace.dtypes.Data] | ||||||
|
|
||||||
| # The compiled program contains a callable object to update the SDFG arguments list. | ||||||
| update_sdfg_ctype_arglist: Callable[ | ||||||
| [ | ||||||
| core_defs.DeviceType, | ||||||
| Sequence[dace.dtypes.Data], | ||||||
| Sequence[Any], | ||||||
| MutableSequence[Any], | ||||||
| common.OffsetProvider, | ||||||
| ], | ||||||
| None, | ||||||
| # Callable to process the GT4Py arguments and offset providers to bring them in a form suitable for calling. | ||||||
| argument_preprocessing_function: Callable[ | ||||||
| [Sequence[Any], common.OffsetProvider, int, Any], tuple[Any, ...] | ||||||
| ] | ||||||
|
|
||||||
| # Processed argument vectors that are passed to `CompiledSDFG.fast_call()`. `None` | ||||||
| # means that it has not been initialized, i.e. no call was ever performed. | ||||||
| # - csdfg_argv: Arguments used for calling the actual compiled SDFG, will be updated. | ||||||
| # - csdfg_init_argv: Arguments used for initialization; used only the first time and | ||||||
| # never updated. | ||||||
| csdfg_argv: MutableSequence[Any] | None | ||||||
| csdfg_init_argv: Sequence[Any] | None | ||||||
| external_workspace: gtx_wfdcommon.ExternalWorkspace | None = ( | ||||||
| None # This attribute is set at runtime, before the first call. | ||||||
| ) | ||||||
|
|
||||||
| # Whether the SDFG has transients with `AllocationLifetime.External`, i.e. whether | ||||||
| # the caller has to install a workspace before the program can run. Determined | ||||||
| # once at load time so that the (non-trivial) workspace configuration is only | ||||||
| # performed for the programs that actually need it. | ||||||
| requires_external_workspace: bool | ||||||
|
|
||||||
| def __init__( | ||||||
| self, | ||||||
| program: dace.CompiledSDFG, | ||||||
| bind_func_name: str, | ||||||
| binding_source_code: str, | ||||||
| ): | ||||||
| self.sdfg_program = program | ||||||
|
|
||||||
| # `dace.CompiledSDFG.arglist()` returns an ordered dictionary that maps the argument | ||||||
| # name to its data type, in the same order as arguments appear in the program ABI. | ||||||
| # This is also the same order of arguments in `dace.CompiledSDFG._lastargs[0]`. | ||||||
| self.sdfg_argtypes = list(program.sdfg.arglist().values()) | ||||||
| self.requires_external_workspace = any( | ||||||
| desc.lifetime == dace.dtypes.AllocationLifetime.External | ||||||
| for _, _, desc in program.sdfg.arrays_recursive() | ||||||
| ) | ||||||
|
|
||||||
| # The binding source code is Python tailored to this specific SDFG. | ||||||
| # We dynamically compile that function and add it to the compiled program. | ||||||
| global_namespace: dict[str, Any] = {} | ||||||
| exec(binding_source_code, global_namespace) | ||||||
| self.update_sdfg_ctype_arglist = global_namespace[bind_func_name] | ||||||
| # For debug purpose, we set a unique module name on the compiled function. | ||||||
| self.update_sdfg_ctype_arglist.__module__ = os.path.basename(program.sdfg.build_folder) | ||||||
|
|
||||||
| # Since the SDFG hasn't been called yet. | ||||||
| self.csdfg_argv = None | ||||||
| self.csdfg_init_argv = None | ||||||
| self.argument_preprocessing_function = global_namespace[bind_func_name] | ||||||
| # For debug purpose, we set a unique module name on the compiled function. | ||||||
| self.argument_preprocessing_function.__module__ = os.path.basename( | ||||||
| program.sdfg.build_folder | ||||||
| ) | ||||||
|
|
||||||
| def _configure_external_workspace(self, **kwargs: Any) -> None: | ||||||
| def configure_external_workspace(self, **kwargs: Any) -> None: | ||||||
| """Install the caller-provided workspace buffers on the compiled SDFG. | ||||||
|
|
||||||
| This eagerly initializes the SDFG state, queries the required workspace | ||||||
| size per storage type and hands the matching buffer of | ||||||
| `self.external_workspace` to DaCe. It has to run before the first call, | ||||||
| because an SDFG with external transients refuses to run with no | ||||||
| workspace installed. | ||||||
|
|
||||||
| The required sizes depend on the symbol values passed here, so they are | ||||||
| only valid for calls made with the same symbols. This is called once, | ||||||
| with the arguments of the first call; keeping the workspace large enough | ||||||
| for subsequent calls is the caller's responsibility. | ||||||
|
|
||||||
| Args: | ||||||
| kwargs: The SDFG call arguments. Only the free symbols among them are | ||||||
| used; the remaining entries are ignored by DaCe. | ||||||
|
|
||||||
| Raises: | ||||||
| RuntimeError: If the SDFG needs a workspace but none was set for the | ||||||
| required device. | ||||||
| TypeError: If a workspace buffer exposes no suitable array interface. | ||||||
| ValueError: If a workspace buffer is too small, or is required for an | ||||||
| unsupported storage type. | ||||||
| """ | ||||||
| self.sdfg_program.initialize(**kwargs) | ||||||
| if workspace_sizes := self.sdfg_program.get_workspace_sizes(): | ||||||
| if workspace_sizes := self.sdfg_program.get_workspace_sizes(**kwargs): | ||||||
| if self.external_workspace is None: | ||||||
| raise RuntimeError( | ||||||
| "External workspace is not set. Please call `set_external_workspace()`" | ||||||
|
|
@@ -192,77 +200,33 @@ def _configure_external_workspace(self, **kwargs: Any) -> None: | |||||
| if workspace is None: | ||||||
| raise RuntimeError(f"External workspace for device {device} not found.") | ||||||
| _validate_external_workspace(workspace, storage, required_nbytes) | ||||||
| self.sdfg_program.set_workspace(storage, workspace) | ||||||
|
|
||||||
| def construct_arguments(self, **kwargs: Any) -> None: | ||||||
| """ | ||||||
| This function will process the arguments and store the processed argument | ||||||
| vectors in `self.csdfg_args`, to call them use `self.fast_call()`. | ||||||
| """ | ||||||
| with dace.config.set_temporary("compiler", "allow_view_arguments", value=True): | ||||||
| csdfg_argv, csdfg_init_argv = self.sdfg_program.construct_arguments(**kwargs) | ||||||
| self._configure_external_workspace(**kwargs) | ||||||
| # Note we only care about `csdfg_argv` (normal call), since we have to update it, | ||||||
| # we ensure that it is a `list`. | ||||||
| self.csdfg_argv = [*csdfg_argv] | ||||||
| self.csdfg_init_argv = csdfg_init_argv | ||||||
|
|
||||||
| def fast_call(self) -> None: | ||||||
| """ | ||||||
| Perform a call to the compiled SDFG using the previously generated argument | ||||||
| vectors, see `self.construct_arguments()`. | ||||||
| """ | ||||||
| assert self.csdfg_argv is not None and self.csdfg_init_argv is not None, ( | ||||||
| "Argument vector was not set properly." | ||||||
| ) | ||||||
| self.sdfg_program.fast_call( | ||||||
| self.csdfg_argv, self.csdfg_init_argv, do_gpu_check=config.DEBUG | ||||||
| ) | ||||||
| self.sdfg_program.set_workspace(storage, workspace, **kwargs) | ||||||
|
|
||||||
| def __call__(self, **kwargs: Any) -> None: | ||||||
| """Call the compiled SDFG with the given arguments. | ||||||
|
|
||||||
| Note that this function will not update the argument vectors stored inside | ||||||
| `self`. Furthermore, it is not recommended to use this function as it is | ||||||
| very slow. | ||||||
| A `CompiledDaceProgram` should not be called directly. Instead | ||||||
| `gt4py.next.program_processors.runners.dace.workflow.decoration.convert_args()` | ||||||
| should be used to obtain a callable. | ||||||
| """ | ||||||
| warnings.warn( | ||||||
| "Called an SDFG through the standard DaCe interface is not recommended, use `fast_call()` instead.", | ||||||
| stacklevel=1, | ||||||
| raise NotImplementedError( | ||||||
| "A `CompiledDaceProgram` can not be called directly. Instead use " | ||||||
| "`gt4py.next.program_processors.runners.dace.workflow.decoration.convert_args()`." | ||||||
| ) | ||||||
| result = self.sdfg_program(**kwargs) | ||||||
| assert result is None | ||||||
|
|
||||||
|
|
||||||
| @dataclasses.dataclass(frozen=True) | ||||||
| class DaCeCompilationArtifact: | ||||||
| """Result of a DaCe compilation: library path + SDFG bindings + the SDFG itself. | ||||||
|
|
||||||
| The SDFG is carried inline as JSON because dace's load path | ||||||
| (``get_program_handle``) needs an SDFG instance to wrap into the | ||||||
| returned ``CompiledSDFG``, and the build folder may not contain a | ||||||
| ``program.sdfg(z)`` dump under the upcoming minimal-build-dir mode. | ||||||
|
|
||||||
| The SDFG we store here is the one on which we called `SDFG.compile(return_program_handle=False)`. | ||||||
| Note that the `compile()` call has side effects, because it applies transformations | ||||||
| to the SDFG, in order to enable code generation for the target platform. | ||||||
| Since we pass `return_program_handle=False`, the `compile()` method does not | ||||||
| return a `CompiledSDFG` instance, therefore we cannot access `CompiledSDFG.sdfg`, | ||||||
| which would be the modified SDFG from which DaCe generates the C++/CUDA/HIP code. | ||||||
| """ | ||||||
| """Result of a DaCe compilation: library path + SDFG bindings + the SDFG itself.""" | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| library_path: pathlib.Path | ||||||
| sdfg_json: str | ||||||
| sdfg_build_folder: pathlib.Path | ||||||
| binding_source_code: str | ||||||
| bind_func_name: str | ||||||
| device_type: core_defs.DeviceType | ||||||
|
|
||||||
| def load(self) -> stages.ExecutableProgram: | ||||||
| # TODO(phimuell): Drop ``sdfg_json`` from the artifact once dace | ||||||
| # exposes a load path that doesn't require an SDFG instance to wrap | ||||||
| # into the returned ``CompiledSDFG``. | ||||||
| sdfg = dace.SDFG.from_json(json.loads(self.sdfg_json)) | ||||||
| sdfg_program = dace_compiler.get_program_handle(self.library_path, sdfg) | ||||||
| sdfg_program = dace_compiler.load_precompiled_sdfg(self.sdfg_build_folder, sdfg=None) | ||||||
| sdfg_program.gpu_error_check = False # Not useful in asynchronous launches. | ||||||
| program = CompiledDaceProgram(sdfg_program, self.bind_func_name, self.binding_source_code) | ||||||
| return gtx_wfddecoration.DaCeDecoratedProgram(program, device_type=self.device_type) | ||||||
|
|
||||||
|
|
@@ -290,7 +254,8 @@ class DaCeCompiler( | |||||
| cmake_build_type: config.CMakeBuildType = dataclasses.field( | ||||||
| default_factory=lambda: config.CMAKE_BUILD_TYPE | ||||||
| ) | ||||||
| # we store the non-default values of `dace.Config` in order to include it in the stage fingerprint | ||||||
| # We store the non-default values of `dace.Config` in order to include it in the stage fingerprint | ||||||
| # NOTE: They do not include the non default keys set through DaCe environment variables. | ||||||
| dace_config_nondefaults: dict[str, Any] = dataclasses.field(init=False) | ||||||
|
|
||||||
| def __post_init__(self) -> None: | ||||||
|
|
@@ -323,8 +288,8 @@ def __call__(self, inp: SDFGExtensionSource) -> DaCeCompilationArtifact: | |||||
| # Configure the SDFG build folder | ||||||
| sdfg.build_folder = sdfg_build_folder | ||||||
|
|
||||||
| # ``build_folder_mode`` is set by ``dace_context``; resolve the library | ||||||
| # path here so ``get_binary_name`` sees the same mode dace built under. | ||||||
| # `compiler.build_folder_mode` is set by `dace_context()`; resolve the library | ||||||
| # path here so `get_binary_name()` sees the same mode DaCe built under. | ||||||
| library_path = dace_compiler.get_binary_name( | ||||||
| object_folder=sdfg_build_folder, sdfg_name=sdfg.name | ||||||
| ) | ||||||
|
|
@@ -348,8 +313,7 @@ def __call__(self, inp: SDFGExtensionSource) -> DaCeCompilationArtifact: | |||||
|
|
||||||
| assert inp.binding_source is not None | ||||||
| return DaCeCompilationArtifact( | ||||||
| library_path=library_path, | ||||||
| sdfg_json=json.dumps(inp.program_source.source_code), | ||||||
| sdfg_build_folder=sdfg_build_folder, | ||||||
| binding_source_code=inp.binding_source.source_code, | ||||||
| bind_func_name=self.bind_func_name, | ||||||
| device_type=self.device_type, | ||||||
|
|
||||||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.