-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Compilation is very slow on some files due to implicit search repeatedly diverging #14333
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
Comments
It looks like the compiler is spending a ton of time doing a bunch of implicit searches which all diverge: ...
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForIorT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForWriterT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForReaderWriterStateT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForIorT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForWriterT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForReaderWriterStateT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForWriterT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForReaderWriterStateT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForEitherT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForOptionT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForOptionT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForKleisli),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForEitherT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForStateT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForOptionT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForKleisli),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForEitherT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForStateT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForIorT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForWriterT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForReaderWriterStateT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForWriterT),1,0)
diverged: Candidate(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class effect)),object kernel),object Sync),method syncForReaderWriterStateT),1,0)
... continues for thousands lines I have no idea what these implicits do but they look like: implicit def syncForOptionT[F[_]](implicit F0: Sync[F]): Sync[OptionT[F, *]] = So I guess whenever the compiler tries one, to see if it can be applied, it ends up doing a recursive implicit search where all these implicits are again possible candidates, and so we get an exponential blow-up. I think we'll need someone to produce a minimization to figure out if this is a bug in scala 3 or an intentional difference in implicit resolution. See #13838 for a similar case, and in particular in #13838 (comment) @joroKr21 suggested there might be situations where we do implicit search that cannot possibly succeed, I don't know if this is the same issue. |
Noteworthy: |
In some situations we only call `constrainResult` for its side-effects and ignore its result, but `constrainResult` calls `necessarilyCompatible` which will call `viewExists` as a last-try. Since `viewExists` doesn't have side-effects we might as well skip it, and it turns out that using `NoViewsAllowed.constrainResult` does exactly that. This leads to a significant speed-up (from ~8s to sub-second with a hot compiler) with the test case from scala#14333 (most likely this is because at the point where we call `constrainResult` we haven't accumulated any constraint from the arguments of the application, so implicit search is free to go on a wild goose chase). I also removed obsolete comments in this method.
In some situations we only call `constrainResult` for its side-effects and ignore its result, but `constrainResult` calls `necessarilyCompatible` which will call `viewExists` as a last-try. Since `viewExists` doesn't have side-effects we might as well skip it, and it turns out that using `NoViewsAllowed.constrainResult` does exactly that. This leads to a significant speed-up (from ~8s to sub-second with a hot compiler) with the test case from scala#14333 (most likely this is because at the point where we call `constrainResult` we haven't accumulated any constraint from the arguments of the application, so implicit search is free to go on a wild goose chase). I also removed obsolete comments in this method.
In some situations we only call `constrainResult` for its side-effects and ignore its result, but `constrainResult` calls `necessarilyCompatible` which will call `viewExists` as a last-try. Since `viewExists` doesn't have side-effects we might as well skip it, and it turns out that using `NoViewsAllowed.constrainResult` does exactly that. This leads to a significant speed-up (from ~8s to sub-second with a hot compiler) with the test case from scala#14333 (most likely this is because at the point where we call `constrainResult` we haven't accumulated any constraint from the arguments of the application, so implicit search is free to go on a wild goose chase). I also removed obsolete comments in this method.
In some situations we only call `constrainResult` for its side-effects and ignore its result, but `constrainResult` calls `necessarilyCompatible` which will call `viewExists` as a last-try. Since `viewExists` doesn't have side-effects we might as well skip it, and it turns out that using `NoViewsAllowed.constrainResult` does exactly that. This leads to a significant speed-up (from ~8s to sub-second with a hot compiler) with the test case from scala#14333 (most likely this is because at the point where we call `constrainResult` we haven't accumulated any constraint from the arguments of the application, so implicit search is free to go on a wild goose chase). I also removed obsolete comments in this method.
In some situations we only call `constrainResult` for its side-effects and ignore its result, but `constrainResult` calls `necessarilyCompatible` which will call `viewExists` as a last-try. Since `viewExists` doesn't have side-effects we might as well skip it, and it turns out that using `NoViewsAllowed.constrainResult` does exactly that. This leads to a significant speed-up (from ~8s to sub-second with a hot compiler) with the test case from scala#14333 (most likely this is because at the point where we call `constrainResult` we haven't accumulated any constraint from the arguments of the application, so implicit search is free to go on a wild goose chase). I also removed obsolete comments in this method. Fixes scala#14333.
Your fix works :) tried with a |
I consider performance bugs as bugs. But I guess you could argue there are performance enhancements that aren't bugs? Also, I saw that the templates marks crashes as bugs, which seemed redundant to me. Surely all crashes are bugs?! |
Yeah our categorization system isn't great. |
In some situations we only call `constrainResult` for its side-effects and ignore its result, but `constrainResult` calls `necessarilyCompatible` which will call `viewExists` as a last-try. Since `viewExists` doesn't have side-effects we might as well skip it, and it turns out that using `NoViewsAllowed.constrainResult` does exactly that. This leads to a significant speed-up (from ~8s to sub-second with a hot compiler) with the test case from scala#14333 (most likely this is because at the point where we call `constrainResult` we haven't accumulated any constraint from the arguments of the application, so implicit search is free to go on a wild goose chase). I also removed obsolete comments in this method. Fixes scala#14333.
In some situations we only call `constrainResult` for its side-effects and ignore its result, but `constrainResult` calls `necessarilyCompatible` which will call `viewExists` as a last-try. Since `viewExists` doesn't have side-effects we might as well skip it, and it turns out that using `NoViewsAllowed.constrainResult` does exactly that. This leads to a significant speed-up (from ~8s to sub-second with a hot compiler) with the test case from scala#14333 (most likely this is because at the point where we call `constrainResult` we haven't accumulated any constraint from the arguments of the application, so implicit search is free to go on a wild goose chase). I also removed obsolete comments in this method. Fixes scala#14333.
Compiler version
3.1.0 / 3.1.1
Minimized code
Expectation
Sub-second recompilation when there are no changes in the file except whitespace.
Reality
It takes 7-8 seconds to compile this file on a fresh JVM. Recompilation also takes nearly as much, and the presentation compiler is affected as well (which is visible when you e.g. attempt to get completions in Metals).
Notes
I think the
*>
call is the culprit here. Removing it fixes the issue.Scala 2.13.8 compiles this in 2 seconds with sub-second incremental recompilations.
The text was updated successfully, but these errors were encountered: