-
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
Optimize lub algorithm #20034
Optimize lub algorithm #20034
Conversation
Simplify had some elaborate condition that prevented hard union types to be recomputed with a lub. I am not sure why that was. In the concrete scenario of i10693.scala, we had an explicitly union result type `B | A` where `A` and `B` are type parameters. So that is a hard union type. Then `A` was instantiated by `Int | String` and `B` was instantiated by `String | Int`. Re-forming the lub of that union would have eliminated one pair, but since the union type was hard tyat was not done. On the other hand I see no reason why hard unions should not be re-lubbed. Hard unions are about preventing the widening of or types with a join. I don't see a connection with avoiding re-lubbing. Fixes scala#10693
Replace mergeIfSuper by a different algorithm that is more efficient. We drop or-summands in both arguments of a lub that are subsumed by the other. This avoids expesnive recusive calls to lub or expensive comparisons with union types on the right.
Based on #20027. Last commit is new. |
@@ -157,15 +157,8 @@ object TypeOps: | |||
tp.derivedAlias(simplify(tp.alias, theMap)) | |||
case AndType(l, r) if !ctx.mode.is(Mode.Type) => | |||
simplify(l, theMap) & simplify(r, theMap) |
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.
Should this be calling into TypeComparer.glb
It just seems odd to be altering the Or case, but not the And case.
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 glb are in fact the same. It's just for lub
that we need the additional isSoft
parameter.
case _ => | ||
NoType | ||
} | ||
private def dropIfSub(tp: Type, sup: Type, canConstrain: Boolean): Type = |
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 new operations are much clearer than the original mergeIf
s. 👍🏼
test performance please |
performance test scheduled: 96 job(s) in queue, 1 running. |
Performance test finished successfully: Visit https://dotty-bench.epfl.ch/20034/ to see the changes. Benchmarks is based on merging with main (43ed9fd) |
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.
Very nice simplification, LGTM.
case _ => | ||
NoType | ||
} | ||
private def dropIfSub(tp: Type, sup: Type, canConstrain: Boolean): Type = |
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.
Add a comment:
/** If some (|-operand of) `tp` is a subtype of `sup` replace it with `NoType`. */
Replace mergeIfSuper by a different algorithm that is more efficient.
We drop or-summands in both arguments of a lub that are subsumed by the other.
This avoids expensive recursive calls to lub or expensive comparisons
with union types on the right.
I tested the previous performance regression #19907 with the new algorithm, and without the changes in #19995 that avoid a slow lub. Where previously it took minutes it now compiles fast.
Specifically, we get for i19907_slow_1000_3.scala: 2.9s with the optimizations in #19995, 3.3s with just this PR. And for i19907_slow_1000_4.scala: 3.9s with the optimizations in #19995, 4.5s with just this PR. So the optimizations in #19995 are much less critical now since lubs are much faster. Still, it's probably worthwhile to leave them in in case there is a humongous program that stresses lubs even more.