Вот очищенная версия вашего кода, которую я использовал для бенчмаркинга. Обратите внимание, что я удалил print
из измеренного кода, потому что сама печать является тяжеловесной операцией, включающей мьютексы, JNI, блокировку выходных потоков и т. Д. Вместо этого я обновляю переменную volatile.
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.future.await
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.lang.Thread.sleep
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletionStage
import java.util.concurrent.TimeUnit.NANOSECONDS
@Volatile
var total = 0
@ExperimentalCoroutinesApi
fun main() = runBlocking {
println("Warmup")
measure(20_000)
println("Measure")
val begin = System.nanoTime()
measure(40_000)
println("Completed in ${NANOSECONDS.toMillis(System.nanoTime() - begin)} ms")
}
fun getText(): CompletableFuture<Int> {
return CompletableFuture.supplyAsync {
sleep(1)
1
}
}
suspend fun measure(count: Int) {
val jobs = List(count) {
GlobalScope.launch { total += getText().await() } // :1
// getText().thenAccept { total += it } // :2
}
jobs.forEach { it.join() }
}
Мой результат составляет 6,5 секунды. для случая номер один против 7 секунд для случая номер два. Это разница в 7%, и она, вероятно, очень специфична для этого конкретного сценария, а не то, что вы обычно будете видеть как разницу между этими двумя подходами.
Причина выбора сопрограмм вместо CompletionStage
программирования определенноне о тех 7%, а о огромной разнице в удобстве. Чтобы понять, что я имею в виду, я предлагаю вам переписать функцию main
, вызвав просто computeAsync
, без использования future.await()
:
suspend fun main() {
try {
if (compute(1) == 2) {
println(compute(4))
} else {
println(compute(7))
}
} catch (e: RuntimeException) {
println("Got an error")
println(compute(8))
}
}
fun main_no_coroutines() {
// Let's see how it might look!
}
fun computeAsync(input: Int): CompletableFuture<Int> {
return CompletableFuture.supplyAsync {
sleep(1)
if (input == 7) {
throw RuntimeException("Input was 7")
}
input % 3
}
}
suspend fun compute(input: Int) = computeAsync(input).await()