https://kotlinlang.org logo
Title
e

ermac10k

05/06/2021, 11:59 AM
Hello there! I made some measures and coroutines loose to parallelStream when they deal with parallel processing of collection elements. It’s sad 😔 Kotlin v1.3.72
3
g

gildor

05/06/2021, 1:30 PM
It really depends on what kind operation you are doing, parallelStream is perfect for CPU intensive non-async operations, it’s directly optimized for it using fork-join pool Coroutines (also depends what kind coroutines) is to control async operations and will not will optimized parallelStream if you just do a bunch of parallel computations which anyway block thread But in general would interesting to see what you particularly your are doing and how you measure
e

ermac10k

05/06/2021, 1:39 PM
https://pl.kotl.in/0Twe1idr4 if you’re intersted
g

gildor

05/06/2021, 1:51 PM
You have very primitive implementetation of coroutines, it’s not different from just launching a bunch of threads and join them it’s not really a case (just a heavy computation on a bunch of threads) where you would expect coroutines to win parallelStream Also there are so many things there which are just not equal, for example ForkJoinPool used for parallelStreams is not the same as your straight forward dispatcher with Runtime.getRuntime().availableProcessors()
And I’m not even talking about way how you benchmark it
but I still would say, parallelPool is perfect for this kind operation (run many parallel blocking CPU intensitive operations as efficient as possible)
e

ermac10k

05/06/2021, 1:52 PM
please, show some code to make this implementation of coroutines not so primitive
i used all that i had found useful in this situation
it’s a bit hard to benchmark spring+kotlin. i continue to find a way to do that
g

gildor

05/06/2021, 2:00 PM
as I said, it depends on your use case
if you want to parallel blocking CPU intensive operation just use fork join
but if you really have async operations, than parallelStream will not work, it’s purely blocking abstraction, this is the case for coroutines
coroutines do not add any value for your example, just a useless abstraction to run a few threads on a fixed thread pool
You have very primitive implementetation of coroutines
It’s really bad explanation, not implementation of coroutines, but usage of them just as a wrapper on top of thread, this would be worse even without coroutines if you would compare it with parallelStream() which has own speciailized thread pool with job stealing mechanism
e

ermac10k

05/06/2021, 2:08 PM
is it possible to use forkjoinpool as coroutines pool? i’ve found only fixed, single and global ones? google keeps silence. the fixed one showed the best perfomance… global one is a lot worse
g

gildor

05/06/2021, 2:10 PM
global? you mean GlobalScope?
e

ermac10k

05/06/2021, 2:11 PM
i mean the way that i don’t specified a pool at all
g

gildor

05/06/2021, 2:11 PM
Well, short answer yes, you can use ForkJoinPool.commonPool().asCoroutineDispatcher()
i mean the way that i don’t specified a pool at all
Correct, because you run by default on DefaultDispatcher, which you shouldn’t really block and it just doesn’t do the same what parallelStream is doing
e

ermac10k

05/06/2021, 2:12 PM
woohoo! interesting
g

gildor

05/06/2021, 2:13 PM
Problem of your example not only in dispatcher, it’s just looks pointless to use coroutines for what you doing in your example
e

ermac10k

05/06/2021, 2:15 PM
yep, i see. i hoped that it would have been more coroutines than threads in par stream. but theese tasks are more cpu bounded than i have expected(
g

gildor

05/06/2021, 2:15 PM
yep
it’s fine to use coroutines for this, just because it’s very convinient, even for CPU bound tasks, but it makes sense as part of bigger asyncronous code, not just to launch a few threads
e

ermac10k

05/06/2021, 2:17 PM
convinient? in our project we do not use them at all because there is no place for them 😭
i’ve tried and lost
g

gildor

05/06/2021, 2:17 PM
yes, extremely convinient if you have a bunch of asynchronous code %)
if all your code is blocking, it would be an improvement in terms of code, just because asyncronous code of coroutines look as blocking code, which allows to write much more simple non-blocking code
e

ermac10k

05/06/2021, 2:20 PM
sounds like it’s a lot more pythonic async/await than golang goroutines
g

gildor

05/06/2021, 2:20 PM
it can do both
and more natural than async/await
e

ermac10k

05/06/2021, 2:22 PM
thx! continue trying to master them
e

eHelg

05/06/2021, 5:08 PM
Not the prettiest proof-of-concept, and somewhat contrived. But using the following code I get a difference of about 200ms tops either way. Which I would say for most use-cases is negligible compared to things such as http-calls/db-access etc. https://pl.kotl.in/rRsZs0ETN
g

gildor

05/06/2021, 11:37 PM
Thread pool average duration: 192432 Coroutine average duration 147564 Coroutine difference -44868 ns -44 ms
See, sometimes coroutines are even faster But I would like to point out, that you cannot rely on such code as benchmark, you need correctly configured benchmark harness which will handle all usual pitfalls of JVM benchmarks
e

ermac10k

05/07/2021, 11:44 AM
Thread pool average duration: 91329 Coroutine average duration 181858 Coroutine difference 90529 ns 90 ms
if somebody helps me to set up true benchmark for kotlin+plus spring… i cannot start spring app from jmh(((
g

gildor

05/07/2021, 12:28 PM
Why do you need spring in this example at all?
e

ermac10k

05/07/2021, 12:33 PM
because i’m working with it. and i wanna benchmark a small piece of an internal process. a hibernate query is important to be included in benchmark among others.
g

gildor

05/07/2021, 12:41 PM
But why? You do not measure hybernate
Also I still think this comparison is pointless
If you want to parallel database requests invest into usage of asynchronous drivers for database, it will really help with performance (at least in some workloads) and it works perfectly with coroutines
Also I believe Spring provides support of reactive DB api out of the box and even support coroutines and Flow
e

ermac10k

05/07/2021, 12:44 PM
yes, i do
b.findAllByaName(it.id)
it’s a hiber query
g

gildor

05/07/2021, 12:45 PM
If you want to measure performance of database compare it with async api, not with blocking
I just want to point out that Micro benchmarking is useless if you want to measure such complex system as database, you should profile it on application level, with close to real life task, to cover all possible bottlenecks
e

ermac10k

05/07/2021, 12:48 PM
hmpf… i benchmarks coroutines against parallesStreams. but the impact of db query is important.
g

gildor

05/07/2021, 12:48 PM
Measuring different implementation one based on pure blocking hybernate and another on something like spring R2DBC
¯\_(ツ)_/¯
e

ermac10k

05/07/2021, 12:49 PM
what?
it’s hyber every time
that’s the point
g

gildor

05/07/2021, 12:50 PM
It just looks for me that you measuring not what you actually should measure with implementation which shouldn't be used for this case
I just want to say, that you shouldn't expect some kind improvement in this case, coroutines will not make your thread pool run faster, just add unnecessary abstraction on top of it
e

ermac10k

05/07/2021, 12:52 PM
yep! this point i caught yesterday 😉