Skip to content
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

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open

Conversation

twicki
Copy link
Contributor

@twicki twicki commented Feb 24, 2025

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

  • All fixes and/or new features come with corresponding tests.

@twicki twicki changed the title <feat>[cartesian] Debug Backend feat[cartesian] Debug Backend Feb 24, 2025
@FlorianDeconinck
Copy link
Contributor

cscs-ci run

Copy link
Contributor

@havogt havogt left a 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
Comment on lines 8 to 9
.gt_cache/
.gt4py_cache/
.gt_cache*/
Copy link
Contributor

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.

Comment on lines 8 to 21

# 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
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
# 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

Copy link
Contributor

Choose a reason for hiding this comment

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

(and in other files)

Copy link
Contributor

@romanc romanc left a 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:
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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:

The goal would be to move this at some point in the future

Copy link
Contributor

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?

Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

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)

import numpy as np


class Field:
Copy link
Contributor

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
Copy link
Contributor

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?

Copy link
Contributor Author

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

@romanc romanc self-requested a review March 4, 2025 13:16
Copy link
Contributor

@romanc romanc left a 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.

Comment on lines +320 to +327
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)
Copy link
Contributor

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

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

Copy link
Contributor Author

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)
Copy link
Contributor

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?

Copy link
Contributor Author

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:
Copy link
Contributor

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

@romanc
Copy link
Contributor

romanc commented Mar 5, 2025

cscs-ci run

@FlorianDeconinck FlorianDeconinck added the gt4py.cartesian Issues concerning the current version with support only for cartesian grids. label Mar 5, 2025
@romanc
Copy link
Contributor

romanc commented Mar 5, 2025

cscs-ci run

1 similar comment
@twicki
Copy link
Contributor Author

twicki commented Mar 6, 2025

cscs-ci run

@FlorianDeconinck
Copy link
Contributor

cscs-ci run

1 similar comment
@romanc
Copy link
Contributor

romanc commented Mar 6, 2025

cscs-ci run

Copy link
Contributor

@havogt havogt left a 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:
Copy link
Contributor

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 (
Copy link
Contributor

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

Suggested change
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
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
from gt4py.cartesian.gtc.definitions import Extent
from gt4py.cartesian.gtc import definitions as gtc_definitions

Copy link
Contributor

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

Comment on lines +28 to +54
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,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
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
Copy link
Contributor

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):
Copy link
Contributor

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.

Comment on lines +12 to +22
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
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
gt4py.cartesian Issues concerning the current version with support only for cartesian grids.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants