Suppose I have ```suspend fun myFun() { repeat...
# coroutines
m
Suppose I have
Copy code
suspend fun myFun() {
    repeat(100) {
        someSuspendingFunSpecifyingIODispatcher()
        someQuickNonSuspendingFun()
    }
}
At the top-level of myFun() is it better to have withContext(Dispatchers.IO), coroutineScope {} or leave it as it is?
g
Leave it as it is
I see no reason to wrap to IO or creating new scope if you just use regular functions
m
wouldn’t there be more potential context switching if the caller calls using, say, Main dispatcher?
g
Why so?
m
I’m guessing there would be some kind of switching from Main to IO each time someSuspendingFunSpecifyingIODispatcher() is called
g
Sure
m
but with withContext(Dispatcher.IO) there wouldn’t be
g
Yes, but it will be on withContext, you just moved it a bit and create unnecessary scope and suspend point and make it less readable
Without any performance gain
m
Just to be clear, I don’t mean the withContext() inside the repeat(), I mean at the top level
Copy code
suspend fun myFun() = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
    repeat(100) {
        someSuspendingFunSpecifyingIODispatcher()
        someQuickNonSuspendingFun()
    }
}
Wouldn’t that run entirely on IO dispatcher? Surely more efficient?
g
I see what you mean
Problem that you leak implementation details
Of someSuspendingFunSpecifyingIO
m
For sure it’s a potential optimization. Knowledge that something will run in IO, means we have the option to choose a strategy knowing that.
g
If this function is blocking (require run on IO) and it's private it would even more performant to do not make it suspend and do not switch any context, just keep it blocking and switch context before repeat
m
Thing is, I have all these utility methods working on Files and InputStreams and I’m making them all suspend functions and wondering whether I should be putting withContext(Dispatchers.IO) at the top of each one. Or just at the lowest level.
Oh I see
g
If it public general usage function and you want easily and safer use it from suspend function, that this function should switch context explicitly inside of the body
If you need performance for some higher level functions, than just use blocking functions to avoid suspend point creation, allocations and context switches
m
Ok, so in general keep a bunch of private blocking functions and public suspend functions that choose the correct dispatcher
g
Yes, this is how we try to organize such APIs
But again, if you want to make it a bit more performant
But careful with cancellation, check for isActive or call yield if you don't have other suspend calls between blocking calls to make it cancellation friendly
👍 1
m
Do you use some kind of naming standard to distinguish between public suspend and private blocking functions? I can imagine there would be lots of cases where the former simply calls the latter (plus a withContext)
p
But careful with cancellation, check for isActive or call yield if you don't have other suspend calls between blocking calls to make it cancellation friendly
why do you need to check for this, shouldn't the next suspension at
someSuspendingFunSpecifyingIODispatcher
just not be triggered?
m
I think he’s talking about the general case
g
I'm talking about case when you some suspend function that calls many blocking APIs. If you call suspend functions nothing worry about
I usually call function that blocks with Blocking suffix to make it explicit
👍 2