-
Notifications
You must be signed in to change notification settings - Fork 15
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
Introduce 'PragmaModelTransformation' and preliminary OpenMP offload #485
base: nams-scc-seq-revector
Are you sure you want to change the base?
Conversation
Documentation for this branch can be viewed at https://sites.ecmwf.int/docs/loki/485/index.html |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## nams-scc-seq-revector #485 +/- ##
=========================================================
- Coverage 96.13% 96.12% -0.02%
=========================================================
Files 224 226 +2
Lines 40582 40991 +409
=========================================================
+ Hits 39015 39404 +389
- Misses 1567 1587 +20
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
OpenMP offload incomplete ...
|
643f400
to
84ce65b
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, this is a really cool development!
Conceptually this is fantastic and I don't have anything to change there. But a few other things could be improved, which I've mostly marked with inline comments.
A few more general remarks here:
I tried to do this during the review but it would be good if you could also double-check where the switch to loki directives has implications on the transformation beyond updating the keyword and naming of directives. For example:
- The SCCAnnotate transformation shouldn't care about the directive type anymore, and anything related to this should be possible to remove
- In a few places there used to be a direct translation from
!$loki ...
to!$acc ...
- which seems sometimes redundant now. I've tried to flag this but it's likely I overlooked this in other situtaitons.
I know that even after this PR the Loki directives are not set in stone and we can update them as required. But it's likely a good idea to sanity check them on this occasion. I've already flagged some things in inline comments, but particularly for structured directives that consist of !$loki command
and !$loki end command
pairs, I don't see any reason why the end statement should include any additional arguments, as it currently does for end scoped-data
.
Other than that, the usual nagging about some small things, docs and tests ;-)
pragma_parameters = PragmaParameters() | ||
parameters = defaultdict(list) | ||
if pragma.keyword.lower() != 'loki': | ||
return None, None | ||
content = pragma.content or '' | ||
# Remove any line-continuation markers | ||
content = content.replace('&', '') | ||
starts_with = content.split(' ')[0] | ||
if starts_with == 'end': | ||
starts_with = f"{starts_with}-{content.split(' ')[1]}" | ||
if not starts_with: | ||
return None, None | ||
content = content[len(starts_with):] | ||
parameter = pragma_parameters.find(content) | ||
for key in parameter: | ||
parameters[key].append(parameter[key]) | ||
parameters = {k: v if len(v) > 1 else v[0] for k, v in parameters.items()} | ||
return starts_with, parameters |
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 largely replicates the other get_pragma_parameters
utility, except for the explicit awareness of the command
and the matching end
key. Can we maybe consolidate the two, or re-use common code?
(Also, the naming of the utility isn't quite right, I think)
An untested idea in this direction:
def get_pragma_command_and_parameters(pragma, only_loki_pragmas=True):
pragma_parameters = list(get_pragma_parameters(pragma, only_loki_pragmas=only_loki_pragmas))
if not pragma_parameters:
return None, None
if pragma_parameters[0] == 'end':
if len(pragma_parameters) < 2 or pragma_parameters[1][1] is not None:
debug('get_pragma_command_and_parameters: Failed to match end-command in pragma {pragma}')
return None, None
pragma_parameters = [
(f'{pragma_parameters[0][0]}-{pragma_parameters[1][0]}', None),
*pragma_parameters[2:]
]
return pragma_parameters[0][0], dict(pragma_parameters[1:])
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 while we're at it, can we please have a test for this utility?
@@ -759,6 +758,7 @@ def create_pool_allocator(self, routine, stack_size): | |||
stack_ptr = self._get_stack_ptr(routine) | |||
stack_end = self._get_stack_end(routine) | |||
|
|||
# TODO: generalise using generic Loki pragmas? |
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.
Yes!
""" | ||
Loki generic pragmas to OpenACC mapper. | ||
""" | ||
|
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.
If you put this here at class-member indentation level, then you shouldn't need to repeat this for every method:
# pylint: disable=unused-argument | |
pmapper_map = {'openacc': OpenACCPragmaMapper(), 'omp-gpu': OpenMPOffloadPragmaMapper()} | ||
if self.directive in pmapper_map: | ||
self.pmapper = pmapper_map[self.directive] | ||
else: | ||
if self.keep_loki_pragmas: | ||
self.pmapper = None | ||
else: | ||
self.pmapper = GenericPragmaMapper() |
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.
If you want to limit instantation only to the actual mapper that is going to be used, you could use a pattern like this:
pmapper_map = {'openacc': OpenACCPragmaMapper(), 'omp-gpu': OpenMPOffloadPragmaMapper()} | |
if self.directive in pmapper_map: | |
self.pmapper = pmapper_map[self.directive] | |
else: | |
if self.keep_loki_pragmas: | |
self.pmapper = None | |
else: | |
self.pmapper = GenericPragmaMapper() | |
pmapper_cls_map = { | |
'openacc': OpenACCPragmaMapper, | |
'omp-gpu': OpenMPOffloadPragmaMapper, | |
} | |
pmapper_cls = pmapper_map.get(self.directive, None if self.keep_loki_pragmas else GenericPragmaMapper) | |
self.pmapper = pmapper_cls() if pmapper_cls else None |
@@ -74,14 +74,14 @@ def annotate_vector_loops(self, routine): | |||
for pragma in as_tuple(loop.pragma): | |||
if is_loki_pragma(pragma, starts_with='loop vector reduction'): | |||
# Turn reduction pragmas into `!$acc` equivalent | |||
pragma._update(keyword='acc') | |||
pragma._update(keyword='loki') |
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 control flow of this loop doesn't make too much sense anymore, given that there's no longer a translation into acc directives. I think this could be simplified as follows:
if private_arrays:
for pragma in as_tuple(loop.pragma):
if 'reduction' not in (pragma_parameters := get_pragma_parameters(pragma, starts_with='loop vector')):
# Add private clause
pragma_parameters['private'] = ', '.join(
v.name
for v in pragma_parameters.get('private', []) + private_arrays
)
pragma_content = [f'{kw}({val})' if val else kw for kw, val in pragma_parameters]
pragma._update(content=f'loop vector {" ".join(pragma_content)}'.strip())
!$loki unscoped-data in(tmp1, tmp2) create(tmp3, tmp4) | ||
!$loki end unscoped-data out(tmp2, tmp3, tmp4) delete(tmp1) | ||
!$loki scoped-data in(tmp1) out(tmp2) inout(tmp3) create(tmp4) | ||
!$loki end scoped-data in(tmp1) out(tmp2) inout(tmp3) create(tmp4) |
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'm not 100% happy with the "scoped/unscoped" data nomenclature, because data still has a scope. Both OpenMP and OpenACC call the distinction between the two "structured/unstructured data directives". Maybe we should adopt that here?
|
||
item_filter = (ProcedureItem, ModuleItem) | ||
|
||
def __init__(self, directive=None, keep_loki_pragmas=True, process_module_items=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.
In what situation would I not want to process module items?
if column_locals: | ||
vnames = ', '.join(v.name for v in column_locals) | ||
pragma = ir.Pragma(keyword='acc', content=f'enter data create({vnames})') | ||
pragma_post = ir.Pragma(keyword='acc', content=f'exit data delete({vnames})') | ||
pragma = ir.Pragma(keyword='loki', content=f'unscoped-data create({vnames})') | ||
pragma_post = ir.Pragma(keyword='loki', content=f'unscoped-enddata delete({vnames})') |
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 entire section is not covered by tests. What situation is this for, what would be required to capture this?
(Cc @mlange05)
|
||
class OpenACCPragmaMapper(GenericPragmaMapper): | ||
""" | ||
Loki generic pragmas to OpenACC mapper. |
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.
Can we please add here the table with the directive translation between loki, openacc and openmp?
And a documentation of the constructor arguments, please.
def pmap_create(self, pragma, parameters, **kwargs): # pylint: disable=unused-argument | ||
if param_device := parameters.get('device'): | ||
return Pragma(keyword='acc', content=f'declare create({param_device})') | ||
return self.default_retval() |
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 default_retval case is never hit in tests, and I'm wondering if we shouldn't better raise an exception than returning something if we have a Loki directive that doesn't match the "spec".
Introduce 'PragmaModelTransformation' and preliminary OpenMP offload
With this all transformation insert "generic" loki pragmas which are then transformed/mapped as (one of the last steps) using the 'PragmaModelTransformation' to a specific Pragma model (e.g., OpenACC, OpenMP offload).