簡體   English   中英

協程在 Kotlin 中是否比 Thread 快? 為什么? 如何獲得“上下文切換”的時間?

[英]Is coroutine faster than Thread in Kotlin? And why? How can I get the time of 'context switching'?

我正在測試 Thread 和 Coroutine 之間的速度。

我發現了一個有趣的東西。

當Thread和Coroutine的數量很少時,Thread速度更快。 但是,當數字變大時,Coroutine 會快很多。

這是我測試過的代碼。

class ExampleUnitTest {
    val reps = 1000000
    val sumSize = 999

    @Test
    fun addition_isCorrect() {
        assertEquals(4, 2 + 2)
    }

    @Test
    fun runInThread() {
        var sum = 0
        val threadList = ArrayList<Thread>()

        println("[start] Active Thread = ${Thread.activeCount()}")
        val time = measureTimeMillis {
            repeat(reps) {
                val mThread = Thread {
//                    println("start: ${Thread.currentThread().name}")
//                    Thread.sleep(1000L)
//                    println("end: ${Thread.currentThread().name}")
                }
                mThread.start()
                threadList += mThread
            }

            println("[end] Active Thread= ${Thread.activeCount()}")

            threadList.forEach {
                it.join()
            }
        }
        println("Time: $time ms\n")
    }

    @Test
    fun runInCoroutine() {
        var sum = 0
        val jobList = ArrayList<Job>()

        runBlocking {
            println("[start] Active Thread = ${Thread.activeCount()}")
            val time = measureTimeMillis {
                repeat(reps) {
                    val job = launch(Dispatchers.Default) {
//                        println("start: ${Thread.currentThread().name}")
//                        delay(1000L)
//                        println("end: ${Thread.currentThread().name}")
                    }
                    jobList += job
                }

                println("[end] Active Thread= ${Thread.activeCount()}")

                jobList.forEach {
                    it.join()
                }
            }
            println("Time: $time ms\n")
        }
    }
}
嘗試 重復次數 線程時間(ms) 協程時間(ms)
1個 10 1個 63
2個 100 8個 65
3個 1000 55 90后
4個 10000 426 175
5個 100000 4089 395
6個 1000000 43868 3165

最后,事實證明使用協程比使用大量線程更快。

但是,我認為只有“上下文切換”不會花費那么多時間,因為任務是空的,而且上下文切換工作看起來非常小。 上下文切換能帶來那么大的不同嗎?

我稍微簡化了您的代碼並添加了一個重復測量的循環:

import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.concurrent.thread
import kotlin.system.measureTimeMillis

fun main() {
    repeat(5) {
        runInCoroutine(10)
    }
}

fun runInCoroutine(reps: Int) {
    runBlocking {
        measureTimeMillis {
            val jobList = ArrayList<Job>()
            repeat(reps) {
                jobList += launch { }
            }
            jobList.forEach { it.join() }
        }.also {
            println("Time: $it ms\n")
        }
    }
}

這是我的典型 output:

Time: 15 ms
Time: 1 ms
Time: 1 ms
Time: 0 ms
Time: 0 ms

正如你所看到的,第一次運行除了“第一次運行代碼的時間”之外,並沒有推廣到其他任何東西。 我還注意到我的第一次運行速度比你這邊快四倍,我在 Java 16 和 Kotlin 1.4.32 上。


編輯

我擴展了這個例子,更真實地展示了協程在“上下文切換”方面的優勢。 現在每個任務連續十次休眠 1 ms,我們使用 10,000 個任務:

import kotlinx.coroutines.*
import java.lang.Thread.sleep
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit.NANOSECONDS
import kotlin.concurrent.thread
import kotlin.system.measureNanoTime
import kotlin.system.measureTimeMillis

fun main() {
    val numTasks = 10_000
    repeat(10) { _ ->
        measureNanoTime {
            runInCoroutine(numTasks)
        }.also { tookNanos ->
            println("Took %,d ms".format(NANOSECONDS.toMillis(tookNanos)))
        }
    }
}

fun runInCoroutine(numCoroutines: Int) {
    List(numCoroutines) {
        GlobalScope.launch {
            repeat(10) { delay(1) }
        }
    }.also { jobs ->
        runBlocking {
            jobs.forEach { it.join() }
        }
    }
}

fun runInThread(numThreads: Int) {
    List(numThreads) {
        thread {
            repeat(10) { sleep(1) }
        }
    }.forEach {
        it.join()
    }
}

對於runInCoroutine ,我得到以下信息:

Took 557 ms
Took 341 ms
Took 334 ms
Took 312 ms
Took 296 ms
Took 264 ms
Took 296 ms
Took 302 ms
Took 304 ms
Took 286 ms

對於runInThread ,我得到了這個:

Took 729 ms
Took 682 ms
Took 654 ms
Took 658 ms
Took 662 ms
Took 660 ms
Took 686 ms
Took 706 ms
Took 689 ms
Took 697 ms

協程代碼花費的時間減少了 2.5 倍。 它可能還使用了更少的 RAM,但我沒有測試那部分。

默認情況下,任務使用線程運行,這通常足以滿足您的日常程序。 當有很多任務正在運行時,它們往往會變慢,這就是使用協程有助於加快速度的原因。 線程以串行方式運行任務,而協程同時運行許多任務。

編輯:我認為應該對你有所幫助。

最好的答案可能在這里描述

存在多種差異 - 線程與操作系統線程相關聯時會消耗更多的操作系統資源。 並發線程通常會切換,這很昂貴。 協程不使用操作系統資源,並發協程之間的切換很便宜。

上面的答案不會令很多人滿意。 對於那些不滿意的人 ->

“Couroutine 導致 model,其中所有數據都是線程私有的,而在普通線程中,數據在線程之間共享。因此在執行時節省了大量處理器時間”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM