Skip to content
This repository was archived by the owner on Jun 14, 2020. It is now read-only.

Should use the same thread-pool #3

Open
alexandru opened this issue Dec 27, 2016 · 3 comments
Open

Should use the same thread-pool #3

alexandru opened this issue Dec 27, 2016 · 3 comments

Comments

@alexandru
Copy link
Contributor

While investigating the performance regression of Monix 2.1.x, I discovered that ForkJoinPool (which is backing Scala's own global execution context) has been migrated in the implementation of Scala 2.12 to java.util.concurrent.ForkJoinPool and that this implementation has lower throughput than the one in Scala 2.11.

The difference is quite significant and at this point I'm actually considering introducing my own implementation of ForkJoinPool in Monix, because unless I'm not horribly mistaken, this isn't a problem that the Scala developers can fix in the standard library, unless they bring back scala.concurrent.forkjoin.ForkJoinPool. Anyway, I've started this issue on my own end:
monix/monix#281 (comment)

That said, when benchmarking, it's important to not measure the wrong thing. At least the tests that are forking threads should be backed by the same thread-pool implementation, otherwise you end up measuring the performance of the underlying thread-pools. For example I don't know what FS2 does, but Scalaz definitely doesn't use by default a ForkJoinPool for its Task.fork or Task.apply.

@djspiewak
Copy link
Contributor

djspiewak commented Dec 27, 2016

As far as I know, none of the benchmarks are using any parallelism in scalaz-stream or fs2. Monix may be running things in parallel outside of our control (I honestly don't know), but the scalaz-stream and fs2 benchmarks are sequential and do not use any thread pools, fork/join or otherwise.

@alexandru
Copy link
Contributor Author

alexandru commented Dec 28, 2016

@djspiewak it's not about parallelism, but about executing things asynchronously.

And FS2 has a specified Strategy that's basically about usage of a thread-pool, taken as a parameter in Task.apply. You couldn't execute your AsyncT without blocking threads otherwise.

Can't figure out what the default for the JVM is, but it seems that in your tests the used strategy is:

val cores: Int = Runtime.getRuntime().availableProcessors
lazy val scaledStrategy: Strategy = Strategy.fromFixedDaemonPool(cores)

Which is then backed by a Java Executors.newFixedThreadPool, returning a fixed size java.util.concurrent.ThreadPoolExecutor.

@djspiewak
Copy link
Contributor

djspiewak commented Dec 28, 2016

@alexandru I think you're operating under a misunderstanding of how fs2's execution model works. The only use of a thread pool with fs2 in any capacity whatsoever in this project is here. Note that flatMap doesn't take an executor, and so there isn't any thread shifting beyond the initial apply.

Monix may make more aggressive use of thread shifting, and given its default-push architecture I would expect it to do so, but fs2 does not. All of the fs2 stream tests in this repository are sequential and do not involve thread pools in any fashion. Thus, the results are not dependent on executor performance. This is part of why I give the consistent disclaimer that benchmarks comparing Monix to fs2 is really "apples to oranges": not only are they solving fundamentally different problems, but they're doing it with a wholly distinct set of tradeoffs.

@rossabaker rossabaker mentioned this issue Jan 3, 2017
# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants