-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Track whether or not let expressions failed to solve in solver #7982
Conversation
After mutating an expression, the solver needs to know two things: 1) Did the expression contain the variable we're solving for 2) Was the expression successfully "solved" for the variable. I.e. the variable only appears once in the leftmost position. We need to know this to know property 1 of any subexpressions (i.e. does the right child of the expression contain the variable). This drives what transformations we do in ways that are guaranteed to terminate and not take exponential time. We were tracking property 1 through lets but not property 2, and this meant we were doing unhelpful transformations in some cases. I found a case in the wild where this made a pipeline take > 1 hour to compile (I killed it after an hour). It may have been in an infinite transformation loop, or it might have just been exponential. Not sure.
@@ -44,18 +44,22 @@ class SolveExpression : public IRMutator { | |||
map<Expr, CacheEntry, ExprCompare>::iterator iter = cache.find(e); | |||
if (iter == cache.end()) { | |||
// Not in the cache, call the base class version. | |||
debug(4) << "Mutating " << e << " (" << uses_var << ")\n"; | |||
debug(4) << "Mutating " << e << " (" << uses_var << ", " << failed << ")\n"; | |||
bool old_uses_var = uses_var; |
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 sure which C++ had a terse way to express this pattern (that wasn't an RIAA class)...
Does this need new test(s)? |
It's a failure to scale gracefully with large inputs. For the correctness of it, the existing solver tests are fine. |
Looks like there are some real failures to investigate |
I assume this is still in-progress? |
Yes |
…ess_through_solver_lets
…//github.com/halide/Halide into abadams/track_failedness_through_solver_lets
The test failure was caused by use of an uninitialized value because I didn't check a return value. I added the must use result macro to reduce_expr_modulo to prevent this problem in future. |
…ess_through_solver_lets
…ess_through_solver_lets
Failure unrelated. Just needs an approval |
bool reduce_expr_modulo(const Expr &e, int64_t modulus, int64_t *remainder); | ||
bool reduce_expr_modulo(const Expr &e, int64_t modulus, int64_t *remainder, const Scope<ModulusRemainder> &scope); | ||
HALIDE_MUST_USE_RESULT bool reduce_expr_modulo(const Expr &e, int64_t modulus, int64_t *remainder); | ||
HALIDE_MUST_USE_RESULT bool reduce_expr_modulo(const Expr &e, int64_t modulus, int64_t *remainder, const Scope<ModulusRemainder> &scope); |
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.
"must use result" should really be the default in the language, with the current behavior opt-in :-/
* Track whether or not let expressions failed to solve in solver After mutating an expression, the solver needs to know two things: 1) Did the expression contain the variable we're solving for 2) Was the expression successfully "solved" for the variable. I.e. the variable only appears once in the leftmost position. We need to know this to know property 1 of any subexpressions (i.e. does the right child of the expression contain the variable). This drives what transformations we do in ways that are guaranteed to terminate and not take exponential time. We were tracking property 1 through lets but not property 2, and this meant we were doing unhelpful transformations in some cases. I found a case in the wild where this made a pipeline take > 1 hour to compile (I killed it after an hour). It may have been in an infinite transformation loop, or it might have just been exponential. Not sure. * Remove surplus comma * Fix use of uninitialized value that could cause bad transformation
…e#7982) * Track whether or not let expressions failed to solve in solver After mutating an expression, the solver needs to know two things: 1) Did the expression contain the variable we're solving for 2) Was the expression successfully "solved" for the variable. I.e. the variable only appears once in the leftmost position. We need to know this to know property 1 of any subexpressions (i.e. does the right child of the expression contain the variable). This drives what transformations we do in ways that are guaranteed to terminate and not take exponential time. We were tracking property 1 through lets but not property 2, and this meant we were doing unhelpful transformations in some cases. I found a case in the wild where this made a pipeline take > 1 hour to compile (I killed it after an hour). It may have been in an infinite transformation loop, or it might have just been exponential. Not sure. * Remove surplus comma * Fix use of uninitialized value that could cause bad transformation
After mutating an expression, the solver needs to know two things:
We were tracking property 1 through lets but not property 2, and this meant we were doing unhelpful transformations in some cases. I found a case in the wild where this made a pipeline take > 1 hour to compile (I killed it after an hour). It may have been in an infinite transformation loop, or it might have just been exponential. Not sure.
As a drive-by while trying to figure out why it was taking so long I also made remainder checking cheaper in the common case of division by a constant, by avoiding a call to can_prove.