I was coding a stopwatch that refreshes every 0.1s.
suspend fun stopwatch() {
val startTime = System.currentTimeMillis()
var lastSeconds = 0L
while (true) {
// refresh rate
delay(100)
// calculate elapsed time
val elapsedTime = System.currentTimeMillis() - startTime
val seconds = elapsedTime / 1000
// update UI if value changed
if (seconds != lastSeconds) {
print("${seconds}s\r")
lastSeconds = seconds
}
}
}
Since delay is less expensive than Thread.sleep (and handler.post?), I thought I could refresh even more often.
suspend fun test() {
val iterations = 10_000_000_000
val timeWithNoDelay = measureTimeMillis {
var count = 0L
while(count < iterations) {
count++
}
}
val timeWithDelay = measureTimeMillis {
var count = 0L
while(count < iterations){
delay(0)
count++
}
}
println("time: $timeWithNoDelay, $timeWithDelay")
println("time per iteration: ${timeWithNoDelay.toDouble() / iterations}, ${timeWithDelay.toDouble() / iterations}")
val timeDelay = timeWithDelay - timeWithNoDelay
println("delay equivalent to ${timeDelay.toDouble() / timeWithNoDelay} additions")
}
time: 3637, 16543
time per iteration: 3.637E-7, 1.6543E-6
delay equivalent to 3.548529007423701 additions
Is there a reason to not use a delay smaller than 100, like delay(10) or even delay(0)? (for an android app) The stopwatch would refresh as often as possible while allowing other tasks in the (UI) queue to execute.
Edit:
"If the given timeMillis is non-positive, this function returns immediately." (documentation) So the time difference I found was from if (timeMillis <= 0) return.
Using yield instead:
time: 414, 32715
iterations: 1000000000, time per iteration: 4.14E-7, 3.2715E-5
delay equivalent to 78.02173913043478 additions
Edit 2:
Also, is it worth doing these calculations outside of the main thread withContext(Dispatchers.Default)? If there are 100 iterations per second it will only take 7ms.
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.yield
import kotlin.system.measureTimeMillis
fun test(iterations: Long){
val startTime = 0
val formattedTime = MutableStateFlow("")
val time = measureTimeMillis {
for (i in 0..iterations) {
val elapsedTime = System.currentTimeMillis() - startTime
formattedTime.value = "$elapsedTime ms"
}
}
println("iterations: $iterations, time: $time, time per iteration: ${time.toDouble()/iterations}")
runBlocking {
val startTime = 0
val formattedTime = MutableStateFlow("")
val timeWithDelay = measureTimeMillis {
for (i in 0..iterations) {
val elapsedTime = System.currentTimeMillis() - startTime
formattedTime.value = "$elapsedTime ms"
yield() // delay of 0
}
}
println("iterations: $iterations, timeWithDelay: $timeWithDelay, time per iteration: ${timeWithDelay.toDouble()/iterations}")
}
}
fun main(){
test(100)
}
iterations: 100, time: 6, time per iteration: 0.06
iterations: 100, timeWithDelay: 7, time per iteration: 0.07