-
Notifications
You must be signed in to change notification settings - Fork 704
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
Slow solving with build-tool-depends
on local packages with tests
#7472
Comments
Quick check for "does this affect me":
|
A few more notes for the interested:
|
For newer versions of cabal which don't run the solver when calling |
Here's the result of my investigation thus far. The
Those preferences say try with bench enabled before bench disabled. In this case we should set the preferences to also have Since this section also sets up constraints, and tests are definitely included in the constraints, all the data should be there to include them in preferences as well. If this is done the search tree will be reordered to try packages first with test enabled (the success path!) and only then disabled. Not quite sure how to tweak the code to do this though, if anyone else wants to take a run at it first :-) |
Anyone affected by this issue is welcome to try out the linked PR an see if it improves things. (Assuming it gets a good review, I think it should probably be merged even if there's no noticeable speedup since it seems slightly more correct, and at worst will remove some "line noise" from solver output by eliminating some unnecessary local backjumps. |
I ran the reproduction, but I'm not sure it reproduces the performance issue, even though it contains the log message about incompatible stanzas. I didn't see any backtracking with cabal 3.4.0.0. Full solver log:
The log shows that cabal is trying to use the same instance of A for both the top-level build target and C's build tool dependency (level 13), which is an optimization. Using the same instance of A means that the two uses must make the same choice for enabling tests. The solver first tries disabling tests for the build tool dependency at level 14 but immediately encounters the conflict and enables tests instead. This is how I would expect the solver to behave, even with more packages in the chain of dependencies. I think I would need to see an example of how increasing the chain of dependencies causes backtracking, especially if it causes the solver to make the same choice multiple times. I also think that cabal should add the same preferences for all local packages as in #7472 (comment), for consistency. |
I've attached the actual constraint-solver log from the project in question. It's quite long, but perhaps illuminating. |
In particular it has:
|
(Sorry that the reproduction wasn't obviously slow, I was trying to just show the test/non-test conflict issue, which seemed more self-contained!) |
Thanks for the logs! I graphed the level in the search tree vs line number and saw that there were four main instances of backtracking: All four have similar conflicts involving build tools and enabling testing, so I focused on the first one, which has these relevant lines:
This conflict differs from the simple example and leads to backtracking because of a difference in goal order. The solver chose the version of the build tool dependency plutus-tx (plutus-pab:cardano-node:exe.plutus-tx) before the version of the top level build target plutus-tx. Only the top level build target is constrained to enable testing, so the solver initially chose to disable tests for plutus-pab:cardano-node:exe.plutus-tx. Then when it chose the version for the top level build target, it tried linking plutus-tx to plutus-pab:cardano-node:exe.plutus-tx, which requires both to make the same choice for enabling testing. The solver was unable to resolve the conflict by unlinking plutus-tx, because cabal doesn't currently support building the same version of a package twice. It was also unable to choose an alternative version for plutus-tx, because it is a local package. Those conflicts meant that the solver had to backtrack many levels to enable testing in plutus-pab:cardano-node:exe.plutus-tx. I think that the solution is to remove the restriction on building the same version of a package twice. Then the solver could have unlinked the two goals and avoided backtracking. If I understand correctly, cabal could still build each component of the package at most once (avoiding increasing build time) while fulfilling the requirement to build both with and without tests. There is already an issue for removing the restriction to only build each version of a package once: #6459 Avoiding backtracking would only decrease the number of steps by about 60%, so there may be other performance issues related to solving for build tools on the happy path. |
@grayjay thanks for the detailed analysis! note that my patch (#7490) would add a preference to build-tool dependency to enable testing as well, which tends to bypass this path that you pointed out -- and I think in fact that's what accounts for its improved performance. Looking at the cabal project now, I note some other places besides tests enabled that would induce the same backjumping. In particular, there are ghc options and flags also set at the top level alone:
Inspecting the logs, I noticed the cryptonite flag in particular led to some significant backjumping. The current patch only pushes stanza prefs through. Do you think it would make sense to have a patch to push through options and flags explicitly as well? |
In particular I'm proposing that at
and the following definition we change |
@gbaz I think that how we treat ghc options and flags should depend on the meaning of a package section. I'm not sure whether the section is supposed to apply to only the top level use of the package or all uses. I had thought that there was an issue about this distinction, but the closest thing I can find now is issues related to specifying local packages vs all packages, which are listed in #3720. I think that whichever behavior we choose should be made consistent across all options that can be specified in a package section. Do you know how ghc options are currently handled? One issue with making constraints in a package section apply to all uses of the package is that it might seem inconsistent with the way that constraints are applied with the --constraint flag. |
Merged #7490 but still not done. Empirical testing shows that the change I suggest above -- which applies flag constraints at all scopes -- yields significant further speedup. However it is not a correct change, as there may be times when constraints need not be uniform (when the dependency is solely on the binary). Further, while we can add constraints such as One thing we should do, eventually, is enable that scope qualified syntax for flag constraints as well as version constraints. However, that's pretty invasive. I think a lightweight change would be to apply qualified version and flag constraints both as preferences at other scopes -- this is a uniform improvement to the solver with no changes to the semantics of the solution space. It just means we try harder, earlier, to keep things uniform. I'll work on a PR towards this goal. |
Ok, with that last breakthrough, now we're done! |
Finally got the chance to try this out, and it looks like the patches perform as advertised! Thanks again! 🙇 |
Describe the bug
Suppose I have a local package
B
which provides a build tool, and which either has tests or depends on a packageA
that has tests. Finally, I have another local packageC
which depends onB
's build tool.Finally, I have
tests: true
in mycabal.project
, since I naturally want to test my local packages.The result of this is that the solver becomes slower, because (I hypothesize) it tries to create a build plan for
A
without tests to satisfy the build-tool dependency, then finds that it can't do that (due to the project constraint) and has to redo it.That looks something like this (from
cabal configure -v3
):This is not a big deal in a small project. However, if we have a long chain of packages up to the build tool dependency like so:
all of which with tests, then the solver will redo most of the work over and over until it finally realises it has to build all of
A
toZ
with tests. This can cause a very large slowdown in solving time.cabal freeze
does not help, since it only pins the versions and the test/no-test incompatibility still bites.To Reproduce
I made a repository that exhibits the behaviour I described in the small: https://github.com/michaelpj/cabal-build-tool-depends-solver-repro
Changing
tests:true
totests:false
incabal.project
stops the solver from failing and having to backtrack.Expected behavior
I don't understand how cabal's solver works at all, but I would like this to not be slow! Maybe the test/no-tests constraint needs to be pushed down eagerly?
System information
The text was updated successfully, but these errors were encountered: