-
Notifications
You must be signed in to change notification settings - Fork 13
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
Pipeline-plan duplicate/remove transformation changing dependencies #462
base: main
Are you sure you want to change the base?
Conversation
Documentation for this branch can be viewed at https://sites.ecmwf.int/docs/loki/462/index.html |
a9ad546
to
afb5750
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #462 +/- ##
==========================================
- Coverage 93.29% 93.27% -0.03%
==========================================
Files 221 223 +2
Lines 41360 41674 +314
==========================================
+ Hits 38587 38871 +284
- Misses 2773 2803 +30
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
@mlange05 and @reuterbal I would wait for some short feedback before I add more tests to improve code coverage |
afb5750
to
3415e92
Compare
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.
Many thanks for this, this is a very useful demonstration of this functionality!
Conceptually, there's a few gotchas and things that I would prefer to do differently, and I'll try to explain here:
-
Item creation should always go through the item_factory, and the introduced
clone
method shortcuts this. Instead, we should have aget_or_create_from_item
method or similar on the ItemFactory, to make sure any new items automatically land in the cache. -
You inject the dependency changes incurred from plan_data in the
dependencies
property. However, that property returns (and I would like to keep it that way) IR nodes that constitute the dependency, e.g.,CallStatement
,Import
, etc. So, we have two options: (a) inject (and remove) relevant IR nodes during planning to fake these dependencies or (b) inject the plan dependencies at a point where we are already working with items. I think (b) is a little easier and I have explored doing this viacreate_dependency_items
instead, which seems to work (more details below). -
The need for a recursive clone can be inferred from not specifying an IR to the clone method. For contained subroutines in ProgramUnit we are already doing this, and I would prefer to do the same for source files instead of introducing a new rebuild-method on scoped nodes.
-
We need to separate plan and trafo in the tests - both need to be able to apply the relevant sgraph topology changes independently, because we run either plan or trafo mode in practice. Getting both to work, i.e., trafo after plan, might be rather tricky but I also don't see a need for that at the moment.
Admittedly, half the observations above I only figured out when starting to play with this directly and trying to apply the relevant changes. So, I've decided not to bother you with too many inline comments that I kept editing and changing and I will instead file a PR on top of yours against your PR branch. I hope this doesn't come across as dismissive, but I've underestimated how fiddly this gets...
@@ -137,8 +139,21 @@ def __init__(self, name, source, config=None): | |||
self.name = name | |||
self.source = source | |||
self.trafo_data = {} | |||
self.plan_data = {} |
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.
Please add a description to the docstring above
if 'source' not in kwargs: | ||
kwargs['source'] = self.source.clone() # self.source.clone() | ||
if self.config is not None and 'config' not in kwargs: | ||
kwargs['config'] = self.config |
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.
kwargs['config'] = self.config | |
kwargs['config'] = self.config.copy() |
Otherwise you will incur aliasing problems. This should also be verified in a test, i.e., clone an item, modify the config in one and ensure the other item's config is unchanged.
@@ -24,6 +25,7 @@ | |||
from loki.tools import as_tuple, flatten, CaseInsensitiveDict | |||
from loki.types import DerivedType | |||
|
|||
# pylint: disable=too-many-lines |
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 suggest moving the item factory into a separate file
super().__init__(config) | ||
|
||
def clone(self, **kwargs): |
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 my preference would be to not have a clone method on Items. The batch processing mechanics is diligent to never replicate items to avoid accidental copies, and this would make it easy to break this behaviour.
Instead, all item creation should go through the ItemFactory and therefore it would be better to have there a method that allows creating items from other items with a new name, forcing the appropriate renaming as required.
print(f"suffix: {self.suffix}") | ||
print(f"module_suffix: {self.module_suffix}") |
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.
print(f"suffix: {self.suffix}") | |
print(f"module_suffix: {self.module_suffix}") |
self.module_suffix = duplicate_module_suffix or duplicate_suffix | ||
print(f"suffix: {self.suffix}") | ||
print(f"module_suffix: {self.module_suffix}") | ||
self.kernels = tuple(kernel.lower() for kernel in as_tuple(kernels)) |
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 this the list of names that are to be duplicated? Can we find a more descriptive name for this?
@@ -959,6 +1014,53 @@ def __contains__(self, key): | |||
""" | |||
return key in self.item_cache | |||
|
|||
def clone_procedure_item(self, item, suffix='', module_suffix=''): |
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.
The suffix
and module_suffix
arguments are unnecessarily restrictive to the current use case and are not required if we instead have a generic clone_item
method in the ItemFactory. That gives us the requested new name of the cloned item, and thus also all this information because the naming pattern is
<module_name_or_empty>#<procedure_name>
The clone_procedure_item
should instead be generalized as suggested below
Ah, I forgot to delete the inline comments. Disregard them or at least don't action on them, yet :) |
That is perfectly fine for me and I wasn't expecting this PR to go in as it is but to serve as a first step/proof of concept. |
Proof-of-concept transformations
DuplicateKernel
andRemoveKernel
with both changing the dependencies for the scheduler.This builds on top of #453
DuplicateKernel
takes a kernel and creates a duplicated kernel with corresponding duplicated items and adds a call to this duplicated kernel.RemoveKernel
removes a kernel from the scheduler and also removes the call.