Skip to content

Conversation

@gregomni
Copy link
Contributor

Some small extensions to the favored type implementation for arithmetic expressions to make the type checker decide on happy paths sooner, if those work out. This is motivated by a particularly slow example:

let result: Double = -(1 + 1) + -(1 + 1) - 1 

First, if there are no favored types found by the linked expr analyzer, use the contextual type.
Second, allow unary minus to pass its favored type down to its argument, as binary arithmetic ops do.
Third, if the assigned type of a literal is its favored type, treat that as good and don't add SK_NonDefaultLiteral to the score.

The result is being able to stop looking for additional solutions if the favored path works. For this example expression, before: Total number of scopes explored: 905225, after: Total number of scopes explored: 337057.

There is one test diagnosis that changes because of the scoring, and it changes to look like the other diagnoses for similar uses.

@gregomni gregomni requested review from hborla and xedin as code owners June 20, 2024 03:36
@gregomni
Copy link
Contributor Author

@swift-ci Please smoke benchmark.

@xedin
Copy link
Contributor

xedin commented Jun 20, 2024

I will take a look but we are trying not to touch favoring and optimizations that are happening in CSGen because any changes to them tend to regress other cases…

@gregomni
Copy link
Contributor Author

That's cool, I understand and thanks for taking a look!

I think this is significant for arithmetic with non-Int literals, but it's not a lot of code or time investment on my part. Feel free to aim me at things that are more likely to be accepted, like with #67791.

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test compiler performance.

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

@gregomni
Copy link
Contributor Author

Missed line number changes on (unrelated) fixits caused by adding a line to the test file.

@swift-ci Please smoke test.

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test compiler performance.

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

@gregomni
Copy link
Contributor Author

Huh. Failed but no problems reported.

@swift-ci Please smoke test.

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

@gregomni gregomni marked this pull request as draft June 30, 2024 22:16
@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

@gregomni
Copy link
Contributor Author

gregomni commented Jul 1, 2024

@swift-ci Please smoke test.

@gregomni
Copy link
Contributor Author

gregomni commented Jul 1, 2024

@swift-ci Please smoke test.

@gregomni
Copy link
Contributor Author

gregomni commented Jul 1, 2024

@swift-ci Please smoke test compiler performance

@gregomni
Copy link
Contributor Author

gregomni commented Jul 1, 2024

The performance test failed (if I'm reading the output right) because it failed to git checkout "Kingfisher". Looking at the build artifacts, the projects that did complete have no differences old vs new for the relevant things like Sema.NumConstraintScopes (which makes sense because anyone who is experienced with Swift avoids the sort of expressions with multiple literals that this change effects).

The example expression, on the other hand -Xfrontend -print-stats:
OLD:

 637089 Constraint solver largest system - # of disjunction terms explored 81360 Constraint solver largest system - # of disjunctions explored 936230 Constraint solver largest system - # of constraints simplified 905225 Constraint solver largest system - # of solution states explored 227637 Constraint solver largest system - # of type variable bindings attempted 202139 Constraint solver largest system - # of type variables bound 1055129 Constraint solver largest system - # of constraints not simplified 

NEW:

 212007 Constraint solver largest system - # of disjunction terms explored 27932 Constraint solver largest system - # of disjunctions explored 349748 Constraint solver largest system - # of constraints simplified 337057 Constraint solver largest system - # of solution states explored 108327 Constraint solver largest system - # of type variable bindings attempted 97669 Constraint solver largest system - # of type variables bound 460286 Constraint solver largest system - # of constraints not simplified 

-Xfrontend -debug-time-expression-type-checking (compiler built with -R --debug-swift so Sema not Release):
OLD: 114287.30ms
NEW: 43049.30ms

@gregomni gregomni marked this pull request as ready for review July 1, 2024 21:34
Copy link
Contributor

@xedin xedin left a comment

Choose a reason for hiding this comment

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

Sorry but I don't think we'll take these changes because favoring is not the right mechanism for these sorts of issues and we are instead looking into ways to transfer all CSGen performance "hacks" into the solver and make them incremental.

Favoring is too big of a hammer because it effectively prunes other choices and would prevent heterogeneous operator overloads from being attempted which is a big problem for custom operator overloads. Attempting to favor based on a contextual type means that the solver is only allowed to attempt overloads that match the result type exactly which is too restrictive since contextual type is a hit and we allow conversions to them which has a potential to regress existing code that relies on the current behavior.

@gregomni
Copy link
Contributor Author

gregomni commented Jul 3, 2024

would prevent heterogeneous operator overloads from being attempted

That is not what is happening here. There is zero change to the binary operator type variable tying together bits which were in the code before. (If you examined this PR originally it could do that, but that was accidental, and is no longer the case. Sorry about that!)

This does favor the contextual type, but favoring only makes that type be tried first in overload sets. The impactful change is that if a literal is determined to be the same type as the favored type, it doesn't get the SK_NonDefaultLiteral score. This makes it so myFloat + 1 (1 is a Float, not an Int) gets a zero score. Along with contextual favoring it makes let _: Double = 1 (1 is a Double, not an Int) also get a zero score. It is incremental in the solver, because it only changes scoring in the solver.

This is so that when a solution is found, we can stop. The current behavior of combinatorial explosion of literal types attempted is happening because the types which actually work aren't the default literal type.

@xedin
Copy link
Contributor

xedin commented Jul 3, 2024

This is so that when a solution is found, we can stop.

This is the main problem with favoring and what I referring to with heterogenous operators as well, we don’t want to always stop checking when the type matches the contextual exactly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants