Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asynchronous Kotlin异步速度测试结果评估_Asynchronous_Kotlin_Kotlin Coroutines - Fatal编程技术网

Asynchronous Kotlin异步速度测试结果评估

Asynchronous Kotlin异步速度测试结果评估,asynchronous,kotlin,kotlin-coroutines,Asynchronous,Kotlin,Kotlin Coroutines,我做了一些测试,比较使用async作为延迟结果的方法的速度,以及使用Job或StartCustomer组合来完成相同工作的CompletableDeferred的速度。 总之,有3个用例: 使用默认启动类型的异步(立即)[async] CompletableDeferred+启动(基本上是作业)[cdl] CompletableDeferred+Start例程[ccdl] 结果如下: 简而言之,每个用例测试的每次迭代都会生成10000个异步/cdl/ccdl请求,并等待它们完成。作为热身(

我做了一些测试,比较使用async作为延迟结果的方法的速度,以及使用Job或StartCustomer组合来完成相同工作的CompletableDeferred的速度。 总之,有3个用例:

  • 使用默认启动类型的异步(立即)[async]
  • CompletableDeferred+启动(基本上是作业)[cdl]
  • CompletableDeferred+Start例程[ccdl]
结果如下:

简而言之,每个用例测试的每次迭代都会生成10000个异步/cdl/ccdl请求,并等待它们完成。作为热身(不包括在结果中)重复225次,25次,并在上述过程的100次迭代中收集数据点(如最小值、最大值、平均值)

下面是一个代码:

import com.source.log.log
import kotlinx.coroutines.*
import kotlin.coroutines.Continuation
import kotlin.coroutines.startCoroutine
import kotlin.system.measureNanoTime
import kotlin.system.measureTimeMillis

/**
 * @project Bricks
 * @author SourceOne on 28.11.2019
 */

/*I know that there are better ways to benchmark speed
* but given the produced results this method is fine enough
* */
fun benchmark(warmUp: Int, repeat: Int, action: suspend () -> Unit): Pair<List<Long>, List<Long>> {
    val warmUpResults = List(warmUp) {
        measureNanoTime {
            runBlocking {
                action()
            }
        }
    }

    val benchmarkResults = List(repeat) {
        measureNanoTime {
            runBlocking {
                action()
            }
        }
    }

    return warmUpResults to benchmarkResults
}


/* find way to cancel startedCoroutine when deferred is
*  canceled (currently you have to cancel whole context)
* */
fun <T> CoroutineScope.completable(provider: suspend () -> T): Deferred<T> {
    return CompletableDeferred<T>().also { completable ->
        provider.startCoroutine(
            Continuation(coroutineContext) { result ->
                completable.completeWith(result)
            }
        )
    }
}

suspend fun calculateAsyncStep() = coroutineScope {
    val list = List(10000) {
        async { "i'm a robot" }
    }

    awaitAll(*list.toTypedArray())
}



suspend fun calculateCDLStep() = coroutineScope {
    val list = List(10000) {
        CompletableDeferred<String>().also {
            launch {
                it.complete("i'm a robot")
            }
        }
    }

    awaitAll(*list.toTypedArray())
}



suspend fun calculateCCDLStep() = coroutineScope {
    val list = List(10000) {
        completable { "i'm a robot" }
    }

    awaitAll(*list.toTypedArray())
}


fun main() {
    val labels = listOf("async", "cdl", "ccdl")
    val collectedResults = listOf(
        mutableListOf<Pair<List<Long>, List<Long>>>(),
        mutableListOf(),
        mutableListOf()
    )

    "stabilizing runs".log()
    repeat(2) {
        println("async $it")
        benchmark(warmUp = 25, repeat = 200) {
            calculateAsyncStep()
        }

        println("CDL $it")
        benchmark(warmUp = 25, repeat = 200) {
            calculateCDLStep()
        }

        println("CCDL $it")
        benchmark(warmUp = 25, repeat = 200) {
            calculateCCDLStep()
        }
    }

    "\n#Benchmark start".log()
    val benchmarkTime = measureTimeMillis {
        repeat(100) {
            println("async $it")
            collectedResults[0] += benchmark(warmUp = 25, repeat = 200) {
                calculateAsyncStep()
            }

            println("CDL $it")
            collectedResults[1] += benchmark(warmUp = 25, repeat = 200) {
                calculateCDLStep()
            }

            println("CCDL $it")
            collectedResults[2] += benchmark(warmUp = 25, repeat = 200) {
                calculateCCDLStep()
            }
        }
    }

    "\n#Benchmark completed in ${benchmarkTime}ms".log()
    "#Benchmark results:".log()

    val minMaxAvg = collectedResults.map { stageResults ->
        stageResults.map { (_, benchmark) ->
            arrayOf(
                benchmark.minBy { it }!!, benchmark.maxBy { it }!!, benchmark.average().toLong()
            )
        }
    }

    minMaxAvg.forEachIndexed { index, list ->
        "results for: ${labels[index]} [min, max, avg]".log()
        list.forEach { results ->
            "${results[0]}\t${results[1]}\t${results[2]}".log()
        }
    }
}
import com.source.log.log
导入kotlinx.coroutines*
导入kotlin.coroutines.Continuation
导入kotlin.coroutines.start例程
导入kotlin.system.measureNanoTime
导入kotlin.system.measureTimeMillis
/**
*@项目砖
*@author SourceOne于2019年11月28日发布
*/
/*我知道有更好的方法来测试速度
*但考虑到产生的结果,这种方法已经足够好了
* */
趣味基准测试(预热:Int,重复:Int,动作:suspend()->Unit):配对{
val预热压力=列表(预热){
测量时间{
运行阻塞{
行动()
}
}
}
val benchmarkResults=列表(重复){
测量时间{
运行阻塞{
行动()
}
}
}
将预热压力返回到基准测试结果
}
/*找到在延迟时取消StartedCorroutine的方法
*已取消(当前必须取消整个上下文)
* */
fun CoroutineScope.completable(提供程序:suspend()->T):延迟{
返回CompletableDeferred()。同时{completable->
provider.start例程(
继续(coroutineContext){result->
completable.completeWith(结果)
}
)
}
}
suspend fun calculateAsyncStep()=coroutineScope{
val list=列表(10000){
异步{“我是机器人”}
}
等待全部(*list.toTypedArray())
}
suspend fun calculateCDLStep()=协同作用域{
val list=列表(10000){
CompletableDeferred()。同时{
发射{
完成(“我是一个机器人”)
}
}
}
等待全部(*list.toTypedArray())
}
挂起ECCDLSTEP()=coroutineScope{
val list=列表(10000){
可完成的{“我是机器人”}
}
等待全部(*list.toTypedArray())
}
主要内容(){
val标签=列表(“异步”、“cdl”、“ccdl”)
val collectedResults=列表(
mutableListOf(),
mutableListOf(),
mutableListOf()
)
“稳定运行”。log()
重复(2){
println(“异步$it”)
基准测试(预热=25,重复=200){
calculateAsyncStep()
}
println(“CDL$it”)
基准测试(预热=25,重复=200){
calculateCDLStep()
}
println(“CCDL$it”)
基准测试(预热=25,重复=200){
计算eccdlstep()
}
}
“\n#基准测试开始“.log()
val benchmarkTime=measureTimeMillis{
重复(100){
println(“异步$it”)
collectedResults[0]+=基准测试(预热=25,重复=200){
calculateAsyncStep()
}
println(“CDL$it”)
收集的结果[1]+=基准测试(预热=25,重复=200){
calculateCDLStep()
}
println(“CCDL$it”)
收集的结果[2]+=基准测试(预热=25,重复=200){
计算eccdlstep()
}
}
}
“\n#基准测试在${benchmarkTime}ms.中完成。log()
“#基准测试结果:”.log()
val minMaxAvg=collectedResults.map{stageResults->
stageResults.map{({,基准)->
排列(
benchmark.minBy{it}!!,benchmark.maxBy{it}!!,benchmark.average().toLong()
)
}
}
minMaxAvg.foreachinedexed{index,list->
“${labels[index]}[min,max,avg]的结果。”.log()
list.forEach{results->
“${results[0]}\t${results[1]}\t${results[2]}”.log()
}
}
}
前两个用例(async和cdl)彼此非常接近,而且async总是更好(因为您没有创建作业来完成延迟对象的开销),这并不奇怪,但是比较async和CompletableDeferred+StartCustomer,它们之间有一个巨大的差距(几乎2倍),有利于最后一个。为什么会有如此大的差异?如果有人知道,我们为什么不直接使用CompletableDeferred+Start例程包装器(比如这里的
completable()
)而不是异步呢

补充1: 以下是1000点的示例: 异步和cdl结果中有恒定的尖峰,ccdl(可能是gc?)中也有一些尖峰,但ccdl中的尖峰仍然少得多。我将重新运行这些测试,并改变测试交错的顺序,但它似乎与协同路由机制下的某些东西有关

编辑1:

我已经接受了Marko Topolnik的答案,但除此之外,如果您在启动的范围内等待结果,您仍然可以使用他所谓的“裸启动方法”


例如,如果您将启动几个不同的协同路由(异步),并且在该范围结束时您将等待它们,那么ccdl方法将按预期工作(至少从我在测试中看到的情况来看)。

因为
launch
async
被构建为低级原语
createcoroutineuntercept>之上的一层()
,而
startcroutine
实际上是对它的直接调用,基准测试结果中没有任何意外

为什么我们不直接使用
CompletableDeferred
+
/*
 * find way to cancel startedCoroutine when deferred is
 * canceled (currently you have to cancel whole context)
 */
fun main() = runBlocking {
    bareLaunch {
        try {
            delay(1000)
            println("Coroutine done")
        } catch (e: CancellationException) {
            println("Coroutine cancelled, the exception is: $e")
        }
    }
    delay(10)
}

fun CoroutineScope.bareLaunch(block: suspend () -> Unit) =
        block.startCoroutine(Continuation(coroutineContext) { Unit })

fun <T> CoroutineScope.bareAsync(block: suspend () -> T) =
        CompletableDeferred<T>().also { deferred ->
            block.startCoroutine(Continuation(coroutineContext) { result ->
                result.exceptionOrNull()?.also {
                    deferred.completeExceptionally(it)
                } ?: run {
                    deferred.complete(result.getOrThrow())
                }
            })
        }