Coroutine Benchmark

There is a cost to spawn and await on a co-routine, but the cost appears to be quite minimal: On the order of 1/1000-1/100 of a millisecond for async/await usage.

Below are benchmark results, first calling function WITHOUT co-routine and second is with async/await mechanism.

Benchmark: Looped add (no-coroutines)

Metric(nanos)(millis)
Min100.000
P50 (Median)200.000
P90200.000
P95510.000
P99610.000
P99.92710.000
Max38,7050.039
Total21,389,56021.390
Iterations1,000,000

Benchmark: Looped add (with async await)

Metric(micros)(millis)
Min20.002
P50 (Median)20.002
P9030.003
P9530.003
P9950.005
P99.990.009
Max4,2844.284
Total2,371,7682371.768
Iterations1,000,000

Code to reproduce

GT-Sandbox-Snapshot

Code

package com.glassthought.sandbox

import com.asgard.testTools.benchmarker.BenchMarkerJVM
import com.glassthought.sandbox.util.out.impl.out
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking
import kotlin.system.exitProcess


private suspend fun mainImpl() {
  BenchMarkerJVM.benchMark(
    {
      loopedAdd()
    },
    description = "Looped add (no-coroutines)",
  ).printFormattedTable()

  BenchMarkerJVM.benchMark(
    {
      asyncAwaitLoopedAdd()
    },
    description = "Looped add (with async await)",
  ).printFormattedTable()

}

suspend fun asyncAwaitLoopedAdd() {
  coroutineScope {
    val result = async {
      loopedAdd()
    }
    result.await()
  }
}

private fun loopedAdd() {
  for (i in 1..100) {
    add(i, i + 1)
  }
}

private fun add(i: Int, j: Int): Int {
  return i + j
}

fun main(): Unit = runBlocking {
  out.info("START - ON MAIN")

  try {
    mainImpl()

    out.info("DONE - WITHOUT errors on MAIN")
  } catch (e: Exception) {
    out.error("Back at MAIN got an exception! of type=[${e::class.simpleName}] with msg=[${e.message}] cause=[${e.cause}]. Exiting with error code 1")

    exitProcess(1)
  }
}

Command to reproduce:

gt.sandbox.checkout.commit b946649204a145ed938f \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run --quiet"

Recorded output of command:

Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
[INFO][elapsed:   14ms][🥇][①][coroutname:@coroutine#1][tname:main/tid:1] START - ON MAIN
#### Benchmark: Looped add (no-coroutines)
| Metric       | (nanos)         | (millis)        |
|--------------|-----------------|-----------------|
| Min          |              10 |           0.000 |
| P50 (Median) |              20 |           0.000 |
| P90          |              20 |           0.000 |
| P95          |              60 |           0.000 |
| P99          |              70 |           0.000 |
| P99.9        |             240 |           0.000 |
| Max          |         110,706 |           0.111 |
| Total        |      21,951,663 |          21.952 |
| Iterations   |       1,000,000 |                 |

#### Benchmark: Looped add (with async await)
| Metric       | (micros)        | (millis)        |
|--------------|-----------------|-----------------|
| Min          |               2 |           0.002 |
| P50 (Median) |               2 |           0.002 |
| P90          |               3 |           0.003 |
| P95          |               3 |           0.003 |
| P99          |               5 |           0.005 |
| P99.9        |               8 |           0.008 |
| Max          |           2,145 |           2.145 |
| Total        |       2,323,353 |        2323.353 |
| Iterations   |       1,000,000 |                 |

[INFO][elapsed: 2658ms][🥇][①][coroutname:@coroutine#1][tname:main/tid:1] DONE - WITHOUT errors on MAIN

Backlinks