-
Notifications
You must be signed in to change notification settings - Fork 50
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
feat[cartesian] Debug Backend #1884
base: main
Are you sure you want to change the base?
Conversation
cscs-ci run |
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.
Just had a quick look and found some merge conflicts.
.gitignore
Outdated
.gt_cache/ | ||
.gt4py_cache/ | ||
.gt_cache*/ |
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.
This is a merge conflict with #1875. I guess you want to change line 8.
|
||
# GT4Py - GridTools Framework | ||
# | ||
# Copyright (c) 2014-2023, ETH Zurich | ||
# All rights reserved. | ||
# | ||
# This file is part of the GT4Py project and the GridTools framework. | ||
# GT4Py is free software: you can redistribute it and/or modify it under | ||
# the terms of the GNU General Public License as published by the | ||
# Free Software Foundation, either version 3 of the License, or any later | ||
# version. See the LICENSE.txt file at the top-level directory of this | ||
# distribution for a copy of the license or check <https://www.gnu.org/licenses/>. | ||
# | ||
# SPDX-License-Identifier: GPL-3.0-or-later |
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.
# GT4Py - GridTools Framework | |
# | |
# Copyright (c) 2014-2023, ETH Zurich | |
# All rights reserved. | |
# | |
# This file is part of the GT4Py project and the GridTools framework. | |
# GT4Py is free software: you can redistribute it and/or modify it under | |
# the terms of the GNU General Public License as published by the | |
# Free Software Foundation, either version 3 of the License, or any later | |
# version. See the LICENSE.txt file at the top-level directory of this | |
# distribution for a copy of the license or check <https://www.gnu.org/licenses/>. | |
# | |
# SPDX-License-Identifier: GPL-3.0-or-later |
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.
(and in other files)
Co-authored-by: Hannes Vogt <[email protected]>
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.
Looking good in general. I left a bunch of inline comments. Most of them are simple code transformation that try to simplify and enhance readability.
import numpy as np | ||
|
||
|
||
class Field: |
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.
what's the intent of this class? Can it be re-used in other backends? If not, I'd suggest to move it into the debug backend folder.
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.
what's the intent of this class?
I have the same question.
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.
It can be re-used. For now it is hard-coded as code-generation in the numpy backend:
class Field: |
The goal would be to move this at some point in the future
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.
I guess it's out of scope for this PR. Can we log a task instead to do this in the future and maybe add a comment in code that this will be done?
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.
Is there already an issue for this cleanup? I fear we might forget otherwise
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.
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.
I would propose to do it here, it should be as easy as inspect.getsource(Field)
tests/cartesian_tests/integration_tests/multi_feature_tests/test_debug_backend.py
Outdated
Show resolved
Hide resolved
Co-authored-by: Roman Cattaneo <[email protected]>
Co-authored-by: Roman Cattaneo <[email protected]>
import numpy as np | ||
|
||
|
||
class Field: |
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.
what's the intent of this class?
I have the same question.
@@ -37,6 +37,9 @@ | |||
abs: np.ufunc = np.abs # noqa: A001 [builtin-variable-shadowing] | |||
minimum: np.ufunc = np.minimum | |||
maximum: np.ufunc = np.maximum | |||
max: np.ufunc = np.maximum # noqa: A001 |
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.
Any reason for adding these aliases?
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.
this is so we can uniformly generate ufunc.function_name
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.
Looking nice and clean now in my eyes :) Just two small questions inline.
def recursive_write(self, root_path: pathlib.Path, tree: dict[str, Union[str, dict]]): | ||
root_path.mkdir(parents=True, exist_ok=True) | ||
for key, value in tree.items(): | ||
if isinstance(value, dict): | ||
self.recursive_write(root_path / key, value) | ||
else: | ||
src_path = root_path / key | ||
src_path.write_text(value) |
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.
This function used to be a standalone function. Now it is part of a base class without accessing any property or method of that class. Have you considered leaving it a standalone function? We could keep it in the same file or create src/gt4py/cartesian/backend/utils.py
and put it there. Another prime candidate to move to a potential backend/utils.py
(in a follow-up PR) would be
gt4py/src/gt4py/cartesian/backend/base.py
Lines 433 to 455 in b6a3162
def disabled(message: str, *, enabled_env_var: str) -> Callable[[Type[Backend]], Type[Backend]]: | |
# We push for hard deprecation here by raising by default and warning if enabling has been forced. | |
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 |
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.
I think since it is so tied to the backends and this is the only place this is used, adding it to the class gives it a more direct link to where the work is used. I prefer this way to avoid generic utils
files that have a tendency to grow big. If there are utilities that are used across larger modules, I see value that way.
We could make it a staticmethod
though
self.body.append(while_code) | ||
self.body.indent() | ||
for statement in while_node.body: | ||
self.visit(statement) |
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.
passing on/down the received kwargs
is a common pattern in node visitors, e.g. visit_Interval
below does that. Here kwargs
aren't forwarded to the body of the while loop. Is that okay to do?
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.
We could opt for generically forwarding all the kwargs all the time. I chose to, for the sake of clarity, only do the forwarding where it is neccesasry to pass in pre-compted information.
import numpy as np | ||
|
||
|
||
class Field: |
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.
Is there already an issue for this cleanup? I fear we might forget otherwise
cscs-ci run |
cscs-ci run |
1 similar comment
cscs-ci run |
cscs-ci run |
1 similar comment
cscs-ci run |
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.
I only had a closer look now. Main points:
- import style
- how to use the helpers on top of eve.NodeVisitor.
import numpy as np | ||
|
||
|
||
class Field: |
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.
I would propose to do it here, it should be as easy as inspect.getsource(Field)
|
||
from gt4py import eve | ||
from gt4py.cartesian import utils | ||
from gt4py.cartesian.gtc.common import ( |
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.
Since this is a new file, let's impose strictly our coding guidelines. Only import modules, see https://google.github.io/styleguide/pyguide.html#22-imports.
Here I would do
from gt4py.cartesian.gtc.common import ( | |
from gt4py.cartesian.gtc import common as gtc_common |
to distinguish from other common
modules.
LoopOrder, | ||
While, | ||
) | ||
from gt4py.cartesian.gtc.definitions import Extent |
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.
from gt4py.cartesian.gtc.definitions import Extent | |
from gt4py.cartesian.gtc import definitions as gtc_definitions |
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.
or check if the convention for the name is different in cartesian/gtc
from gt4py.cartesian.gtc.oir import ( | ||
AssignStmt, | ||
BinaryOp, | ||
CacheDesc, | ||
Cast, | ||
Decl, | ||
FieldDecl, | ||
HorizontalExecution, | ||
HorizontalRestriction, | ||
IJCache, | ||
Interval, | ||
KCache, | ||
Literal, | ||
LocalScalar, | ||
MaskStmt, | ||
NativeFuncCall, | ||
ScalarAccess, | ||
ScalarDecl, | ||
Stencil, | ||
Temporary, | ||
TernaryOp, | ||
UnaryOp, | ||
UnboundedInterval, | ||
VariableKOffset, | ||
VerticalLoop, | ||
VerticalLoopSection, | ||
) |
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.
from gt4py.cartesian.gtc.oir import ( | |
AssignStmt, | |
BinaryOp, | |
CacheDesc, | |
Cast, | |
Decl, | |
FieldDecl, | |
HorizontalExecution, | |
HorizontalRestriction, | |
IJCache, | |
Interval, | |
KCache, | |
Literal, | |
LocalScalar, | |
MaskStmt, | |
NativeFuncCall, | |
ScalarAccess, | |
ScalarDecl, | |
Stencil, | |
Temporary, | |
TernaryOp, | |
UnaryOp, | |
UnboundedInterval, | |
VariableKOffset, | |
VerticalLoop, | |
VerticalLoopSection, | |
) | |
from gt4py.cartesian.gtc import oir |
etc
|
||
|
||
import numbers | ||
from typing import Tuple |
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.
fyi, this import is ok, as pure typing imports are excluded from the rule, see https://google.github.io/styleguide/pyguide.html#2241-exemptions
from gt4py.eve.concepts import SymbolRef | ||
|
||
|
||
class DebugCodeGen(codegen.TemplatedGenerator, eve.VisitorWithSymbolTableTrait): |
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.
- It seems you are not using the SymbolTableTrait, but instead of managing symbols manually. What's the reason?
- You are also not using the TemplatedGenerator, only a normal NodeVisitor as far as I can see.
I could give an intro to both in case there are questions.
from gt4py import storage | ||
from gt4py.cartesian.backend.base import BaseBackend, CLIBackendMixin, register | ||
from gt4py.cartesian.backend.numpy_backend import ModuleGenerator | ||
from gt4py.cartesian.gtc.debug.debug_codegen import DebugCodeGen | ||
from gt4py.cartesian.gtc.gtir_to_oir import GTIRToOIR | ||
from gt4py.cartesian.gtc.passes.oir_optimizations.horizontal_execution_merging import ( | ||
HorizontalExecutionMerging, | ||
) | ||
from gt4py.cartesian.gtc.passes.oir_optimizations.temporaries import LocalTemporariesToScalars | ||
from gt4py.cartesian.gtc.passes.oir_pipeline import OirPipeline | ||
from gt4py.eve.codegen import format_source |
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.
See my comments about imports below
Description
This PR implements a plain python backend for debugging and rapid prototyping of features.
The main difference between the debug backend and the numpy backend is that this backend is ment to be easier to step through with a debugger to understand the generated code or to visually inspect the generated code because model developers are more familiar with with loop based implementations.
The second big difference is that this backend directly represents the OIR without lowering to an additional lower level IR to visualize the resulting OIR a bit more directly.
This backend is not optimized for speed but rather for readabiltiy.
Requirements