Ruby には実際のマルチスレッド機能があるのでしょうか?
-
09-06-2019 - |
質問
私は Ruby の「協力的」スレッド化について知っています。 緑の糸. 。処理に複数の CPU コアを利用するために、アプリケーション内で実際の「OS レベル」のスレッドを作成するにはどうすればよいですか?
解決
Jörg の 2011 年 9 月のコメントを更新
2つを混同しているようですね とても ここではさまざまなことが起こります:Rubyプログラミング言語と、Rubyプログラミング言語の1つの特定の実装の特定のスレッドモデル。現在、Rubyプログラミング言語の約11の異なる実装があり、 とても 異なるユニークなスレッドモデル。
(残念ながら、これらの11の実装のうち2つだけが実際に生産の準備ができていますが、年末までにその数はおそらく4〜5になります。)アップデート:今は5です:MRI、JRuby、YARV (Ruby 1.9 のインタプリタ)、Rubinius、IronRuby)。
最初の実装には実際には名前がありません。そのため、それを参照するのは非常に厄介で、本当に迷惑で混乱しています。ほとんどの場合、「Ruby」と呼ばれます。これは、Rubyプログラミング言語の機能と特定のRubyの実装の間の無限の混乱につながるため、名前を持たないよりもさらに迷惑で混乱しています。
また、「MRI」(「MatzのRuby実装」の場合)、CrubyまたはMatzrubyとも呼ばれます。
MRIは、ルビースレッドを通訳内の緑のスレッドとして実装しています. 。残念ながら、これらのスレッドを並行してスケジュールすることはできず、一度に1つのスレッドしか実行できません。
ただし、任意の数のCスレッド(POSIXスレッドなど)はRubyスレッドと並行して実行できるため、外部Cライブラリ、または独自のスレッドを作成するMRI C拡張機能は並行して実行できます。
2番目の実装は、 ヤーフ (「さらに別のRuby VM」の略)。 YARVはルビースレッドをPOSIXまたはWindowsNTスレッドとして実装しています, ただし、グローバルインタープリターロック(GIL)を使用して、1つのRubyスレッドのみが実際に一度にスケジュールできるようにします。
MRI、C スレッドのような できる 実際には Ruby スレッドと並行して実行されます。
将来的には、GIL が かもしれない より細粒のロックに分解されるため、ますます多くのコードが実際に並行して実行できるようになりますが、それは遠く離れています。 計画された まだ。
JRuby Ruby スレッドをネイティブ スレッドとして実装します, 、JVMの場合の「ネイティブスレッド」は明らかに「JVMスレッド」を意味します。JRuby はそれらに追加のロックを課しません。したがって、これらのスレッドが実際に並行して実行できるかどうかは、JVMに依存します。一部のJVMは、JVMスレッドをOSスレッドとして実装し、一部はグリーンスレッドとして実装しています。(Sun/Oracle の主流の JVM は、JDK 1.3 以降、OS スレッドのみを使用します)
XRuby また Ruby スレッドを JVM スレッドとして実装します. アップデート:XRubyは死んだ。
鉄ルビー Ruby スレッドをネイティブ スレッドとして実装します, 、CLRの場合の「ネイティブスレッド」は明らかに「CLRスレッド」を意味します。Ironrubyはそれらに追加のロックを課さないため、CLRがそれをサポートしている限り、それらは並行して実行する必要があります。
Ruby.NET また RubyスレッドをCLRスレッドとして実装します. アップデート: Ruby.NETは死んだ。
ルビニウス ルビースレッドを仮想マシン内の緑のスレッドとして実装します. 。より正確に:Rubinius VMは、非常に軽量で、非常に柔軟な並行性/並列性/非ローカルコントロールフロー構造をエクスポートします。タスク"、および他のすべての並行構成(この議論のスレッドだけでなく、 続き, 俳優 およびその他のもの)は、タスクを使用して純粋なルビーに実装されます。
Rubiniusは(現在)スレッドを並行してスケジュールすることはできませんが、それはあまり問題ではありません。ルビニウスはすでにできます いくつかのPOSIXスレッドでいくつかのVMインスタンスを並行して実行する, 1 つの Rubinius プロセス内で。スレッドは実際にRubyに実装されているため、他のRubyオブジェクトと同様に、シリアル化して、別のPOSIXスレッドで別のVMに送信できます。(BEAMと同じモデルです アーラン VMはSMPの並行性を使用します。それはすでに ルビニウス俳優向けに実装.)
アップデート:この回答の Rubinius に関する情報は、Shotgun VM に関するものですが、もう存在しません。「新しい」C++ VM は、複数の VM にわたってスケジュールされたグリーン スレッドを使用しません (つまり、Erlang/BEAM スタイル)、CLR、Mono、およびほぼすべての JVM で採用されているものと同様に、複数のネイティブ OS スレッド モデルを備えたより伝統的な単一 VM を使用します。
マクルビー Objective-C Runtime、CoreFoundation、Cocoa Frameworksの上にあるYARVの港として始まりました。現在、Yarvから大幅に分岐していますが、現在はまだ YARVと同じスレッドモデルを共有します. アップデート: MacRuby は Apple のガベージ コレクタに依存していますが、これは非推奨と宣言されており、MacOSX の以降のバージョンでは削除される予定です。MacRuby はアンデッドです。
枢機卿 のための Ruby 実装です。 オウム仮想マシン. 。それはまだスレッドを実装していませんが、それがそうするとき、それはおそらくそれらを実装するでしょう オウムスレッド. アップデート:カーディナルは非常に活動的ではないか、死んでいるように見えます。
マグレブ のための Ruby 実装です。 Gemstone/s SmallTalk VM. 。スレッドモデルのGemstone/sが使用するスレッド化、Maglevが使用するスレッド化されたスレッドがまだ実装されている場合でも、どのような情報がありません(おそらくそうではありません)。
ホットルビー は ない 独自の完全なルビーの実装。JavaScriptにおけるYARV ByteCode VMの実装です。hotrubyはスレッドをサポートしていません(まだ?)、JavaScriptには真の並列性をサポートしていないため、並行して実行できません。ただし、HotRubyのActionScriptバージョンがあり、ActionScriptは実際に並列性をサポートする場合があります。 アップデート:HotRuby は死んだ。
残念ながら、これらの11のRuby実装のうち2つだけが実際に制作可能です。MRI と JRuby。
したがって、あなたが真の並列スレッドが必要な場合、Jrubyは現在あなたの唯一の選択肢です - それが悪いものではありません:Jrubyは実際にはMRIよりも速く、間違いなく安定しています。
それ以外の場合、「クラシック」ルビーソリューションは、並列性のためにスレッドの代わりにプロセスを使用することです。Ruby Coreライブラリにはが含まれています Process
モジュール とともに Process.fork
方法 これにより、別のルビープロセスを簡単に分岐できます。また、Ruby 標準ライブラリには、分散Ruby (dRuby / dRb) ライブラリは、同じマシンだけでなく、ネットワーク全体でも、複数のプロセスにRubyコードを簡単に分散できるようにすることができます。
他のヒント
Ruby 1.8 にはグリーン スレッドのみがあり、実際の「OS レベル」のスレッドを作成する方法はありません。ただし、Ruby 1.9 にはファイバーと呼ばれる新機能が追加され、実際の OS レベルのスレッドを作成できるようになります。残念ながら、Ruby 1.9 はまだベータ版であり、数か月以内に安定版になる予定です。
もう 1 つの方法は、JRuby を使用することです。JRuby はスレッドを OS レベルのスレッドとして実装しており、そこには「グリーン スレッド」はありません。JRuby の最新バージョンは 1.1.4 で、Ruby 1.8 と同等です。
それは実装によって異なります。
- MRIはありませんが、YARVの方が近いです。
- JRuby と MacRuby にはそれがあります。
ルビーは クロージャ として Blocks
, lambdas
そして Procs
. 。JRuby のクロージャとマルチコアを最大限に活用するには、 Javaの実行者 便利です;私が好きなMacRubyの場合 GCD のキュー.
作成できることに注意してください。 実際の「OSレベル」スレッド 並列処理に複数の CPU コアを使用できることを意味するものではありません。以下の例を見てください。
これはの出力です 3つのスレッドを使用する単純なRubyプログラム Ruby 2.1.0を使用:
(jalcazar@mac ~)$ ps -M 69877
USER PID TT %CPU STAT PRI STIME UTIME COMMAND
jalcazar 69877 s002 0.0 S 31T 0:00.01 0:00.04 /Users/jalcazar/.rvm/rubies/ruby-2.1.0/bin/ruby threads.rb
69877 0.0 S 31T 0:00.01 0:00.00
69877 33.4 S 31T 0:00.01 0:08.73
69877 43.1 S 31T 0:00.01 0:08.73
69877 22.8 R 31T 0:00.01 0:08.65
ここでわかるように、OS スレッドは 4 つありますが、状態を持つスレッドは 1 つだけです。 R
が走っています。これは、Ruby のスレッドの実装方法の制限によるものです。
同じプログラムが JRuby で使用されています。状態を示す 3 つのスレッドが表示されます R
, 、これは並行して実行されていることを意味します。
(jalcazar@mac ~)$ ps -M 72286
USER PID TT %CPU STAT PRI STIME UTIME COMMAND
jalcazar 72286 s002 0.0 S 31T 0:00.01 0:00.01 /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/bin/java -Djdk.home= -Djruby.home=/Users/jalcazar/.rvm/rubies/jruby-1.7.10 -Djruby.script=jruby -Djruby.shell=/bin/sh -Djffi.boot.library.path=/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni/Darwin -Xss2048k -Dsun.java.command=org.jruby.Main -cp -Xbootclasspath/a:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jruby.jar -Xmx1924M -XX:PermSize=992m -Dfile.encoding=UTF-8 org/jruby/Main threads.rb
72286 0.0 S 31T 0:00.00 0:00.00
72286 0.0 S 33T 0:00.00 0:00.00
72286 0.0 S 31T 0:00.09 0:02.34
72286 7.9 S 31T 0:00.15 0:04.63
72286 0.0 S 31T 0:00.00 0:00.00
72286 0.0 S 31T 0:00.00 0:00.00
72286 0.0 S 31T 0:00.00 0:00.00
72286 0.0 S 31T 0:00.04 0:01.68
72286 0.0 S 31T 0:00.03 0:01.54
72286 0.0 S 31T 0:00.00 0:00.00
72286 0.0 S 31T 0:00.01 0:00.01
72286 0.0 S 31T 0:00.00 0:00.01
72286 0.0 S 31T 0:00.00 0:00.03
72286 74.2 R 31T 0:09.21 0:37.73
72286 72.4 R 31T 0:09.24 0:37.71
72286 74.7 R 31T 0:09.24 0:37.80
同じプログラムが MacRuby になりました。また、3 つのスレッドが並行して実行されます。それの訳は MacRuby スレッドは POSIX スレッドです (実際の「OSレベル」スレッド) そこには GVLなし
(jalcazar@mac ~)$ ps -M 38293
USER PID TT %CPU STAT PRI STIME UTIME COMMAND
jalcazar 38293 s002 0.0 R 0T 0:00.02 0:00.10 /Users/jalcazar/.rvm/rubies/macruby-0.12/usr/bin/macruby threads.rb
38293 0.0 S 33T 0:00.00 0:00.00
38293 100.0 R 31T 0:00.04 0:21.92
38293 100.0 R 31T 0:00.04 0:21.95
38293 100.0 R 31T 0:00.04 0:21.99
もう一度同じプログラムですが、古き良き MRI を使用しています。この実装ではグリーン スレッドが使用されているため、スレッドは 1 つだけ表示されます。
(jalcazar@mac ~)$ ps -M 70032
USER PID TT %CPU STAT PRI STIME UTIME COMMAND
jalcazar 70032 s002 100.0 R 31T 0:00.08 0:26.62 /Users/jalcazar/.rvm/rubies/ruby-1.8.7-p374/bin/ruby threads.rb
Ruby マルチスレッドに興味がある場合は、私のレポートを参照してください。 フォークハンドラーを使用した並列プログラムのデバッグ 面白い。
Ruby の内部構造のより一般的な概要については、 顕微鏡下のルビー 良い読み物です。
また、 Ruby スレッドと C のグローバル インタプリタ ロック Omniref では、Ruby スレッドが並列実行されない理由をソース コードで説明しています。
使ってみてはどうでしょうか drb?これは実際のマルチスレッドではなく、複数のプロセス間の通信ですが、1.8 では使用できるようになり、摩擦はかなり低くなります。
この質問には「システムモニター」に答えてもらいましょう。どちらの場合も、i7 (4 ハイパースレッドコア) マシン上で実行されている 8 つの Ruby スレッドで同じコード (以下、素数を計算します) を実行しています...最初の実行は次のとおりです。
Jruby 1.5.6(Ruby 1.8.7 Patchlevel 249)(2014-02-03 6586)(OpenJDK 64-Bit Server VM 1.7.0_75)[AMD64-Java
2 つ目は次のとおりです。
ルビー 2.1.2p95 (2014-05-08) [x86_64-linux-gnu]
興味深いことに、CPU は JRuby スレッドの方が高いですが、完了までの時間は解釈された Ruby の方がわずかに短くなります。グラフからはわかりにくいですが、2 回目の (Ruby を解釈した) 実行では CPU の約 1/2 が使用されています (ハイパースレッディングなし?)。
def eratosthenes(n)
nums = [nil, nil, *2..n]
(2..Math.sqrt(n)).each do |i|
(i**2..n).step(i){|m| nums[m] = nil} if nums[i]
end
nums.compact
end
MAX_PRIME=10000000
THREADS=8
threads = []
1.upto(THREADS) do |num|
puts "Starting thread #{num}"
threads[num]=Thread.new { eratosthenes MAX_PRIME }
end
1.upto(THREADS) do |num|
threads[num].join
end
MRI を使用している場合は、拡張機能として、または Ruby-inline gem を使用して C でスレッド コードを作成できます。
実稼働レベルのシステム (ベータ版を使用できない場合) で Ruby の並列処理が本当に必要な場合は、おそらくプロセスがより良い代替手段となります。
ただし、まず JRuby でスレッドを試してみる価値は間違いなくあります。
また、Ruby でのスレッドの将来に興味がある場合は、これを見つけるかもしれません。 記事 役に立つ。
ここでは、Linda (並列処理および分散コンピューティング パラダイム) の Ruby 実装である Rinda についての情報をいくつか示します。 http://charmalloc.blogspot.com/2009/12/linda-tuples-rinda-drb-Parallel.html
その回答を編集できなかったため、ここに新しい返信を追加してください。
更新(2017-05-08)
この記事は非常に古く、情報はCurrent(2017)トレッドに従うことはありません。以下はいくつかのサプリメントです。
オパール Ruby から JavaScript へのソース間コンパイラーです。また、Ruby corelib の実装もあり、現在非常に活発に開発されており、多くの (フロントエンド) フレームワークが開発されています。そして生産準備完了。JavaScript をベースとしているため、並列スレッドはサポートされていません。
トリュフルビー Ruby プログラミング言語の高性能実装です。Oracle Labs によって GraalVM 上に構築された TruffleRuby は JRuby のフォークであり、Rubinius プロジェクトのコードと結合されています。また、Ruby の標準実装、MRI のコードも含まれていますが、本番環境にはまだ対応していません。このバージョンの Ruby はパフォーマンスのために生まれたようです。並列スレッドをサポートするかどうかはわかりませんが、サポートする必要があると思います。