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