-
Notifications
You must be signed in to change notification settings - Fork 0
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
[Py2F]: add profiling support & optimisations #449
Changes from all commits
6284fab
ec54515
8bcca18
a5241bf
03e4045
7e21089
e6b453f
47be422
a55c509
0b7afb5
17fc611
ae733b1
b06ee35
53dda46
d0995e5
e133e75
6c81409
18fab84
fbdcd07
bfe3b19
f105880
2a1345d
c8660c5
dffdbc3
eb40203
c272075
6be6136
82515de
06d2a04
ffb27c6
9ea0689
7900cec
07751ec
92e08fc
8619487
aaa39eb
44cf44e
e14ac59
7ee29e7
e0aca5e
0e596fa
f3c49b9
0fd5f10
b3bc385
b4bc10a
f133da5
fd44acd
1de4962
639727a
30726b2
389ea83
12f2dab
8e45e06
fa98986
833f7be
08906f1
b4211d8
622c64a
b5086a9
6f7d074
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 |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# ICON4Py - ICON inspired code in Python and GT4Py | ||
# | ||
# Copyright (c) 2022, ETH Zurich and MeteoSwiss | ||
# All rights reserved. | ||
# | ||
# This file 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 | ||
|
||
from icon4py.model.atmosphere.diffusion.diffusion_utils import ( | ||
copy_field as copy_field_orig, | ||
init_diffusion_local_fields_for_regular_timestep as init_diffusion_local_fields_for_regular_timestep_orig, | ||
scale_k as scale_k_orig, | ||
setup_fields_for_initial_step as setup_fields_for_initial_step_orig, | ||
) | ||
from icon4py.model.atmosphere.diffusion.stencils.apply_diffusion_to_vn import ( | ||
apply_diffusion_to_vn as apply_diffusion_to_vn_orig, | ||
) | ||
from icon4py.model.atmosphere.diffusion.stencils.apply_diffusion_to_w_and_compute_horizontal_gradients_for_turbulence import ( | ||
apply_diffusion_to_w_and_compute_horizontal_gradients_for_turbulence as apply_diffusion_to_w_and_compute_horizontal_gradients_for_turbulence_orig, | ||
) | ||
from icon4py.model.atmosphere.diffusion.stencils.calculate_diagnostic_quantities_for_turbulence import ( | ||
calculate_diagnostic_quantities_for_turbulence as calculate_diagnostic_quantities_for_turbulence_orig, | ||
) | ||
from icon4py.model.atmosphere.diffusion.stencils.calculate_enhanced_diffusion_coefficients_for_grid_point_cold_pools import ( | ||
calculate_enhanced_diffusion_coefficients_for_grid_point_cold_pools as calculate_enhanced_diffusion_coefficients_for_grid_point_cold_pools_orig, | ||
) | ||
from icon4py.model.atmosphere.diffusion.stencils.calculate_nabla2_and_smag_coefficients_for_vn import ( | ||
calculate_nabla2_and_smag_coefficients_for_vn as calculate_nabla2_and_smag_coefficients_for_vn_orig, | ||
) | ||
from icon4py.model.atmosphere.diffusion.stencils.calculate_nabla2_for_theta import ( | ||
calculate_nabla2_for_theta as calculate_nabla2_for_theta_orig, | ||
) | ||
from icon4py.model.atmosphere.diffusion.stencils.truly_horizontal_diffusion_nabla_of_theta_over_steep_points import ( | ||
truly_horizontal_diffusion_nabla_of_theta_over_steep_points as truly_horizontal_diffusion_nabla_of_theta_over_steep_points_orig, | ||
) | ||
from icon4py.model.atmosphere.diffusion.stencils.update_theta_and_exner import ( | ||
update_theta_and_exner as update_theta_and_exner_orig, | ||
) | ||
from icon4py.model.common.caching import CachedProgram | ||
from icon4py.model.common.interpolation.stencils.mo_intp_rbf_rbf_vec_interpol_vertex import ( | ||
mo_intp_rbf_rbf_vec_interpol_vertex as mo_intp_rbf_rbf_vec_interpol_vertex_orig, | ||
) | ||
|
||
|
||
# diffusion run stencils | ||
apply_diffusion_to_vn = CachedProgram(apply_diffusion_to_vn_orig) | ||
apply_diffusion_to_w_and_compute_horizontal_gradients_for_turbulence = CachedProgram( | ||
apply_diffusion_to_w_and_compute_horizontal_gradients_for_turbulence_orig | ||
) | ||
calculate_diagnostic_quantities_for_turbulence = CachedProgram( | ||
calculate_diagnostic_quantities_for_turbulence_orig | ||
) | ||
calculate_enhanced_diffusion_coefficients_for_grid_point_cold_pools = CachedProgram( | ||
calculate_enhanced_diffusion_coefficients_for_grid_point_cold_pools_orig | ||
) | ||
calculate_nabla2_and_smag_coefficients_for_vn = CachedProgram( | ||
calculate_nabla2_and_smag_coefficients_for_vn_orig | ||
) | ||
calculate_nabla2_for_theta = CachedProgram(calculate_nabla2_for_theta_orig) | ||
truly_horizontal_diffusion_nabla_of_theta_over_steep_points = CachedProgram( | ||
truly_horizontal_diffusion_nabla_of_theta_over_steep_points_orig | ||
) | ||
update_theta_and_exner = CachedProgram(update_theta_and_exner_orig) | ||
|
||
mo_intp_rbf_rbf_vec_interpol_vertex = CachedProgram(mo_intp_rbf_rbf_vec_interpol_vertex_orig) | ||
|
||
|
||
# model init stencils | ||
setup_fields_for_initial_step = CachedProgram(setup_fields_for_initial_step_orig, with_domain=False) | ||
copy_field = CachedProgram(copy_field_orig, with_domain=False) | ||
init_diffusion_local_fields_for_regular_timestep = CachedProgram( | ||
init_diffusion_local_fields_for_regular_timestep_orig, with_domain=False | ||
) | ||
scale_k = CachedProgram(scale_k_orig, with_domain=False) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
# ICON4Py - ICON inspired code in Python and GT4Py | ||
# | ||
# Copyright (c) 2022, ETH Zurich and MeteoSwiss | ||
# All rights reserved. | ||
# | ||
# This file 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 | ||
|
||
import dataclasses | ||
from typing import Any, Callable, Optional | ||
|
||
import numpy as np | ||
from gt4py import next as gtx | ||
from gt4py.next.otf import workflow | ||
from gt4py.next.program_processors.runners.gtfn import extract_connectivity_args | ||
|
||
from icon4py.model.common.settings import device | ||
|
||
|
||
try: | ||
import cupy as cp | ||
from gt4py.next.embedded.nd_array_field import CuPyArrayField | ||
except ImportError: | ||
cp: Optional = None # type:ignore[no-redef] | ||
|
||
from gt4py.next.embedded.nd_array_field import NumPyArrayField | ||
|
||
|
||
def handle_numpy_integer(value): | ||
return int(value) | ||
|
||
|
||
def handle_common_field(value, sizes): | ||
sizes.extend(value.shape) | ||
return value # Return the value unmodified, but side-effect on sizes | ||
|
||
|
||
def handle_default(value): | ||
return value # Return the value unchanged | ||
|
||
|
||
if cp: | ||
type_handlers = { | ||
np.integer: handle_numpy_integer, | ||
NumPyArrayField: handle_common_field, | ||
CuPyArrayField: handle_common_field, | ||
} | ||
else: | ||
type_handlers = { | ||
np.integer: handle_numpy_integer, | ||
NumPyArrayField: handle_common_field, | ||
} | ||
|
||
|
||
def process_arg(value, sizes): | ||
handler = type_handlers.get(type(value), handle_default) | ||
return handler(value, sizes) if handler == handle_common_field else handler(value) | ||
|
||
|
||
@dataclasses.dataclass | ||
class CachedProgram: | ||
"""Class to handle caching and compilation of GT4Py programs. | ||
|
||
This class is responsible for caching and compiling GT4Py programs | ||
with optional domain information. The compiled program and its | ||
connectivity arguments are stored for efficient execution. | ||
|
||
Attributes: | ||
program (gtx.ffront.decorator.Program): The GT4Py program to be cached and compiled. | ||
with_domain (bool): Flag to indicate if the program should be compiled with domain information. Defaults to True. | ||
_compiled_program (Optional[Callable]): The compiled GT4Py program. | ||
_conn_args (Any): Connectivity arguments extracted from the offset provider. | ||
_compiled_args (tuple): Arguments used during the compilation of the program. | ||
|
||
Properties: | ||
compiled_program (Callable): Returns the compiled GT4Py program. | ||
conn_args (Any): Returns the connectivity arguments. | ||
|
||
Note: | ||
This functionality will be provided by GT4Py in the future. | ||
""" | ||
|
||
program: gtx.ffront.decorator.Program | ||
with_domain: bool = True | ||
_compiled_program: Optional[Callable] = None | ||
_conn_args: Any = None | ||
_compiled_args: tuple = dataclasses.field(default_factory=tuple) | ||
|
||
@property | ||
def compiled_program(self) -> Callable: | ||
return self._compiled_program | ||
|
||
@property | ||
def conn_args(self) -> Callable: | ||
return self._conn_args | ||
|
||
def compile_the_program( | ||
self, *args, offset_provider: dict[str, gtx.Dimension], **kwargs: Any | ||
) -> Callable: | ||
backend = self.program.backend | ||
program_call = backend.transforms_prog( | ||
workflow.InputWithArgs( | ||
data=self.program.definition_stage, | ||
args=args, | ||
kwargs=kwargs | {"offset_provider": offset_provider}, | ||
) | ||
) | ||
self._compiled_args = program_call.args | ||
return backend.executor.otf_workflow(program_call) | ||
|
||
def __call__(self, *args, offset_provider: dict[str, gtx.Dimension], **kwargs: Any) -> None: | ||
if not self.compiled_program: | ||
self._compiled_program = self.compile_the_program( | ||
*args, offset_provider=offset_provider, **kwargs | ||
) | ||
self._conn_args = extract_connectivity_args(offset_provider, device) | ||
|
||
kwargs_as_tuples = tuple(kwargs.values()) | ||
program_args = list(args) + list(kwargs_as_tuples) | ||
sizes = [] | ||
|
||
# Convert numpy integers in args to int and handle gtx.common.Field | ||
for i in range(len(program_args)): | ||
program_args[i] = process_arg(program_args[i], sizes) | ||
|
||
if not self.with_domain: | ||
program_args.extend(sizes) | ||
|
||
# todo(samkellerhals): if we merge gt4py PR we can also pass connectivity args here conn_args=self.conn_args | ||
return self.compiled_program(*program_args, offset_provider=offset_provider) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,3 +69,7 @@ def device(self): | |
} | ||
device = device_map[self.icon4py_backend] | ||
return device | ||
|
||
@cached_property | ||
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. Unfortunately ICON has a seperate namelist parameter for this, where I think that should be determined in the gridfile. In fact in the MCH grid file there is 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. If it is not provided by MPI-M then there is no consistent way of passing it into the wrapper from ICON across experiments (MCH, APE). Therefore I would rather keep this environment variable for now, it is very easy to set in the run script, and doesn't require further changes on the ICON side. 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. I don't really care about the ICON side, what I am always a bit worried is that we constrain the pure python greenline runs to do awkward things because of ICON or the ICON integration. And all this grid file related things should eventually come from the model configuration file (same as ICON) and not from the environment. (This is different for the backend, I think it makes sense to run the exact same experiment (without changes in config) on different backends. ) Anyhow, since we don't have a consolidated configuration yet in icon4Py we leave it as is and rework it in due time. That might mean then that we would maybe also need to change things on how the py2fgen or the wrapper works... 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. makes sense, I agree we postpone it until we think about how to configure icon4py. |
||
def limited_area(self): | ||
return os.environ.get("ICON4PY_LAM", False) |
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.
wouldn't it be more future proof if you had here the
ICON_GRID_PATH = "/project/d121/icon4py/ci/testdata/grids
and then extended the path in your test case? Then we could switch between grids more easily.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.
Extending the path will currently not always work in ICON since the grids are not all in one root folder. For it to work we'd have to move all grids into one folder. For now I propose to keep it as is, which is the user can define
ICON_GRID_LOC
which is the path to the root folder of the grid, and then can defineICON_GRID_NAME
which is the name of the grid (in each runscript), so already there is some flexibility here. Regarding the CI tests, I can improve that in my next PR which will be about py2fgen CI improvements.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 was only thinking about CI actually here... For me is fine to do this in a next PR.