-
-
Notifications
You must be signed in to change notification settings - Fork 389
hls-graph speculatively builds all prior dependencies for a key even if they might have changed #3423
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
@pepeiborra thoughts? |
It doesn't build the previous dependencies, only recomputes them. This can be a no-op if the dependencies are not dirty. Either way, this behaviour is by design, to implement early cutoff. That's my understanding at least. And as far as I remember, Shake does the same thing.
Yup, I can see how the side effects and the spurious diagnostics can be annoying. Maybe GetModIfaceFromDiskAndIndex needs to check whether the file is a FOI. Not sure what you mean about preconditions?
The build system makes no promises around evaluation order, and for good reason since we want maximal parallelism. The only promise is that if rule B depends on rule A the build system guarantees that A will build before B.
So you want to give up early cutoff? I suspect the performance impact will be massive. |
What is the distinction between 'build' and 'compute' here? It seems like all the code in the
We could add an ordering for dependencies so that dependencies using applicative style or
I'm not sure why this means we have to give up early cutoff. After recomputing previous dependencies, since the rule has finished execution, we should have the result and be able to do early cutoff using that? |
Computing a build target involves checking for dirtiness, computing its dependencies, and then running the rule giving it a
This is not a property of the build rule, but an invariant that we want to maintain. It seems difficult to encode this invariant in the build system though |
So the gist of your proposal is to not evaluate all the dependencies in parallel. Instead, evaluate them "only as much as needed", and stop as early as possible, i.e. short-circuit. Is that right? I am unconvinced that this will prevent running It will also make the build system less performant in subtle ways, specially when combined with |
Yes. I also noticed in profiles that hls-graph spawns essentially a new thread for each build target, which seems excessive and probably not very good for performance (see our old friend https://gitlab.haskell.org/ghc/ghc/issues/18224). Of course the exact improvement depends on how many threads are blocked on MVars or IO, versus have actual work that can be performed.
At least in the case I mentioned in the ticket body, the second time |
On a project with 3000 modules, it looks like hls-graph spawns and hold on to 31000 TSOs for the duration of the entire build. |
This is by design,
Build target threads block on blackholes, IIRC, see
Sounds about right. Rebuilds after edits should spawn much fewer TSOs though, have you measured that? |
This makes sense to me, but I still have concerns about the performance impact. Your suggestion of grouping dependencies and evaluating the group in parallel would go a long way here. So I agree that this would be a good change. |
In summary. Cons: Some thoughts:
B: |
I am doing a proof of concept draft in #4087 (comment) |
I ran into this bug while debugging a flaky test case -
A.hs
is initially not anFOI
, soGetModIface A.hs
has two dependenciesIsFileOfInterest A.hs
andGetModIfaceFromDiskAndIndex A.hs
. ThenA.hs
is opened, turning it into anFOI
haskell-language-server/ghcide/src/Development/IDE/Core/Rules.hs
Lines 934 to 957 in dfd8f0b
At this point
A.hs
should have dependenciesIsFileOfInterest A.hs
,Typecheck A.hs
... (theIsFOI
branch). However, I was seeingGetModIfaceFromDiskAndIndex
still getting run forA.hs
.Investigating further, it turns out to be the implementation of
refresh
inhls-graph
:haskell-language-server/hls-graph/src/Development/IDE/Graph/Internal/Database.hs
Lines 145 to 148 in dfd8f0b
It seems like it speculatively builds all the previous dependencies of the key even if they might have changed. This is wrong as rules can have side effects or require preconditions (
GetModIfaceFromDiskAndIndex
has both). This also can lead to spurious diagnostics from these pseudo-dependencies.We should instead build dependencies in the order that they occurred (which we currently do not maintain), and recompute with
RunDependenciesChanged
if any of them are dirty, short circuiting the evaluation of later dependencies (since they might not be true dependencies). However, this might have a negative impact on performance.In the above case, that would mean skipping
GetModIfaceFromDiskAndIndex
after we discover thatIsFOI
has changed.The text was updated successfully, but these errors were encountered: