-
Notifications
You must be signed in to change notification settings - Fork 248
compiler: Address various compiler hotspots with operators containing large expression counts #2624
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
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2624 +/- ##
==========================================
- Coverage 92.01% 92.00% -0.01%
==========================================
Files 245 245
Lines 48710 48727 +17
Branches 4292 4294 +2
==========================================
+ Hits 44819 44830 +11
- Misses 3205 3209 +4
- Partials 686 688 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
9b01400 to
8377718
Compare
devito/ir/support/basic.py
Outdated
| return self.timestamp < other.timestamp | ||
|
|
||
| # Note: memoization yields mild compiler speedup | ||
| @memoized_meth |
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.
Where did this bad boy come from, and can we not use the built in @cached/@lru_cache? I believe these are thread safe
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.
Looks like memoized_meth caches on the instance, whereas @cache I believe would have some unexpected behavior for instance methods (possibly would store an extra reference?)
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, iirc you need to be careful using @cache and @lru_cache on methods, as you can leak memory if you just use them as-is
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.
Okay, I will update this comment to say that this cache is not thread safe
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.
memoization yields mild compiler speedup.
how mild? if it's just 1~2% we may as well avoid it
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.
Honestly probably something around that? It was just enough to be consistently visible above the noise across multiple runs
devito/symbolics/queries.py
Outdated
| return expr.is_CTemp | ||
| except AttributeError: | ||
| return 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.
Does this really need to be a stand alone function?
getattr(expr, "is_CTemp", 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.
Keeps consistency with the other search methods which improves readability, so I would say yes
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.
Could have just done isinstance like for ComponentAccess rather than the is_CTemp that's used only here since it's not "standard" sympy is_xxx
devito/symbolics/search.py
Outdated
|
|
||
| def retrieve_ctemps(exprs, mode='all'): | ||
| """Shorthand to retrieve the CTemps in `exprs`""" | ||
| return search(exprs, q_ctemp, mode, 'dfs') |
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 this is the only place the function is used, it can be a lambda
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.
It can, but imo it's better to keep consistency with the other search shortcuts
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 agree about consistency.
However, since ctemps are a CSE-local type, I'd move it inside cse.py
mloubout
left a comment
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.
Minor comments, thanks for digging.
devito/ir/support/basic.py
Outdated
| # To avoid evaluating expensive symbolic Lt or Gt operations, | ||
| # we pre-empt such operations by checking if the values to be compared | ||
| # to are symbolic, and skip this case if not. | ||
| if not any(isinstance(i, sympy.core.Basic) |
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.
just sympy.Basic
devito/passes/clusters/cse.py
Outdated
| mapper.update({e.lhs: e.rhs for e in candidates | ||
| if sum([i.rhs.count(e.lhs) for i in exprs]) == 1}) | ||
| # Find all the CTemps in expression right-hand-sides without removing duplicates | ||
| ctemps = retrieve_ctemps([e.rhs for e in exprs]) |
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.
(e.rhs for e in expr) don't need a list here just generator
devito/passes/clusters/cse.py
Outdated
| if sum([i.rhs.count(e.lhs) for i in exprs]) == 1}) | ||
| # Find all the CTemps in expression right-hand-sides without removing duplicates | ||
| ctemps = retrieve_ctemps([e.rhs for e in exprs]) | ||
| ctemp_count = Counter(ctemps) |
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.
move in if ctemps
devito/symbolics/queries.py
Outdated
| return expr.is_CTemp | ||
| except AttributeError: | ||
| return 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.
Could have just done isinstance like for ComponentAccess rather than the is_CTemp that's used only here since it's not "standard" sympy is_xxx
devito/passes/clusters/misc.py
Outdated
| # (intuitively, "the loop nests are to be kept separated") | ||
| # * All ClusterGroups between `cg0` and `cg1` must precede `cg1` | ||
| # * All ClusterGroups after `cg1` cannot precede `cg1` | ||
|
|
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.
why a blank line?
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.
Leftover
devito/ir/support/basic.py
Outdated
| # To avoid evaluating expensive symbolic Lt or Gt operations, | ||
| # we pre-empt such operations by checking if the values to be compared | ||
| # to are symbolic, and skip this case if not. | ||
| if not any(isinstance(i, sympy.Basic) |
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.
do you mean if all(is_integer(...)) ?
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.
Are the two statements equivalent here?
Certainly the current implementation is more congruent with the comment and my original intention
devito/ir/support/basic.py
Outdated
| for i in (sit.symbolic_min, sit.symbolic_max)): | ||
|
|
||
| for v in (self[n], other[n]): | ||
| try: |
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 don't think you need the try-except anymore now that you have the if on the outside?
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.
Symbolic inequalities cannot always be converted to bool, so the try-except is still needed since v can still be symbolic I think
devito/ir/support/basic.py
Outdated
| return Vector(S.ImaginaryUnit) | ||
| except TypeError: | ||
| pass | ||
| # Note: To avoid evaluating expensive symbolic Lt or Gt operations, |
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 comment is now obsolete and should be dropped
smart_lt's docstring should already contain all this info, no?
FabioLuporini
left a comment
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.
GTG
Now ready for reviews.