-
Notifications
You must be signed in to change notification settings - Fork 842
Speed up Constraint Solver's overload resolution #1530
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
|
@gmpl, Thanks for signing the contribution license agreement so quickly! Actual humans will now validate the agreement and then evaluate the PR. |
|
Super, super interesting, especially since the adjustments are small Seeing these failures - not sure why - some changes in errors/warnings may be expected but there are other failures here too |
|
Thanks for your feedback @dsyme I had a quick look at those tests but was not able to follow the code, actually I have no clue what is being tested (and failing) there, the error messages doesn't tell that much. Since I have no time in the week I will have a deeper look in the coming weekend. |
|
@gmpl You need to look at the test output logs in order to determine what actually failed. For example, for the first test, From that log, we can see that the line that caused the test to fail had this error: Your changes result in a discrepancy between the inference algorithm's results and the generated signature file (notice that It's possible that the signature generation code simply needs to be updated to mirror whatever changes you've made here, but that's just a guess. BTW, hats off to you if you can solve this--this has been my biggest F# pain point for a good year or more now, as someone who relies heavily on FsControl (sorry, Don :)). |
|
@drvink Thanks a lot for the explanation. |
src/fsharp/ConstraintSolver.fs
Outdated
| (MustUnifyInsideUndo csenv ndeep newTrace) | ||
| (TypesMustSubsumeOrConvertInsideUndo csenv ndeep (WithTrace newTrace) m) | ||
| (ArgsEquivInsideUndo csenv Trace.New isConstraint) | ||
| (ArgsEquivInsideUndo csenv Trace.New cx.IsSome) |
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 use of Trace.New in the original code looks suspicious. It feels like it should certainly be newTrace
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.
Good point @dsyme, looks like a bug. Because if a new trace is used then FilterEachThenUndo will not undo those changes.
Now if this is a bug that has no consequences I wonder if that operation must be inside an undo at all.
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, actually the trace is not used at all (see the function ArgsEquivInsideUndo).
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.
OK, thanks, pease remove the argument as part of this PR
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.
No worries, I will.
|
Now the adjustments are not that small if you look at the number of lines changed but in fact the logic change is still small, I just introduced an additional argument to pass the member constraint solution and apply it later, when all the inference equations are already in place. |
|
Didn't you have a extra repro sample? Can you please add it as a test? |
src/fsharp/ConstraintSolver.fs
Outdated
| let sty2 = stripTyEqnsA csenv.g canShortcut ty2 | ||
|
|
||
| match cxsln with | ||
| | Some (traitInfo, traitSln) when traitInfo.Solution.IsNone -> TransactMemberConstraintSolution traitInfo trace traitSln |
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.
Is this a imperative call?
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, it is writing to a reference cell, see this comments from the original code.
|
@forki Do you mean as a test constrained by the compile time? If so, can you point me to similar tests already in the solution? |
gusty
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.
This test was failing because it expects an error message to show twice. That was a weird behavior which is related to the fact that the constraint solver was solving many times the same problem. Now this fix (or workaround) apart from speeding up, solves this duplication on error messages.
|
@dsyme Another alternative is rather than adding an additional parameter to many functions, add a property into the Solver State to store the constraint solution until we commit it. That change will be quite small in terms of lines of code and function signatures. What do you think? |
|
@forki Now I see you were referring to another issue, I was thinking in the other one but they were marked as duplicate. So yes I will have a look tonight and try to merge it. Please leave it open for today, thanks. |
|
@forki Do you know why all test failed after merging your test? |
|
Unable to find version '14.0.24516-Pre' of package 'VisualCppTools'. that's nuget hickup, completely unrelatd to the merge. @dotnet-bot test this please. |
|
I wonder if it will have much difference on type checking during IDE, I guess it needs to be back ported to FCS to test. |
|
AFAIK this PR speed ups the type checker specifically, so FCS should speed up even further as it does not do byte code generation. |
|
hopefully parallelism can speed it up to the point where people stop complaining about how slow the compiler is 😸 |
|
@cloudRoutine for IDE? Yes, I hope. As for building, it's already parallized by msbuild/VS, so CPU is about 100% load. |
|
@cloudRoutine That would be very interesting, unfortunately I can only work on weekends. |
|
@vasily-kirichenko I don't think this optimization changed the name resolution order, there were other important changes since F# 4.0 that most likely did that. Did you check with the previous version before this commit? Nice to hear that your project compilation speeds up a lot, do you use FsControl or a do you use a lot of "constrained overloadeds" there? I tried to compile FsControl, before it was taking 2' 30'' and now with this version it takes only 30 secs :) Anyway I think the FsControl.dll compilation is not the part that will benefit the most from this optimization. |
|
@gmpl @forki @vasily-kirichenko It's crucial that we determine where name resolution changed in F# 4.1 as soon as possible, and if possible revert it to previous behaviour. @gmpl @vasily-kirichenko If there's any chance you could do a binary chop on the commits for F# 4.1 to determine where name resolution changed that would be awesome. It might be that using the commit history for http://github.com/fsharp/fsharp may be easier. @forki Could you carefully check if your name resolution changes altered the order of name resolution results in cases like the above |
|
@vasily-kirichenko can you come up with a repro case? |
|
@vasily-kirichenko I tried to put that into a test: #1652 Looks like it resolves fine for me on master: |
|
Maybe it behaves differently if files compiled as part of a project? |
|
Does it work the same in F# 4 FSI? |
|
@forki It might need to have the two namespace fragments in one file Come to think of it, I think this may have been a deliberate bug fix, see this commit - I do recall fixing an inconsistency when combining namespace fragments from a single file. It might even have been that the effective combined declaration order flipped every time a "combine" happened. If so I suppose we would be OK with this being a breaking change, you could always workaround by qualifying the union cases by the type name. |
|
Tbh I think this case was always the same and I think the current behavior is correct. @vasily-kirichenko can you try to modify my sample so that it breaks for you? |
|
@forki I tried to reproduce it on a minimal project and failed. Also I benchmarked 4.0/4.1-before-optimization/4.1-master compilers by compiling FCS project and all of them showed identical results (~42 seconds). Now I'm not sure why my large solution is compiled more than 2x faster on master compiler. I switched between 4.0 and master ones several times and it's reliably reproduced. Can it be some mess with ngen or the like? |
|
@vasily-kirichenko if your solution doesn't make use of constrained overload resolution I don't see why it would speed up that much with this PR. But if it does, even in a relative small number it could have improved, since some cases were taking insane compile time. |

This speeds up to 20x overload resolution and solves #343 at least the repro there mentioned.
This is what I get running the repro many times with the current version of the compiler:
And here's what I get with this optimized version:
It also seems logic to me that when exploring each possible solution, the constraint should be assumed as solved. I can't imagine a reason why not to assume that.
In case there is interest I have more complex repros with very interesting results.