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