Skip to content

Add test suite with two basic tests for solver space leaks. #4122

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

Merged
merged 6 commits into from
Nov 22, 2016

Conversation

grayjay
Copy link
Collaborator

@grayjay grayjay commented Nov 20, 2016

The new test suite uses the solver DSL and is built with -with-rtsopts=-M4M. I think this is a better way to check for space leaks than using the cabal executable, because it isolates the solver and uses simpler packages. Here's an example where I introduced a space leak by removing the bang patterns on the Int parameter in

go :: [Var QPN] -> Int -> Progress Message a b -> Progress String a b
:

Solver DSL (There are three tests, because I used PR #4110.)
solver-dsl-tests-with-space-leak

cabal executable
cabal-with-space-leak

I also had to fix two space leaks to make the tests pass. One occurs when the solver always has exactly one choice for the next goal. The other is caused by a bug in GHC 7.6.3.

This is an edge case. If there is only ever one choice for the next goal, the
solver never evaluates the conflict count map. Additions to the map create a
growing thunk.
@mention-bot
Copy link

@grayjay, thanks for your PR! By analyzing the history of the files in this pull request, we identified @BardurArantsson, @fmthoma and @kosmikus to be potential reviewers.

Copy link
Collaborator

@fmthoma fmthoma left a comment

Choose a reason for hiding this comment

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

Have you considered also limiting the stack size? Some space leaks tend to only build up the stack, but not the heap, and these would not be discovered here. (Example: foldr (+) 0 [1..])

And just for curiosity: Why did you disable backjumping for your test cases? Would it make sense to include additional test cases where backjumping is enabled?

@@ -60,7 +60,7 @@ backjump (EnableBackjumping enableBj) var initial xs =
| otherwise = f (csAcc `CS.union` cs) cm'

logBackjump :: ConflictSet QPN -> ConflictMap -> ConflictSetLog a
logBackjump cs cm = failWith (Failure cs Backjump) (cs, updateCM initial cm)
logBackjump cs cm = cm `seq` failWith (Failure cs Backjump) (cs, updateCM initial cm)
Copy link
Collaborator

@fmthoma fmthoma Nov 20, 2016

Choose a reason for hiding this comment

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

I'd consider a bang pattern instead of seq for readability.

Also, in exploreLog below the cm is only updated when conflict counting is enabled, while it is always updated here. As you suggested in #3960, I think the if countConflicts… would make sense here as well. (Or, if you think that seqing cm is already sufficient, remove the if countConflicts in exploreLog as well, which would IMHO improve readability).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'd consider a bang pattern instead of seq for readability.

fixed

Also, in exploreLog below the cm is only updated when conflict counting is enabled, while it is always updated here. As you suggested in #3960, I think the if countConflicts… would make sense here as well. (Or, if you think that seqing cm is already sufficient, remove the if countConflicts in exploreLog as well, which would IMHO improve readability).

I just made a minimal change to avoid conflicts with your PR, but you're right: it's simpler to always update the map.

@grayjay
Copy link
Collaborator Author

grayjay commented Nov 20, 2016

@fmthoma Thanks for the review. I applied your suggestions in the last two commits.

Have you considered also limiting the stack size? Some space leaks tend to only build up the stack, but not the heap, and these would not be discovered here. (Example: foldr (+) 0 [1..])

Good idea. It seems to work really well, because the test framework catches the exception and continues running the other tests. Maybe we should also apply it to the other test suites.

And just for curiosity: Why did you disable backjumping for your test cases? Would it make sense to include additional test cases where backjumping is enabled?

I thought it would be better to test with backjumping, but I didn't know how to ensure that the solver doesn't find the result immediately. Then the tests wouldn't catch space leaks that only appear after a large area of the search tree has been explored. Turning off backjumping forces the solver to explore the whole tree.

We could probably do something like count the number of nodes that the solver has searched and assert that it is high enough. Then we would know when we needed to make the tests more difficult. I think that should go in another PR, though.

@fmthoma
Copy link
Collaborator

fmthoma commented Nov 21, 2016

Good idea. It seems to work really well, because the test framework catches the exception and continues running the other tests. Maybe we should also apply it to the other test suites.

The idea isn't mine, but yes, I think limiting the stack to a reasonable, but not overly small size for all tests is a good idea to detect space leaks early on. The only drawback is that when some test fails because of a newly introduced space leak, it may not be immediately obvious.

I think that should go in another PR, though.

Agreed :-)

@grayjay
Copy link
Collaborator Author

grayjay commented Nov 22, 2016

Thanks!

@grayjay grayjay merged commit 2146ea1 into haskell:master Nov 22, 2016
@grayjay grayjay deleted the memory-usage-tests branch November 22, 2016 04:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants