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

Generalize Targets as Operators #1028

Merged
merged 16 commits into from Jan 16, 2020
Merged

Generalize Targets as Operators #1028

merged 16 commits into from Jan 16, 2020

Conversation

FabioLuporini
Copy link
Contributor

This PR builds on top of #1026 and #1027 . It basically generalises the Target concept introduced in these two preliminary PRs.

Again, no new functionality is added. This PR is a mere refactoring aimed at simplifying one's life when it gets to specialising compilation passes for a given <platform, mode, language>

Comment on lines 1 to 3
from .profiling import profiler_registry # noqa
from .registry import operator_registry # noqa
from .operator import Operator # noqa
Copy link
Contributor

Choose a reason for hiding this comment

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

not sure if intended, but these seem to be in a (almost)reversed alphabetical order

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 Author

Choose a reason for hiding this comment

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

ahhhh. OK, fixing

devito/operator/operator.py Show resolved Hide resolved

* `platform` is an object of type Platform, that is the architecture
the generated code should be specializer for.
* `mode` is the optimization level (e.g., `advanced`)
Copy link
Contributor

Choose a reason for hiding this comment

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

fullstop?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah. Fixing

@@ -906,4 +911,8 @@ def parse_kwargs(**kwargs):
except:
raise InvalidOperator("Illegal `dse=%s`" % str(dse))

# Attach `platform` too for convenience, basically so that we don't always have
# to query `configuration
Copy link
Contributor

Choose a reason for hiding this comment

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

missing a closing `

maybe rewrite it. it's a bit confusing..
what about:

Attach `platform` too for convenience, so we don't have to always query for `configuration`

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@FabioLuporini FabioLuporini force-pushed the refactor-as-operators branch 2 times, most recently from 1d2b9cd to fec5d95 Compare January 8, 2020 09:37
@codecov
Copy link

codecov bot commented Jan 8, 2020

Codecov Report

Merging #1028 into master will decrease coverage by 0.04%.
The diff coverage is 92.09%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1028      +/-   ##
==========================================
- Coverage   90.66%   90.61%   -0.05%     
==========================================
  Files         166      164       -2     
  Lines       23127    23161      +34     
  Branches     3325     3326       +1     
==========================================
+ Hits        20968    20988      +20     
- Misses       1753     1765      +12     
- Partials      406      408       +2
Impacted Files Coverage Δ
devito/ir/support/properties.py 100% <ø> (ø) ⬆️
devito/core/operator.py 94.11% <100%> (ø) ⬆️
devito/passes/iet/mpi.py 97.75% <100%> (ø)
devito/core/__init__.py 100% <100%> (ø) ⬆️
devito/passes/iet/engine.py 96% <100%> (ø)
tests/test_autotuner.py 100% <100%> (ø) ⬆️
devito/ops/__init__.py 100% <100%> (ø) ⬆️
devito/passes/__init__.py 100% <100%> (ø)
devito/passes/iet/__init__.py 100% <100%> (ø)
tests/test_operator.py 97.91% <100%> (ø) ⬆️
... and 22 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 46cf0ef...e33e6a3. Read the comment docs.

Copy link
Contributor

@georgebisbas georgebisbas left a comment

Choose a reason for hiding this comment

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

Only did a first pass.

# Lower Clusters to a Schedule tree
stree = st_build(clusters)
# Turn Clusters into a Schedule tree
stree = cls._lower_stree(clusters, **kwargs)
Copy link
Contributor

Choose a reason for hiding this comment

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

I am a bit confused here. Lower Clusters is changed to Turn into...
However the new name of the function is lower_stree ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah it's not very clear is it? I will revert to "lower". Good catch

# Lower IET to a Target-specific IET
op, target_state = cls._specialize_iet(op, **kwargs)
# Lower Schedule tree to an Iteration/Expression tree
iet, byproduct = cls._lower_iet(stree, profiler, **kwargs)
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn;it better to have something like lower_to_iet
Seems like the difference between lower sth to lower_to_sth ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

maybe yes?

it's deliberately ambiguous I'd say. While it's true that a ScheduleTree is lowered TO an IET, it is also true that the lowered IET goes through several other sub-passes (which are increasingly lowering the IET itself). So in a certain sense we're also lowering the IET too

Makes sense?

Copy link
Contributor

Choose a reason for hiding this comment

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

What you say makes sense.
However:
WHat I see is a y=f(x) where x=stree and y=iet and f=lower_iet
So it may make absolute sense, however I would like to eliminate a bit of the ambiguity.
I think adding a line or two of docstrings with some more info along what you wrote would be better.

where:

* `platform` is an object of type Platform, that is the architecture
the generated code should be specializer for.
Copy link
Contributor

Choose a reason for hiding this comment

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

specialized?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes. Fixing.


# Force-call `openmp` if requested via global option
if 'openmp' not in passes and options['openmp']:
passes_mapper['openmp'](graph)
Copy link
Contributor

Choose a reason for hiding this comment

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

For mpi sure, but this one is by default to using it so maybe shouldn't if not asked for in the custom options?

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 I'm just replicating master's behavior ?

we only end up here when dle != noop/advanced , eg

Operator(..., dle=('blocking', 'simd', X))

all the code is doing is that if 'openmp' doesn't appear among X above, then it depends on the value of DEVITO_OPENMP (which is encoded in options['openmp'])


# Make it an actual Operator
op = Callable.__new__(cls, **op.args)
op = Callable.__new__(cls, **iet.args)
Copy link
Contributor

Choose a reason for hiding this comment

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

Still not sure why init not called in new.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

cls is an Operator, so it will indeed call Operator.__init__ that is a no-op however. Then, I have to explicitly call Callable's __init__ (line below)


* Group expressions into Clusters;
* Optimize Clusters for performance (flops, data locality, ...);
* Introduce guards for conditional Clusters
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be just "Specialize for ..." then these two in _specialize_clusters?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch. Only the latter though. Will adjust

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah, no: it is gonna be like that I think as of #1030 , but not in this PR yet. With this PR, both optimization and guarding occur through clusterize, hence the docstring is correct. Will have to update it in #1030 though.


* Turn a sequence of Clusters into a ScheduleTree;
* Derive and attach metadata for distributed-memory parallelism;
* Derive sections for performance profiling
Copy link
Contributor

Choose a reason for hiding this comment

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

Both Drive on specialize?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, they are compulsory in lower.

_specialize_stree is currently a no-op

* Perform analysis to detect optimization opportunities;
* Introduce distributed-memory, shared-memory, and SIMD parallelism;
* Introduce optimizations for data locality;
* Finalization (e.g., symbol definitions, array casts)
Copy link
Contributor

Choose a reason for hiding this comment

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

Finalize

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, fixing

Copy link
Contributor Author

@FabioLuporini FabioLuporini left a comment

Choose a reason for hiding this comment

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

Addressed first round of comments


# Force-call `openmp` if requested via global option
if 'openmp' not in passes and options['openmp']:
passes_mapper['openmp'](graph)
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 I'm just replicating master's behavior ?

we only end up here when dle != noop/advanced , eg

Operator(..., dle=('blocking', 'simd', X))

all the code is doing is that if 'openmp' doesn't appear among X above, then it depends on the value of DEVITO_OPENMP (which is encoded in options['openmp'])

# Lower Clusters to a Schedule tree
stree = st_build(clusters)
# Turn Clusters into a Schedule tree
stree = cls._lower_stree(clusters, **kwargs)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah it's not very clear is it? I will revert to "lower". Good catch

# Lower IET to a Target-specific IET
op, target_state = cls._specialize_iet(op, **kwargs)
# Lower Schedule tree to an Iteration/Expression tree
iet, byproduct = cls._lower_iet(stree, profiler, **kwargs)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

maybe yes?

it's deliberately ambiguous I'd say. While it's true that a ScheduleTree is lowered TO an IET, it is also true that the lowered IET goes through several other sub-passes (which are increasingly lowering the IET itself). So in a certain sense we're also lowering the IET too

Makes sense?


# Make it an actual Operator
op = Callable.__new__(cls, **op.args)
op = Callable.__new__(cls, **iet.args)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

cls is an Operator, so it will indeed call Operator.__init__ that is a no-op however. Then, I have to explicitly call Callable's __init__ (line below)


* Group expressions into Clusters;
* Optimize Clusters for performance (flops, data locality, ...);
* Introduce guards for conditional Clusters
Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch. Only the latter though. Will adjust


* Group expressions into Clusters;
* Optimize Clusters for performance (flops, data locality, ...);
* Introduce guards for conditional Clusters
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah, no: it is gonna be like that I think as of #1030 , but not in this PR yet. With this PR, both optimization and guarding occur through clusterize, hence the docstring is correct. Will have to update it in #1030 though.


* Turn a sequence of Clusters into a ScheduleTree;
* Derive and attach metadata for distributed-memory parallelism;
* Derive sections for performance profiling
Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, they are compulsory in lower.

_specialize_stree is currently a no-op

* Perform analysis to detect optimization opportunities;
* Introduce distributed-memory, shared-memory, and SIMD parallelism;
* Introduce optimizations for data locality;
* Finalization (e.g., symbol definitions, array casts)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, fixing

where:

* `platform` is an object of type Platform, that is the architecture
the generated code should be specializer for.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes. Fixing.

Copy link
Contributor

@georgebisbas georgebisbas left a comment

Choose a reason for hiding this comment

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

Maybe some of my comments seem nitpicking but all I is do driven from what I feel I need to understand ;-)

# Lower IET to a Target-specific IET
op, target_state = cls._specialize_iet(op, **kwargs)
# Lower Schedule tree to an Iteration/Expression tree
iet, byproduct = cls._lower_iet(stree, profiler, **kwargs)
Copy link
Contributor

Choose a reason for hiding this comment

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

What you say makes sense.
However:
WHat I see is a y=f(x) where x=stree and y=iet and f=lower_iet
So it may make absolute sense, however I would like to eliminate a bit of the ambiguity.
I think adding a line or two of docstrings with some more info along what you wrote would be better.

Callable.__init__(op, **op.args)

# Header files, etc.
op._headers = list(cls._default_headers)
op._headers.extend(target_state.headers)
op._headers.extend(byproduct.headers)
Copy link
Contributor

Choose a reason for hiding this comment

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

My VScode pylint says
Instance of 'Callable' has no '_headers' member on this line?
SHould we care?

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe some info about byproduct? Like a docstring?

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe what I say its driven form the fact that byproduct is a bit vague.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My VScode pylint

it's buggy. op is of type Operator

Maybe some info about byproduct? Like a docstring?

I could..... but it feels like overkill. byproduct is simply the by-product of lower-iet which is a bag of additional objects and properties

* `platform` is an object of type Platform, that is the architecture
the generated code should be specialized for.
* `mode` is the optimization level (e.g., `advanced`).
* `language` is the generated code language (default is C+OpenMP+MPI,
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing parenthesis closing

@FabioLuporini FabioLuporini merged commit 14b475c into master Jan 16, 2020
@FabioLuporini FabioLuporini deleted the refactor-as-operators branch January 16, 2020 17:25
@FabioLuporini
Copy link
Contributor Author

approved -> merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants