https://kotlinlang.org logo
Channels
100daysofcode
100daysofkotlin
100daysofkotlin-2021
advent-of-code
aem
ai
alexa
algeria
algolialibraries
amsterdam
android
android-architecture
android-databinding
android-studio
androidgithubprojects
androidthings
androidx
androidx-xprocessing
anime
anko
announcements
apollo-kotlin
appintro
arabic
argentina
arkenv
arksemdevteam
armenia
arrow
arrow-contributors
arrow-meta
ass
atlanta
atm17
atrium
austin
australia
austria
awesome-kotlin
ballast
bangladesh
barcelona
bayarea
bazel
beepiz-libraries
belgium
benchmarks
berlin
big-data
books
boston
brazil
brikk
budapest
build
build-tools
bulgaria
bydgoszcz
cambodia
canada
carrat
carrat-dev
carrat-feed
chicago
chile
china
chucker
cincinnati-user-group
cli
clikt
cloudfoundry
cn
cobalt
code-coverage
codeforces
codemash-precompiler
codereview
codingame
codingconventions
coimbatore
collaborations
colombia
colorado
communities
competitive-programming
competitivecoding
compiler
compose
compose-android
compose-desktop
compose-hiring
compose-ios
compose-mp
compose-ui-showcase
compose-wear
compose-web
confetti
connect-audit-events
corda
cork
coroutines
couchbase
coursera
croatia
cryptography
cscenter-course-2016
cucumber-bdd
cyprus
czech
dagger
data2viz
databinding
datascience
dckotlin
debugging
decompose
decouple
denmark
deprecated
detekt
detekt-hint
dev-core
dfw
docs-revamped
dokka
domain-driven-design
doodle
dsl
dublin
dutch
eap
eclipse
ecuador
edinburgh
education
effective-kotlin
effectivekotlin
emacs
embedded-kotlin
estatik
event21-community-content
events
exposed
failgood
fb-internal-demo
feed
firebase
flow
fluid-libraries
forkhandles
forum
fosdem
fp-in-kotlin
framework-elide
freenode
french
fritz2
fuchsia
functional
funktionale
gamedev
ge-kotlin
general-advice
georgia
geospatial
german-lang
getting-started
github-workflows-kt
glance
godot-kotlin
google-io
gradle
graphic
graphkool
graphql
graphql-kotlin
graviton-browser
greece
grpc
gsoc
gui
hackathons
hacktoberfest
hamburg
hamkrest
helios
helsinki
hexagon
hibernate
hikari-cp
hire-me
hiring
hiring-french
hongkong
hoplite
http4k
hungary
hyderabad
image-processing
india
indonesia
inkremental
intellij
intellij-plugins
intellij-tricks
internships
introduce-yourself
io
ios
iran
israel
istanbulcoders
italian
jackson-kotlin
jadx
japanese
jasync-sql
java-to-kotlin-refactoring
javadevelopers
javafx
javalin
javascript
jdbi
jhipster-kotlin
jobsworldwide
jpa
jshdq
juul-libraries
jvm-ir-backend-feedback
jxadapter
k2-early-adopters
kaal
kafka
kakao
kalasim
kapt
karachi
karg
karlsruhe
kash_shell
kaskade
kbuild
kdbc
kgen-doc-tools
kgraphql
kinta
klaxon
klock
kloudformation
kmdc
kmm-español
kmongo
knbt
knote
koalaql
koans
kobalt
kobweb
kodein
kodex
kohesive
koin
koin-dev
komapper
kondor-json
kong
kontent
kontributors
korau
korean
korge
korim
korio
korlibs
korte
kotest
kotest-contributors
kotless
kotlick
kotlin-asia
kotlin-beam
kotlin-by-example
kotlin-csv
kotlin-data-storage
kotlin-foundation
kotlin-fuel
kotlin-in-action
kotlin-inject
kotlin-latam
kotlin-logging
kotlin-multiplatform-contest
kotlin-mumbai
kotlin-native
kotlin-pakistan
kotlin-plugin
kotlin-pune
kotlin-roadmap
kotlin-samples
kotlin-sap
kotlin-serbia
kotlin-spark
kotlin-szeged
kotlin-website
kotlinacademy
kotlinbot
kotlinconf
kotlindl
kotlinforbeginners
kotlingforbeginners
kotlinlondon
kotlinmad
kotlinprogrammers
kotlinsu
kotlintest
kotlintest-devs
kotlintlv
kotlinultimatechallenge
kotlinx-datetime
kotlinx-files
kotlinx-html
kotrix
kotson
kovenant
kprompt
kraph
krawler
kroto-plus
ksp
ktcc
ktfmt
ktlint
ktor
ktp
kubed
kug-leads
kug-torino
kvision
kweb
lambdaworld_cadiz
lanark
language-evolution
language-proposals
latvia
leakcanary
leedskotlinusergroup
lets-have-fun
libgdx
libkgd
library-development
lincheck
linkeddata
lithuania
london
losangeles
lottie
love
lychee
macedonia
machinelearningbawas
madrid
malaysia
mathematics
meetkotlin
memes
meta
metro-detroit
mexico
miami
micronaut
minnesota
minutest
mirror
mockk
moko
moldova
monsterpuzzle
montreal
moonbean
morocco
motionlayout
mpapt
mu
multiplatform
mumbai
munich
mvikotlin
mvrx
myndocs-oauth2-server
naming
navigation-architecture-component
nepal
new-mexico
new-zealand
newname
nigeria
nodejs
norway
npm-publish
nyc
oceania
ohio-kotlin-users
oldenburg
oolong
opensource
orbit-mvi
osgi
otpisani
package-search
pakistan
panamá
pattern-matching
pbandk
pdx
peru
philippines
phoenix
pinoy
pocketgitclient
polish
popkorn
portugal
practical-functional-programming
proguard
prozis-android-backup
pyhsikal
python
python-contributors
quasar
random
re
react
reaktive
realm
realworldkotlin
reductor
reduks
redux
redux-kotlin
refactoring-to-kotlin
reflect
refreshversions
reports
result
rethink
revolver
rhein-main
rocksdb
romania
room
rpi-pico
rsocket
russian
russian_feed
russian-kotlinasfirst
rx
rxjava
san-diego
science
scotland
scrcast
scrimage
script
scripting
seattle
serialization
server
sg-user-group
singapore
skia-wasm-interop-temp
skrape-it
slovak
snake
sofl-user-group
southafrica
spacemacs
spain
spanish
speaking
spek
spin
splitties
spotify-mobius
spring
spring-security
squarelibraries
stackoverflow
stacks
stayhungrystayfoolish
stdlib
stlouis
strife-discord-lib
strikt
students
stuttgart
sudan
swagger-gradle-codegen
swarm
sweden
swing
swiss-user-group
switzerland
talking-kotlin
tallinn
tampa
teamcity
tegal
tempe
tensorflow
terminal
test
testing
testtestest
texas
tgbotapi
thailand
tornadofx
touchlab-tools
training
tricity-kotlin-user-group
trójmiasto
truth
tunisia
turkey
turkiye
twitter-feed
uae
udacityindia
uk
ukrainian
uniflow
unkonf
uruguay
utah
uuid
vancouver
vankotlin
vertx
videos
vienna
vietnam
vim
vkug
vuejs
web-mpp
webassembly
webrtc
wimix_sentry
wwdc
zircon
Powered by
Title
s

Stephan Schroeder

05/19/2020, 8:25 AM
Is
runBlocking
an expensive operation? The docs seem to suggest that it’s a good idea to use it once in the main-method and than stay in suspend-country for the rest of the codebase. But how about sprinkling in a bit of concurrent computation here and there with snippets like this:
val (a, b) = runBlocking(Dispatchers.Default) {
        val aDeferred = async {getA()}
        val aDeferred = async {getB()}
        return@runBlocking aDeferred.await() to bDeferred.await()
}
Doable or bad idea?
o

octylFractal

05/19/2020, 8:50 AM
the overhead is pretty small, but it does block the thread
it would probably be more efficient to put as much of the function inside the runBlocking as you can
s

Stephan Schroeder

05/19/2020, 9:41 AM
sounds like “doable” then 🙂
g

gildor

05/19/2020, 9:54 AM
I would also removed default dispatcher, it’s not really necessary there
k

Kroppeb

05/19/2020, 10:32 AM
It makes getA and getB run parallel though?
g

gildor

05/19/2020, 11:36 AM
depends on how a and b are implemented
apparently, if those a and b are proper suspend functions they will run in parallel
if they are blocking functions, one can run them in IO thread to make them parallel
async(IO) { getA() }
u

Ugi

05/19/2020, 5:40 PM
apparently, if those a and b are proper suspend functions they will run in parallel
I don't think
getA()
or
getB()
being suspend function affects them executing in parallel in this case. As far as I understand it, it will depend on the dispatcher.
g

gildor

05/20/2020, 2:17 AM
if function is proper suspend function, so it doesn’t block the thread, they will run in parallel, with any dispatcher (except some non-existing, which limits amount of coroutines in paralell)
o

octylFractal

05/20/2020, 2:19 AM
actually, if you were to drop the
Dispatchers.Default
, they would run concurrently but not in parallel, because there is only one thread for
runBlocking
g

gildor

05/20/2020, 2:20 AM
not necessary, it depends on getA() and getB implementation
just imagine you replace getA and getB with delay, they will run perfectly in parallel
also with any other, even blocking function, if it wrapped to own dispatcher it will run in parallel, which must be the case for all properly designed suspend functions
so Dispatchers.Default will not affect behaviour, until functions are blocking
o

octylFractal

05/20/2020, 2:24 AM
I think you have a misunderstanding of the term 'parallel' -- it means that code is executing at the same time, which
delay
does not represent, no code is executed while the delay is happening (i.e., no CPU usage). using
delay
represents a concurrent process. take for example Javascript, nothing is ever done in parallel with javascript, it is all one single-threaded event loop. that is what I mean by
runBlocking
(with no specified dispatcher) being concurrent but not parallel
g

gildor

05/20/2020, 2:24 AM
I know what is parallel
okay, maybe delay is not perfect example, just because nothing is actually happening in parallel, they just run concurrently. Now see:
suspend fun getA() = IO { File("A").readBytes() }
suspend fun getB() = IO { File("B").readBytes() }
o

octylFractal

05/20/2020, 2:27 AM
right, but that introduces a new dispatcher, I am specifically talking if you didn't add the I/O dispatcher
g

gildor

05/20/2020, 2:27 AM
With any dispatcher in runBlocking they will run in parallel
This is why I said that it depends on getA and getB implementation, isn’t it?
o

octylFractal

05/20/2020, 2:28 AM
that is true, but I want to be clear that it requires them to use a different dispatcher
g

gildor

05/20/2020, 2:28 AM
or if they are asyncronous, right?
You trying to say that parallel execution impossible in the same dispatcher with the same thread, but it’s not exactly true
for example you have such things as animations or anything else backed by event queue
of course you may argue it still not parallel, but it does works in parallel, even tho under the hood it dispatched using an even queue
so on practice, from point of view of consumer of this API it run in parallel, exactly what was original intent of this pattern
it’s also true for delay, even tho, I agree, it’s probably to simplistic example, yeah, “Code is not executing”, as you said, but task is executing
o

octylFractal

05/20/2020, 2:32 AM
this code demonstrates the issue:
fun main() {
    runBlocking {
        val a = async {
            println("Computing [A]...")
            // be blocking
            Thread.sleep(1000)
            println("Finish [A]!")
            100
        }
        val b = async {
            println("Computing [B]...")
            // be blocking
            Thread.sleep(1000)
            println("Finish [B]!")
            200
        }
        println("Technically concurrent! I can execute with those queued up.")
        println(a.await() + b.await())
    }
}
both computations are not done in parallel, though they are both done concurrently, i.e. it does not stop you from setting up the code to execute later obviously, just using
Dispatchers.Default
here is enough to alleviate the problem
g

gildor

05/20/2020, 2:33 AM
I mentioned this problem in my first message, it will run in parallel only if functions are not blocking
o

octylFractal

05/20/2020, 2:33 AM
right, but all code at some level is blocking, it's just for how long
g

gildor

05/20/2020, 2:33 AM
Dispatchers.Default will solve it, I agree, but I would say that it’s bad pattern
especially if getA and getB are suspend
right, but all code at some level is blocking, it’s just for how long
Are we talking about absolutes or about practical usage?
it reminds my arguing that non-blocking IO is actually blocking on level of sockets in operation system
Returning to our discussion, I will agree with you, passing dispatcher to runBlocking may be useful if you call a lot of blocking function in parallel inside
But on abstract example, I would say that it’s not the cleanest solution, for suspend functions it shouldn’t be necessary, for blocking functions I would rather wrap them explicitly to new
runInterruptible(IO)
, so usually it’s better to extract them to own suspend function. So it makes usage of dispatcher for runBlocking unnecessary
o

octylFractal

05/20/2020, 2:54 AM
I am kinda talking about practical usage here -- even some operations on non-IO become sufficiently blocking that their performance is measurable. I worked out this example of sorting large lists: https://gist.github.com/octylFractal/5443dc85fbf3673553f30f68fa888f21
results on my 12 core machine are noticeable:
872ms for event loop
346ms for Default dispatcher
basically, I just want people to be aware of the fact that the event loop does not run anything in parallel -- if you delegate to another dispatcher, then it can await the results from said dispatcher concurrently but the event loop itself is not parallel-capable
g

gildor

05/20/2020, 2:56 AM
But this operation is blocking
you do big chunk of computation
o

octylFractal

05/20/2020, 2:56 AM
yes, that's what I'm getting at
g

gildor

05/20/2020, 2:56 AM
so you should wrap this chunk to Dispatcher.Default
o

octylFractal

05/20/2020, 2:57 AM
right, but not everyone does that
g

gildor

05/20/2020, 2:57 AM
this chunk, not expect that some user of this API will do this for you
Okay, okay, I agree with you
o

octylFractal

05/20/2020, 2:57 AM
I'm just saying be aware of how things are implemented
g

gildor

05/20/2020, 2:57 AM
maybe introducing bad pattern to fix another bad code is fine
I would rather tried to avoid both
o

octylFractal

05/20/2020, 2:57 AM
yes, certainly
👍 1
k

Kroppeb

05/20/2020, 7:22 AM
so you should wrap this chunk to Dispatcher.Default
I did not know that was best practice, switching dispatcher close to the expensive code itself.
o

octylFractal

05/20/2020, 7:23 AM
I would think it is, if you're already on the dispatcher it's nearly free
g

gildor

05/20/2020, 7:23 AM
if you do not wrap some heavy code to own dispatcher, it may cause issues for consumer
when invoke suspend function you expect that it will not block for significant amount of time
u

Ugi

05/20/2020, 7:37 AM
basically, I just want people to be aware of the fact that the event loop does not run anything in parallel -- if you delegate to another dispatcher, then it can await the results from said dispatcher concurrently but the event loop itself is not parallel-capable
This is the gist of it. I think it's also important to be aware of what your default dispatcher is especially in multi platform environment, because on JVM it's backed by a thread pool, so you will get parallel execution, but on JS (always single-thread) and Native (uses SingleThreadDispatcher at least in 1.3.5-native-mt-1.4M1) you will get concurrent but not parallel execution.
👍 1
r

rbares

05/20/2020, 3:23 PM
Likewise if using
kotlinx-coroutines-android
you will find that
Dispatchers.Main
is backed by a
Handler
using
Looper.getMainLooper()
, which is another example of a single-threaded event loop. This same logic also applies to dispatchers based on a single-threaded Executor. The lack of parallel execution in these cases is one of the reasons you can use thread confinement to safely mutate shared state without need for other synchronization mechanisms.