https://kotlinlang.org logo
#coroutines
Title
# coroutines
m

Marko Mitic

08/01/2019, 9:17 PM
^ this inspired me to test whether switching threads in a coroutine flushes the cache similarly to java
synchonized
block. Here's the test, results in thread
Copy code
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield

class Test {
    var i = 0

    suspend fun countWithThreadSwitching(){
        repeat(10) {
            i++
            delay(1) //yield() didn't make coroutine resume on another thread
        }
        println(i)
    }
}

fun main() = runBlocking {
    coroutineScope{
        repeat(100) {
            launch(Dispatchers.Default) {
                Test().countWithThreadSwitching()
            }
        }    
    }
}
Looks like it does - it writes out 100x 10
d

Dominaezzz

08/01/2019, 9:48 PM
cache?
m

Marko Mitic

08/01/2019, 9:51 PM
CPU cache
It does flush the cache
d

Dominaezzz

08/01/2019, 10:00 PM
L1? How do you know?
d

Dico

08/02/2019, 1:25 AM
It's not even switching threads
j

Jag

08/02/2019, 2:33 AM
Yea the above isn’t switching threads.. did you mean something like this?
Copy code
@Test
    fun main() = runBlocking {
        repeat(100) {
            val t = Test()
            coroutineScope {
                launch(Dispatchers.Default) {
                    t.countWithThreadSwitching()
                }
            }
            println(t.i) 
        }
    }
m

Marko Mitic

08/02/2019, 8:46 AM
^ that makes coroutines execute sequentially, no parallelism at all
I wanted to have mutable state changed on only one coroutine which would switch threads
I was expecting
yield
to release current thread and another thread from pool to pick up the coroutine for execution later
Using
delay(1)
instead of
yield()
did the trick
d

Dico

08/02/2019, 8:59 AM
So I don't think the code working as expected says much about cpu cache
You're changing a variable from one thread at a time
m

Marko Mitic

08/02/2019, 9:07 AM
Yeah but some thread could have that variable cached and not notice another thread incrementing the value. In that case some output would be <10
Either caches are flushed on thread switch or I need a better code to cause the scenario
d

Dominaezzz

08/02/2019, 9:41 AM
Caches are always flushed in general, regardless of Kotlin, JVM or OS. CPU guarantees.
m

Marko Mitic

08/02/2019, 9:43 AM
In that case, there wouldn't be a need for AtomicInt, right?
or
volatile
d

Dominaezzz

08/02/2019, 9:45 AM
Race conditions are still a thing with cache flushed.
Depending on the language,
volatile
just tells the compiler to take it easy with the optimizations.
For example the compiler might cache the variable in a register but that won't work for multithreading.
m

Marko Mitic

08/02/2019, 9:48 AM
Ok, one clarification, by cache flushing I also meant cache invalidation
I didn't find proper term for cache flushing+invalidation
So, before entering java's
synchronized
block, caches are invalidated and before leaving it, caches are flushed (ok, that part is CPU automatic)
I was interested whether thread switches in coroutines do the same, that's what code is trying to test
d

Dominaezzz

08/02/2019, 9:53 AM
Since the variable in stored in the heap and captured all over the place, compiler will be very hesitant to optimise the variable away.
So there will be no (Language level) caching in the first place.
3 Views