-
Notifications
You must be signed in to change notification settings - Fork 49
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
refactor[cartesian]: use DeviceType
from core_defn in LayoutInfo
#1867
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,9 +45,9 @@ | |
REGISTRY = gt_utils.Registry() | ||
|
||
|
||
def from_name(name: str) -> Optional[Type[Backend]]: | ||
def from_name(name: str) -> Type[Backend]: | ||
backend = REGISTRY.get(name, None) | ||
if not backend: | ||
if backend is None: | ||
raise NotImplementedError( | ||
f"Backend {name} is not implemented, options are: {REGISTRY.names}" | ||
) | ||
|
@@ -84,7 +84,7 @@ class Backend(abc.ABC): | |
#: Backend-specific storage parametrization: | ||
#: | ||
#: - "alignment": in bytes | ||
#: - "device": "cpu" | "gpu" | ||
#: - "device": core_defs.DeviceType | None | ||
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. Just a question: why is |
||
#: - "layout_map": callback converting a mask to a layout | ||
#: - "is_optimal_layout": callback checking if a storage has compatible layout | ||
storage_info: ClassVar[gt_storage.layout.LayoutInfo] | ||
|
@@ -435,21 +435,20 @@ def disabled(message: str, *, enabled_env_var: str) -> Callable[[Type[Backend]], | |
enabled = bool(int(os.environ.get(enabled_env_var, "0"))) | ||
if enabled: | ||
return deprecated(message) | ||
else: | ||
|
||
def _decorator(cls: Type[Backend]) -> Type[Backend]: | ||
def _no_generate(obj) -> Type[StencilObject]: | ||
raise NotImplementedError( | ||
f"Disabled '{cls.name}' backend: 'f{message}'\n", | ||
f"You can still enable the backend by hand using the environment variable '{enabled_env_var}=1'", | ||
) | ||
|
||
# Replace generate method with raise | ||
if not hasattr(cls, "generate"): | ||
raise ValueError(f"Coding error. Expected a generate method on {cls}") | ||
# Flag that it got disabled for register lookup | ||
cls.disabled = True # type: ignore | ||
cls.generate = _no_generate # type: ignore | ||
return cls | ||
|
||
return _decorator | ||
def _decorator(cls: Type[Backend]) -> Type[Backend]: | ||
def _no_generate(obj) -> Type[StencilObject]: | ||
raise NotImplementedError( | ||
f"Disabled '{cls.name}' backend: 'f{message}'\n", | ||
f"You can still enable the backend by hand using the environment variable '{enabled_env_var}=1'", | ||
) | ||
|
||
# Replace generate method with raise | ||
if not hasattr(cls, "generate"): | ||
raise ValueError(f"Coding error. Expected a generate method on {cls}") | ||
# Flag that it got disabled for register lookup | ||
cls.disabled = True # type: ignore | ||
cls.generate = _no_generate # type: ignore | ||
return cls | ||
|
||
return _decorator | ||
Comment on lines
+437
to
+452
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. Unrelated: no |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -421,7 +421,7 @@ def __call__(self, stencil_ir: gtir.Stencil) -> Dict[str, Dict[str, str]]: | |
stencil_ir, sdfg, module_name=self.module_name, backend=self.backend | ||
) | ||
|
||
bindings_ext = "cu" if self.backend.storage_info["device"] == "gpu" else "cpp" | ||
bindings_ext = "cu" if self.backend.name.endswith(":gpu") else "cpp" | ||
sources = { | ||
"computation": {"computation.hpp": implementation}, | ||
"bindings": {f"bindings.{bindings_ext}": bindings}, | ||
|
@@ -674,9 +674,7 @@ def generate_entry_params(self, stencil_ir: gtir.Stencil, sdfg: dace.SDFG) -> Li | |
assert isinstance(data, dace.data.Array) | ||
res[name] = ( | ||
"py::{pybind_type} {name}, std::array<gt::int_t,{ndim}> {name}_origin".format( | ||
pybind_type=( | ||
"object" if self.backend.storage_info["device"] == "gpu" else "buffer" | ||
), | ||
pybind_type=("object" if self.backend.name.endswith(":gpu") else "buffer"), | ||
name=name, | ||
ndim=len(data.shape), | ||
) | ||
|
@@ -777,14 +775,7 @@ def generate(self) -> Type[StencilObject]: | |
class DaceCPUBackend(BaseDaceBackend): | ||
name = "dace:cpu" | ||
languages = {"computation": "c++", "bindings": ["python"]} | ||
storage_info = { | ||
"alignment": 1, | ||
"device": "cpu", | ||
"layout_map": gt_storage.layout.layout_maker_factory((0, 1, 2)), | ||
"is_optimal_layout": gt_storage.layout.layout_checker_factory( | ||
gt_storage.layout.layout_maker_factory((0, 1, 2)) | ||
), | ||
} | ||
Comment on lines
-780
to
-787
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. Moved to |
||
storage_info = gt_storage.layout.DaceCPULayout | ||
MODULE_GENERATOR_CLASS = DaCePyExtModuleGenerator | ||
|
||
options = BaseGTBackend.GT_BACKEND_OPTS | ||
|
@@ -799,14 +790,7 @@ class DaceGPUBackend(BaseDaceBackend): | |
|
||
name = "dace:gpu" | ||
languages = {"computation": "cuda", "bindings": ["python"]} | ||
storage_info = { | ||
"alignment": 32, | ||
"device": "gpu", | ||
"layout_map": gt_storage.layout.layout_maker_factory((2, 1, 0)), | ||
"is_optimal_layout": gt_storage.layout.layout_checker_factory( | ||
gt_storage.layout.layout_maker_factory((2, 1, 0)) | ||
), | ||
} | ||
storage_info = gt_storage.layout.DaceGPULayout | ||
MODULE_GENERATOR_CLASS = DaCeCUDAPyExtModuleGenerator | ||
options = {**BaseGTBackend.GT_BACKEND_OPTS, "device_sync": {"versioning": True, "type": bool}} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ | |
from dataclasses import dataclass | ||
from numbers import Number | ||
from pickle import dumps | ||
from typing import Any, Callable, ClassVar, Dict, Literal, Optional, Tuple, Union, cast | ||
from typing import Any, Callable, ClassVar, Dict, Optional, Tuple, Union, cast | ||
|
||
import numpy as np | ||
|
||
|
@@ -24,6 +24,7 @@ | |
from gt4py import cartesian as gt4pyc | ||
from gt4py.cartesian.definitions import AccessKind, DomainInfo, FieldInfo, ParameterInfo | ||
from gt4py.cartesian.gtc.definitions import Index, Shape | ||
from gt4py.storage.cartesian.layout import LayoutInfo | ||
|
||
|
||
try: | ||
|
@@ -51,22 +52,21 @@ def _compute_domain_origin_cache_key( | |
|
||
@dataclass | ||
class ArgsInfo: | ||
device: str | ||
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. Never used, so I removed it |
||
array: FieldType | ||
original_object: Optional[Any] = None | ||
origin: Optional[Tuple[int, ...]] = None | ||
dimensions: Optional[Tuple[str, ...]] = None | ||
|
||
|
||
def _extract_array_infos( | ||
field_args: Dict[str, Optional[FieldType]], device: Literal["cpu", "gpu"] | ||
field_args: Dict[str, Optional[FieldType]], layout_info: LayoutInfo | ||
) -> Dict[str, Optional[ArgsInfo]]: | ||
array_infos: Dict[str, Optional[ArgsInfo]] = {} | ||
for name, arg in field_args.items(): | ||
if arg is None: | ||
array_infos[name] = None | ||
else: | ||
array = storage_utils.asarray(arg, device=device) | ||
array = storage_utils.asarray(arg, layout_info=layout_info) | ||
dimensions = storage_utils.get_dims(arg) | ||
if dimensions is not None: | ||
sorted_dimensions = [d for d in "IJK" if d in dimensions] | ||
|
@@ -79,7 +79,6 @@ def _extract_array_infos( | |
array=array, | ||
original_object=arg, | ||
dimensions=dimensions, | ||
device=device, | ||
origin=storage_utils.get_origin(arg), | ||
) | ||
return array_infos | ||
|
@@ -562,8 +561,7 @@ def _call_run( | |
exec_info["call_run_start_time"] = time.perf_counter() | ||
backend_cls = gt4pyc.backend.from_name(self.backend) | ||
assert backend_cls is not None | ||
device = backend_cls.storage_info["device"] | ||
array_infos = _extract_array_infos(field_args, device) | ||
array_infos = _extract_array_infos(field_args, backend_cls.storage_info) | ||
|
||
cache_key = _compute_domain_origin_cache_key(array_infos, parameter_args, domain, origin) | ||
if cache_key not in self._domain_origin_cache: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# GT4Py - GridTools Framework | ||
# | ||
# Copyright (c) 2014-2024, ETH Zurich | ||
# All rights reserved. | ||
# | ||
# Please, refer to the LICENSE file in the root directory. | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
|
||
from gt4py.cartesian.backend.base import REGISTRY as BACKEND_REGISTRY | ||
|
||
|
||
# TODO (romanc) | ||
# This file can move to the tests/ folder once we refactor the `StencilTestSuite` | ||
# class (i.e. https://github.com/GEOS-ESM/NDSL/issues/72). The stencil test suites | ||
# use the `GPU_BACKEND_NAMES` and I didn't wanna have `gt4py/cartesian/testing` depend | ||
# on the `tests/` directory. That sounded the wrong way around, so I moved them here | ||
# for now. | ||
|
||
ALL_BACKEND_NAMES = list(BACKEND_REGISTRY.keys()) | ||
|
||
GPU_BACKEND_NAMES = ["cuda", "gt:gpu", "dace:gpu"] | ||
CPU_BACKEND_NAMES = [name for name in ALL_BACKEND_NAMES if name not in GPU_BACKEND_NAMES] | ||
|
||
PERFORMANCE_BACKEND_NAMES = [name for name in ALL_BACKEND_NAMES if name not in ("numpy", "cuda")] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ | |
from gt4py.cartesian.definitions import AccessKind, FieldInfo | ||
from gt4py.cartesian.gtc.definitions import Boundary, CartesianSpace, Index, Shape | ||
from gt4py.cartesian.stencil_object import StencilObject | ||
from gt4py.cartesian.testing.definitions import GPU_BACKEND_NAMES | ||
from gt4py.storage.cartesian import utils as storage_utils | ||
|
||
from .input_strategies import ( | ||
|
@@ -206,7 +207,7 @@ def hyp_wrapper(test_hyp, hypothesis_data): | |
) | ||
|
||
marks = test["marks"].copy() | ||
if gt4pyc.backend.from_name(test["backend"]).storage_info["device"] == "gpu": | ||
if test["backend"] in GPU_BACKEND_NAMES: | ||
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. We come by here during test setup. This happens for CPU/GPU tests alike. In case For the test system, we thus need something that is independent of cupy availability to denote that a certain tests require GPU while other don't. This could also be done differently, e.g. a 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. Agreed. I think the earlier design was to split layout from backend by addressing them by their underlying allocator (numpy/cupy) to the backend. Though it's correct architecturally, I would argue it's a bit of an over engineering and repeating data is not an issue. Which leads me to the fact that Backend should be the source of truth, including for 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. And we could extend it even further by re-routing allocation through, etc. I am no fan of massive class that does it all, but in this case 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. Probably out of scope for this PR |
||
marks.append(pytest.mark.requires_gpu) | ||
# Run generation and implementation tests in the same group to ensure | ||
# (thread-) safe parallelization of stencil tests. | ||
|
@@ -240,7 +241,7 @@ def hyp_wrapper(test_hyp, hypothesis_data): | |
) | ||
|
||
marks = test["marks"].copy() | ||
if gt4pyc.backend.from_name(test["backend"]).storage_info["device"] == "gpu": | ||
if test["backend"] in GPU_BACKEND_NAMES: | ||
marks.append(pytest.mark.requires_gpu) | ||
# Run generation and implementation tests in the same group to ensure | ||
# (thread-) safe parallelization of stencil tests. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# GT4Py - GridTools Framework | ||
# | ||
# Copyright (c) 2014-2024, ETH Zurich | ||
# All rights reserved. | ||
# | ||
# Please, refer to the LICENSE file in the root directory. | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
|
||
from __future__ import annotations | ||
|
||
from typing import Final, Literal | ||
|
||
from gt4py._core.definitions import DeviceType | ||
|
||
|
||
try: | ||
import cupy as cp | ||
except ImportError: | ||
cp = None | ||
|
||
|
||
CUPY_DEVICE: Final[Literal[DeviceType.CUDA, DeviceType.ROCM] | None] = ( | ||
None if not cp else (DeviceType.ROCM if cp.cuda.get_hipcc_path() else DeviceType.CUDA) | ||
) | ||
Comment on lines
+22
to
+24
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. We could share this between next and cartesian. If so, should it go into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated: The return type is not optional. It raises an exception in case the backend is not found.