Project Loomをご存知でしょうか?
http://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html
Project Loomは軽量スレッドであるFiberと、継続(Continuation)をJavaに導入することを狙っています。Project Loomはまだ提案されたばかりで、OpenJDKのプロジェクトとして登録されていません。現在は、プロジェクトの提案を参照することが可能です。
最新Java情報局 - (3/5)JavaOne 2017レポート、ここ数年で最も充実していたキーノート:ITpro
僕も軽量スレッド?Fiber?という感じです(継続はまだしも)。なのでLoomそのものの前にFiberを調べてみます。すると、"Quasar"という製品に行き着きます。"lightweight threads and actors for the JVM"、JVM用軽量スレッドとアクターです。
Quasar’s core implements true lightweight threads on the JVM called fibers. Fibers can be instantiated and run just like regular threads, but rather than a few thousand of threads, a single JVM can easily run hundreds of thousands or even millions of fibers.
Quasarのコア実装はJVM上のホンモノの軽量スレッドで、fiberと呼ばれるものです。fiberは通常のスレッドのようにインスタンス生成と実行ができますが、単一のJVMではスレッドでは数千といったところでもfiberなら簡単に数十万、数百万でさえ実行できます。
なるほど、すごそうです。ではこのQuasarのfiberはProject Loomでのfiberと同一なのか?という疑問が起こります。
If you look at openjdk mailing list Quasar author has joined Oracle and putting this proposal.
openjdkのメーリングリストを見れば、Quasar実装者がオラクルに入ってこの提案を書いたとわかるよ。
Project Loom: Fibers and Continuations for the Java Virtual Machine | Svelte Hacker News
HIbernateとJPAのような関係でしょうか。Quasarのfiberがベースと考えてよさそうです。では課題を置き換えて、Quasarのfiberは何なのでしょう?JReble/XRebelで有名なZeroTurnaround社のブログに記事があります。
What are fibers? -> fibers are threads
How is a fiber used? -> a Quasar fiber is used exactly like a Java thread, and by that I mean it implements the thread API.
(単純に言うと)fiberはスレッドであり、完全にスレッドのように使える、ということです。
スレッドはOSが実装しているものに対して、fiberはユーザモードで実装されるものです。このユーザモードというのはJavaの話ではなく、CPUのモードの話です。
コンピューターのプロセッサには、ユーザー モードとカーネル モードという 2 つのモードがあります。2 つのモードの間で使うプロセッサのスイッチは、プロセッサ上で実行されているコードの種類によって異なります。アプリケーションはユーザー モードで、オペレーティング システムのコア コンポーネントはカーネル モードでそれぞれ実行されます。
ユーザー モードとカーネル モード (Windows Drivers)
そもそもOSスレッドにはパフォーマンス上のペナルティがあります。スレッド間のスイッチにはユーザモードからカーネルモードへのジャンプが含まれ、アドレス空間境界を越えることもあります。これはコストの高い操作で、TLBのフラッシュやキャッシュミス、CPUのパイプラインを混乱させるといったことを引き起こします。割り込みやsyscallが通常のプロシージャコールよりとてつもなく遅くなる理由です。カーネルのスケジュールスレッドは汎用スケジュールアルゴリズムを使っており、あらゆる種類のスレッドを考慮します。fiberはアプリケーションレイヤでスケジュールされているので、アプリケーション外のことは考慮せずに済みます。
IOや他のfiberから起動され、プロセッシングサイクルが短く、キューなど他の同期メカニズムで次のfiverに処理を移すというfiberの振る舞いは、スケジューラに“work-stealing”アルゴリズムを採用するのがよく、これはErlangやGoで採用されており、Quasarでもデフォルトで使うようです。fiber間の切り替えで最小のキャッシュミスで済みます。
カーネルは高い並列性ではボトルネックとなるようです。スレッドでのカーネルデータ構造を避けることで、メモリフットプリントや効率的なスイッチを得られます。
fiberはどのように使うのでしょうか?Javadocを見てみます。
There are two ways to create a new fiber: either subclass the Fiber class and override the run method, or pass the code to be executed in the fiber as the target parameter to the constructor. All in all, the Fiber API resembles the Thread class in many ways.
A fiber runs inside a ForkJoinPool.
A new Fiber occupies under 400 bytes of memory (when using the default stack size, and compressed OOPs are turned on, as they are by default).
fiberの生成方法は2つある。Fiberクラスのサブクラスを作りrunメソッドをオーバーライドするか、コンストラクタのtargetパラメータにfiberで実行させたいコードを渡すかだ。全体的にFiberのAPIはThreadクラスに多くの点で似ている。
fiberはForkJoinPool内で動作する。
新しいFIberはメモリを400バイト未満しか使わない(デフォルトのスタックサイズを使う場合かつcompressed OOPがオンであるとき。これらはデフォルトでそうなっている)。
こんなコードですね。
new Fiber<V>() { @Override protected V run() throws SuspendExecution, InterruptedException { // your code } }.start();
ジェネリックで戻り値を定義できます。Kotlinでも。
fiber @Suspendable { // The fiber will be created and will start executing this body }
Kotlinにはkotlinx.coroutines
(コルーチン)もあるでしょうし、この辺りの住み分け/違いはどうなっているのでしょうか?
strandやpark/unparkといった項目もありますので、引き続き調べます。
なお、Quasarのドキュメントはしっかりしていますので、詳細はそちらから。