2017-10-21 439 views
8

Kotlin corutines是有限狀態機和一些任務運行器的糖(例如,默認的ForkJoinPool)。 https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md#implementation-details哪個協程(goroutines和kotlin協程)更快?

換句話說,java/kotlin運行時中沒有運行時協同程序(但可以通過http://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html更改)。 Kotlin協同程序只是順序執行的任務,它們被逐一執行。每個任務都可以在線程池中的任何線程中執行。

Go運行時支持「協同程序」。但是goroutines不是真正的協程。 Goroutines不允許在程序中設置屈服點。另外,Go不允許設置自定義線程池。您只能在默認池中設置線程的大小。

kotlin協程和goroutines之間的第一個區別是Go運行時管理此時正在運行的協程。當某個IO操作(或同步原語)阻塞了goroutine時,選擇下一個作業執行它。在JVM中,這種條件下沒有智能工作轉換。

因此,Go可以便宜地更改當前正在運行的作業。 Go只需要更改一些註冊表https://groups.google.com/forum/#!msg/golang-nuts/j51G7ieoKh4/wxNaKkFEfvcJ。但有人說,JVM可以使用堆棧線程而不是使用寄存器。所以根本沒有保存和加載寄存器。

kotlin協程和goroutines之間的第二個區別是協程類型。 Kotlin協程是無堆棧協程。 Goroutines是堆棧協程。 Kotlin協同程序的所有狀態都存儲在Kotlin環境中,並存儲在堆中。 Goroutines狀態存儲在寄存器和線程堆棧中。

我想知道哪些協程(goroutines和kotlin協程)在IO綁定任務中更快? CPU綁定任務?內存消耗如何?

+0

「堆棧協程」和「無堆棧協程」之間的區別是不明確的,沒有實際意義。有關詳細信息,請參閱我的JVMLS講座:https://www.youtube.com/watch?v=3xalVUY69Ok –

回答

24

Kotlin中的協程與Go中的協程不同,所以哪一個「更快」取決於您正在解決的問題以及您正在編寫的代碼類型。

一般來說,很難預先告訴哪一個人可以更好地處理您手邊的問題。您必須爲特定的工作負載運行基準測試。但是,這裏是關鍵差異的一般總結,應該給你一些指導。

  • Kotlin協程每個簡單實例所需的內存少於Go goroutines。 Kotlin中的一個簡單協程只佔用了幾十個字節的堆內存,而Go程序以4KiB的堆棧空間開始。這意味着,如果你打算擁有數百萬條協程,那麼Kotlin中的協程可能會給你一個優勢。它還使得Kotlin協同程序更適合於生成器和惰性序列等非常短暫的小任務。

  • Kotlin協同程序可以轉到任何棧深度,但每個掛起函數的調用都會在堆中爲其堆棧分配對象。 Kotlin協同程序中的調用堆棧當前是作爲堆對象的鏈接列表實現的。相反,Go中的goroutines使用線性堆棧空間。這使得在Go中更高效的懸掛在深層堆棧上。因此,如果您正在編寫的代碼在堆棧中暫停很深,可能會發現goroutines對您更有效。

  • 高效的異步IO是一個非常多維的設計問題。對於某種應用程序而言,高效的方法可能無法爲另一種應用程序提供最佳性能。 Kotlin協同程序中的所有IO操作都是用Kotlin或Java編寫的庫實現的。Kotlin代碼提供了各種各樣的IO庫。在Go中,異步IO由Go運行時使用通用Go代碼不可用的基元實現。如果Go方法實現IO操作非常適合您的應用程序,那麼您可能會發現它與Go運行時的緊密集成爲您帶來了優勢。另一方面,在Kotlin中,您可以找到一個庫或自己編寫一個以最適合您的應用程序的方式實現異步IO的庫。

  • Go運行時在物理OS線程上完成對調度goroutines執行的控制。這種方法的優點是你不必考慮這一切。使用Kotlin協同程序,您可以對協同程序的執行環境進行細粒度控制。這很容易出錯(例如,您可能會創建太多不同的線程池,並浪費CPU時間來切換它們之間的上下文切換)。但是,它使您能夠爲應用程序微調您的線程分配和上下文切換。例如,在Kotlin中,很容易在單個OS線程(或線程池)中執行整個應用程序或其代碼的子集,以便完全避免在OS線程之間切換上下文,只需通過編寫相應的代碼即可。

+0

「在代碼中無法說」在相同的OS線程上運行這些goroutines「。」不是100%真實的:'runtime.LockOSThread()'調用將調用的goroutine鎖定到當前正在運行的操作系統線程。這將確保調用的goroutine總是安排在同一個線程上,並且沒有其他goroutine會這樣做。 OTOH,除了非常罕見的情況,這是不需要的,事實上,如果可能的話,Go調度器會非常努力地確保從線程移除的goroutine再次安排在其上,因此常常會適得其反。 – kostix

+1

@Max,考慮閱讀[這篇經典文章](http://journal.stuffwithstuff.com/2015/02/01/what-c​​olor-is-your-function/)關於併發性作爲庫之間的區別和並行通過運行時調度程序區分(這裏:Java +任何東西與Go)。 – kostix

+0

@kostix謝謝。更正,通過刪除此部分的準確性。 –