Skip to content
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

Throttle list size in ListWrapper #2648

Closed

Conversation

Alistair-Johnson
Copy link
Contributor

Running the tests locally, and in particular ApplicativeSuite, I managed to occasionally, but nonetheless repeatedly, experience the same test timeouts we see on travis. Additionally, I observed some tests taking much longer than average, always coupled with the test JVM memory jumping up from about 1.2G to the max of 3G.

A few println's later...the List[A]'s generated by Arbitrary in the custom ListWrapper are generally in the sub 100 element size, but I have seen them balloon to over 8 million in the slow tests; this would, I believe, explain the OOM's.

The real fix is to make sure scalacheck creates reasonable size lists for List[A]'s in the first place, but unfortunately, I could not quickly achieve this. So the current fix is suboptimal, but works.

So if you are happy with the investigation, I might suggest that the current "fix" is better than what we currently have - a new issue can be raised for a more elegant solution, that might also take into account other slow tests that might suffer from a similar issue.

@Alistair-Johnson
Copy link
Contributor Author

Also, perhaps resolves #2319 ?

@codecov-io
Copy link

codecov-io commented Nov 29, 2018

Codecov Report

Merging #2648 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #2648   +/-   ##
=======================================
  Coverage   95.12%   95.12%           
=======================================
  Files         363      363           
  Lines        6704     6704           
  Branches      305      289   -16     
=======================================
  Hits         6377     6377           
  Misses        327      327

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a19f353...b64c76b. Read the comment docs.

@kailuowang
Copy link
Contributor

hmmm, this might be a little surprising to users. Imagine they intentionally created a 10k item ListWrapper to test stack safety of some type class. I am curious how this solution compared to changing the Arbitrary instance of LIstWrapper? namely change line 142
from

implicit def listWrapperArbitrary[A: Arbitrary]: Arbitrary[ListWrapper[A]] =
    Arbitrary(arbitrary[List[A]].map(ListWrapper.apply))

to

implicit def listWrapperArbitrary[A: Arbitrary]: Arbitrary[ListWrapper[A]] =
    Arbitrary(arbitrary[List[A]].map(l => ListWrapper(l.take(100))

@Alistair-Johnson
Copy link
Contributor Author

It actually needs :

Arbitrary(arbitrary[List[A]].map((l: List[A]) => ListWrapper(l.take(100))))

But unfortunately, it does not work. Whilst re-testing, I actually got a new PB of 29 million list size...

Test code, for those interested, :

new Alternative[ListWrapper] {
      def pure[A](x: A): ListWrapper[A] = ListWrapper(M.pure(x))

      def ap[A, B](f: ListWrapper[A => B])(fa: ListWrapper[A]): ListWrapper[B] ={
        if (f.list.length > 1000 ||  fa.list.length >1000)
          println( f.list.length +" "+  fa.list.length )
        ListWrapper(M.ap(f.list)(fa.list))
      }
...

I'm totally with you that the current "fix" is not ideal and could give surprising results. The problem is, the current implemention does give surprising results, and does so regularly; it make my laptop sound like an air raid siren. "oooooOOOOOOoooooooOOOOOOOOoooooo"

@Alistair-Johnson
Copy link
Contributor Author

Just thinking out loud... I wonder if the issue has anything to do with the fact that we aren't directly using a ListWrapper[A], but in fact an Alternative[ListWrapper] ?

@kailuowang
Copy link
Contributor

It's interesting that the list size was not caused by Arbitrary so there must be other code ballooning that size to millions. If we throw an exception in ListWrapper constructor when the given list has more than a large size, we maybe able to find out where it happened?

@Alistair-Johnson
Copy link
Contributor Author

Yeah...this is worth nailing once and for all...I'll have a look later 👍

@ceedubs
Copy link
Contributor

ceedubs commented Jan 13, 2019

@Alistair-Johnson when you saw the ballooned sizes, was it for passing tests or was it when Scalacheck was trying to shrink a failing test case?

@DavidGregory084
Copy link
Member

DavidGregory084 commented Jan 15, 2019

FWIW I had a go at doing something similar with

  implicit def listWrapperArbitrary[A](implicit arbA: Arbitrary[A]): Arbitrary[ListWrapper[A]] =
    Arbitrary {
      for {
        size <- Gen.choose(0, 100000)
        list <- Gen.resize(size, Gen.listOf(arbA.arbitrary))
      } yield ListWrapper(list)
    }

CPU usage / GC activity was a little less spiky but still at the limit of the heap size and scala List cons cells still make up ~600MB of heap. Maybe I need to try Gen.listOfN as well as Gen.resize.

EDIT: Sadly that didn't seem to have the desired effect either :(

@DavidGregory084
Copy link
Member

I'm not convinced this is caused by ListWrapper, as it doesn't happen if you do catsJVM/test. Is it possible this is due to the Scala.js compiler somehow?

@kailuowang
Copy link
Contributor

@DavidGregory084 we suspect that the list is ballooned after the arbitrary generation. Did you try throw an exception in ListWrapper constructor when the given list has more than a very large size? This might tell us what is trying to create a huge listwrapper.

@kailuowang
Copy link
Contributor

FYI just encountered another one of these on build (2.13)

[info] - Applicative[ListWrapper].monoid.monoid.combine all *** FAILED ***
[info]   OutOfMemoryError was thrown during property evaluation.
[info]     Message: GC overhead limit exceeded
[info]     Occurred when passed generated values (
[info]       arg0 = Vector(ListWrapper(List(1074835304, 0, -777179633, 1455578008, -25683529, 155715011)), ListWrapper(List(-1840890490, -2147483648, 1007200674, -1, -1, -2147483648, -1, 0, -107186259)), ListWrapper(List(0, -911786825, 1123360752, 0, -821246721, 0, -2147483648, 1495617553, -1, 2036308788)), ListWrapper(List(-916165509, 1950063819, -1961633005, 1, 1856854552, -2147483648, -1130776)), ListWrapper(List(-1, 711237612, 1424350193)), ListWrapper(List(0, -58409028, 1, -1897919184, -2134889206)), ListWrapper(List(0, -2147483648, -72482326, -1, -1641350473, -1717342098, 1, -1)), ListWrapper(List(0, -2147483648, -1996303227, -459503821, 0, -2147483648, 2147483647, 1, -1321329796, -1)), ListWrapper(List(0, -1, 930579752, 770218430, 1, 1469285130, 0)))
[info]     )

@DavidGregory084
Copy link
Member

@kailuowang I can reproduce the same behaviour just by running sbt catsJS/test:fastOptJS: it maxes out heap usage and a memory sampler shows a similar amount of scala.collection.immutable.$colon$colon allocations.

I was looking at some heap dumps earlier and they are dominated by scala.js ClearableLinkers.

Is it possible to increase the heap size to the ~3.5-4gb we need for this to run? Alternatively, can we split the build up so that fewer tasks are running in parallel?

SBT will happily run four fastOptJS tasks in parallel which is very heavy in terms of memory usage. Perhaps we could use the tag system in SBT to cut down the number of fastOptJS tasks that can run at once.

@kailuowang
Copy link
Contributor

@DavidGregory084 the VM running our build, according to travis documentation, has 6G of memory, so we should be able to increase. I'd like to further break up the build task as well (as part of this proposal #2570), but that might be a heavier task.

@DavidGregory084
Copy link
Member

I'm not convinced this is caused by ListWrapper, as it doesn't happen if you do catsJVM/test. Is it possible this is due to the Scala.js compiler somehow?

I think now that I was totally wrong about this as I have indeed seen the same thing on JVM tests while investigating #2319

@larsrh
Copy link
Contributor

larsrh commented Apr 11, 2020

Is this still an issue? If yes, feel free to re-open.

@larsrh larsrh closed this Apr 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants