-
Notifications
You must be signed in to change notification settings - Fork 520
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
Severe memory consumption when using *> operator #401
Comments
Try using I'd also remove the |
@mpilquist It's worth noting that I can confirm this exact same issue with, what appears to be, the same root cause. Also worth noting that fs2 itself uses To be clear, it appears that use of |
👍 The existing fs2 uses should all be safe but if you see any suspect cases please let me know. OTOH, I wouldn't be against a policy of "always favor |
@mpilquist From what I can tell, there are no safe uses, unfortunately. I can add/remove a |
in fs2 I'd just go with "use >>" , and I'm wondering if it should be an FAQ in cats/cats-effect as well |
Oh interesting, so that's different than the non-tail monadic recursion issue. You're saying that Is there a potential fix in cats-effect where we somehow null out references to |
@mpilquist I haven't been able to track it down to that degree of precision, but that does appear to be the issue, approximately. Here are some heap traces (pointers go "up", from subsequent lines to previous ones):
Altogether, there are 184 instances of The relevant thing to see here is it's all the same instance of |
Also all the code involved is open-source, so I can just push a branch and reproduction instructions if you want, but it's anything but minimal. The system as a whole is extremely complex, and I haven't been able to get it to happen on anything smaller. |
Herein some insides:
Method chain: def productR[A, B](fa: F[A])(fb: F[B]): F[B] =
map2(fa, fb)((_, b) => b)
def map2[A, B, Z](fa: F[A], fb: F[B])(f: (A, B) => Z): F[Z] =
map(product(fa, fb))(f.tupled)
def product[A, B](fa: F[A], fb: F[B]): F[(A, B)] =
flatMap(fa)(a => map(fb)(b => (a, b)))
|
Note the following program does not leak memory: @ def loop: IO[Unit] = IO.unit >> loop
defined function loop
@ (IO.unit *> loop).unsafeRunSync |
* Workaround for memory issue of `*>`. See typelevel/cats-effect#401 * Monad for Applicative and FlatMap
Is this still a thing? I haven't seen this in a while, but then I've also been careful to use |
I'm new to cats-effect and decided to develop a simple app which runs an infinite loop that receives events and performs some side effects. During testing I noticed that performance decreased drastically after 5 millions messages were processed. I started investigation and figured out that the problem is related to usage of *> operator.
From official documentation
the *> operator is defined in Cats and you can treat it as an alias for lh.flatMap(_ => rh)
I created to methods: one uses flatMap to chain effects and another
*>
Result:
counter = 10 000 000 total time 33597 ms
Memory consumption is fine, no GS spikes, see visualVM screenshot
Ok, now the same method but flat map has replaced with
*>
Result:
counter = 10 000 000 total time 62128 ms
So if
fa *> fb
is just a syntax sugar forfa.flatMap( _ => fb)
I was expecting to get the same performance but the variant with*>
is 2 times slower.After
13-14m
cycles the application has frozen almost completely.The first thing I noticed was a constantly growing heap and periodic GC spikes, see the screenshot
I created heap dump and analyzed it using MAT and here what I found:
histogram
25m Map object of a total Retained Heap = 2GB
Then using
Immediate Dominator
feature I found this:dominator tree
99% of total Heap is occupied by
cats.effect.internals.ArrayStack
Is it a problem with
*>
or I'm using it wrong ?I was looking into the source code but I failed to find a reason of such memory consumption.
Could someone assist please ?
The text was updated successfully, but these errors were encountered: