首页 > 解决方案 > 挂起功能的执行时间差异巨大

问题描述

我仍然试图围绕挂起函数以及 IO 绑定和 CPU 绑定挂起函数之间的区别(如果有的话)以及其他事情。

我在主线程中启动一个协程,并以不同的方式运行一个 CPU 密集型函数,看看会发生什么。

class TestActivity : AppCompatActivity(), CoroutineScope {
    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.Main

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        launch {
            val start = System.currentTimeMillis()
            Log.d("test", "start: $start")

            fib(24)

            val finish = System.currentTimeMillis()
            Log.d("test", "finish: $finish")
            Log.d("test", "duration: ${finish - start}")
        }
    }

我已经尝试了该功能的这三种变体fib

private fun fib(x: Int): Int =
    if (x <= 1) x else fib(x - 1) + fib(x - 2)

常规方式:xml 不会立即膨胀,并且该函数需要 0.1 秒才能运行。

private suspend fun fib(x: Int): Int =
    if (x <= 1) x else fib(x - 1) + fib(x - 2)

常规方式 +suspend关键字:xml 不会立即膨胀,并且该函数需要 1.3 秒才能运行。

private suspend fun fib(x: Int): Int =
    withContext(Dispatchers.Default) { if (x <= 1) x else fib(x - 1) + fib(x - 2) }

常规方式 +suspend关键字 + 用 包装它withContext(Dispatchers.Default):xml 立即膨胀,并且该函数需要 25 秒才能运行。

任何人都可以解释为什么这三个功能之间的持续时间有如此大的差异吗?

标签: androidkotlin-coroutines

解决方案


private fun fib(x: Int): Int =
    if (x <= 1) x else fib(x - 1) + fib(x - 2)

这是你的基本情况,斐波那契的一个极其低效的递归实现。它的计算fib(x - 1)完全独立于fib(x - 2),这导致函数调用呈指数级增长。计算fib(24)它大约(黄金比例)24 = 103,682 次调用。

private suspend fun fib(x: Int): Int =
    if (x <= 1) x else fib(x - 1) + fib(x - 2)

从语义上讲,这与上面完全相同。声明为可挂起的函数,但挂起点为零。由于可挂起函数固有的 CPS 转换开销,它更慢。

private suspend fun fib(x: Int): Int =
    withContext(Dispatchers.Default) { if (x <= 1) x else fib(x - 1) + fib(x - 2) }

在这里,您实际上实现了一些并行性,但并行加速与分派非常小的工作的开销相比相形见绌。此外,由于要计算斐波那契数列的第 n 个成员,您需要已经计算的 (n - 1) 和 (n - 2)并行化斐波那契。在您的情况下,您对相同的成员进行了很多重新计算,因此可以通过并行性来改进,但仍然远远不能正确实现单线程解决方案。


推荐阅读