技術的には、なぜErlangのプロセスはOSスレッドよりも効率的ですか?


170

アーランの特徴

以下からのErlangプログラミング(2009):

Erlangの並行性は高速でスケーラブルです。そのプロセスは軽量であり、Erlang仮想マシンは、作成されたすべてのプロセスに対してOSスレッドを作成しません。これらは、基盤となるオペレーティングシステムに関係なく、VMで作成、スケジュール、および処理されます。その結果、プロセスの作成時間はマイクロ秒のオーダーであり、同時に存在するプロセスの数とは無関係です。これをJavaおよびC#と比較してください。すべてのプロセスについて、基盤となるOSスレッドが作成されます。Erlangが両方の言語よりもはるかに優れているため、いくつかの非常に競争力のある比較が得られます。

同時実行アーラン(PDFファイル)に指向プログラミング (スライド)(2003年):

Erlangプロセスの作成にかかる時間は1µsから2,500プロセスまで一定であることがわかります。その後、最大30,000プロセスで約3µsに増加します。JavaとC#のパフォーマンスは、図の上部に示されています。少数のプロセスでは、プロセスの作成に約300µsかかります。2,000を超えるプロセスを作成することは不可能です。

最大30,000プロセスの場合、2つのErlangプロセス間でメッセージを送信する時間は約0.8µsであることがわかります。C#の場合、メッセージごとに約50µs、最大プロセス数(約1800プロセス)までかかります。Javaはさらに悪く、最大100のプロセスでメッセージあたり約50µsかかった後、約1000のJavaプロセスがあるとメッセージあたり10msに急速に増加しました。

私の考え

Erlangプロセスが新しいプロセスの生成においてはるかに効率的であり、プロセスごとのメモリフットプリントがはるかに小さい理由を技術的に完全には理解していません。OSとErlang VMの両方は、スケジューリング、コンテキストスイッチを実行し、レジスタの値を追跡する必要があります。

単に、OSスレッドがErlangのプロセスと同じ方法で実装されていないのはなぜですか?彼らはもっと何かをサポートする必要がありますか?そして、なぜ彼らはより大きなメモリフットプリントを必要とするのですか?そしてなぜ彼らは産卵とコミュニケーションが遅いのですか?

技術的に、スポーンと通信に関して、ErlangのプロセスがOSスレッドよりも効率的であるのはなぜですか?また、OSのスレッドを同じ効率的な方法で実装および管理できないのはなぜですか。また、OSスレッドのメモリフットプリントが大きく、生成と通信が遅いのはなぜですか?

もっと読む


1
仮説が真である理由を理解しようとする前に、仮説が真であるかどうかを確認する必要があります。たとえば、証拠によって裏付けられています。あなたはのための参照を持っていますかどのような-のためのようなErlangのプロセスが実際にすることを実証比較最新のJVM上の(たとえば)Javaスレッドよりも効率的ですか?または、OSプロセスとスレッドサポートを直接使用するCアプリですか?(後者は私には非常に、非常にありそうもないように見えます。前者は多少可能性があります。)つまり、十分な環境(フランシスコのポイント)が限られている場合、それは本当かもしれませんが、数値を確認したいと思います。
TJクラウダー2010

1
@Donal:他の多くの絶対ステートメントの場合と同様に。:-)
TJクラウダー2010

1
@ジョナス:ありがとう、でも日付(1998-11-02)とJVMバージョン(1.1.6)までたどり着いて、やめました。SunのJVMは、特にスレッド化の分野で、過去11.5 年間(そしておそらくErlangのインタプリタも同様に)かなり改善されています。(明確にするために、私は仮説が真実ではないと言っているわけではありません[そしてフランシスコとドナルは、アーランドがそこで何かをすることができるかもしれない理由を指摘しました];私はそれを額面どおりに受け取るべきではないと言っていますチェックされていません。)
TJクラウダー2010

1
@ジョナス:「...でもアーランでできると思う...」それは「推測」の部分だよ。:-) Erlangのプロセス切り替えが数千を超えてスケ​​ールアップしていることを推測しています。あなたはしている推測それはとても良くJavaやOSのスレッドよりもないこと。推測とソフトウェア開発は素晴らしい組み合わせではありません。:-)しかし、私は私の主張をしたと思います。
TJクラウダー2010

17
@TJ Crowder:erlangをインストールして実行erl +P 1000100 +hms 100し、タイプして{_, PIDs} = timer:tc(lists,map,[fun(_)->spawn(fun()->receive stop -> ok end end) end, lists:seq(1,1000000)]).、結果が出るまで約3分待ちます。それはとても簡単です。それは私のラップトップ上でプロセスごとに140usと1GBの全RAMを必要とします。しかし、それは直接シェルを形成するものであり、コンパイルされたコードからより良いはずです。
Hynek -Pichi- Vychodil 2010

回答:


113

いくつかの要因があります。

  1. ErlangプロセスはOSプロセスではありません。これらは、軽量の協調スレッドモデルを使用してErlang VMによって実装されます(Erlangレベルではプリエンプティブですが、協調的にスケジュールされたランタイムの制御下にあります)。これは、既知の制御されたポイントでのみ切り替わるため、CPU状態全体(通常、SSEおよびFPUレジスタ、アドレス空間マッピングなど)を保存する必要がないため、コンテキストを切り替えるほうがはるかに安価であることを意味します。
  2. Erlangプロセスは動的に割り当てられたスタックを使用します。スタックは非常に小さく始まり、必要に応じて大きくなります。これにより、使用可能なすべてのRAMを消費することなく、何千、さらには何百万ものErlangプロセスを生成できます。
  3. Erlangは以前はシングルスレッドでした。つまり、プロセス間でスレッドの安全性を確保する必要はありませんでした。現在はSMPをサポートしていますが、同じスケジューラ/コア上のErlangプロセス間の相互作用は非常に軽量です(コアごとに個別の実行キューがあります)。

6
2つ目のポイント:プロセスがまだ実行されていない場合は、スタックを割り当てる理由はありません。さらに:プロセスのGCをいじってメモリを収集しないようにすることで、いくつかのトリックをプレイできます。しかし、それは進んでいて、やや危険です:)
私は、

3
3つ目のポイント:Erlangは不変データを適用するため、SMPを導入してもスレッドセーフ性に影響はありません。
nilskp 2012年

@nilskp、そうです、erlangは関数型プログラミング言語でもあります。したがって、「変数」データはありません。これにより、スレッドセーフになります。
liuyang1 2013年

6
@nilskp:(RE:ポイント3についてコメントします…)言語自体には不変の型システムがありますが、基盤となる実装(メッセージパッシング、スケジューラーなど)はまったく別の話です。正確で効率的なSMPサポートは、スイッチを操作するだけでは実現できませんでした。
Marcelo Cantos 2013年

@rvirding:明確な補遺をありがとう。私はあなたのポイントを私の答えの本文に統合するために自由を取りました。
Marcelo Cantos 14

73

さらに調査を行った後、ジョーアームストロングのプレゼンテーションを見つけました。

Erlangから-並行世界のためのソフトウェア(プレゼンテーション)(13分):

[Erlang]は並行言語です。つまり、スレッドはプログラミング言語の一部であり、オペレーティングシステムに属していません。これは、JavaやC ++などのプログラミング言語の実際の問題です。スレッドはプログラミング言語ではなく、オペレーティングシステムにあるものであり、オペレーティングシステムに存在するすべての問題を継承しています。問題の1つは、メモリ管理システムの粒度です。 オペレーティングシステムのメモリ管理はメモリのページ全体を保護するため、スレッドの最小サイズはページの最小サイズです。 それは実際には大きすぎます。

あなたがあなたのマシンにメモリを追加した場合-あなたは、ページテーブルの粒度が上がるように、メモリを保護し、同じビット数を持っている - あなたは数百バイトで実行している知っているプロセスのために言うの64kBのを使用して終了します。

すべてではないにしても、少なくとも私の質問のいくつかはそれで答えると思います



2
スタック上のメモリ保護は、理由があります。Erlangは、プロセッサのMMUを介して異なる実行コンテキストのスタックを保護しないのですか?(そして、最高のものを期待していますか?)スレッドがその小さなスタック以上のものを使用している場合はどうなりますか?(より大きなスタックが必要かどうかを確認するために、すべてのスタック割り当てがチェックされていますか?スタックは移動可能ですか?)
Thanatos

2
@Thanatos:Erlangでは、プログラムがメモリにアクセスしたり、スタックをいじったりすることはできません。すべての割り当ては、ヒープとスタックの両方のマネージランタイムを経由する必要があります。つまり、ハードウェア保護は、とにかく起こり得ないことから保護するため、役に立たないのです。言語は、ポインタセーフ、スタックセーフ、メモリセーフ、およびタイプセーフです。スタックは必要に応じて大きくなるため、プロセスはその「小さなスタック」以上を使用できません。あなたはそれを小さなものの反対であると考えるかもしれません:無限に大きいです。(ただし、遅延して割り当てられます。)
JörgW Mittag

4
Microsoft ResearchのSingularity Operating Systemをご覧ください。Singularityでは、すべてのコード、カーネル、デバイスドライバー、ライブラリ、ユーザープログラムは、完全なカーネル特権でリング0で実行されます。すべてのコード、カーネル、デバイスドライバー、ライブラリ、およびユーザープログラムは、メモリ保護がまったくなく、単一のフラットな物理アドレス空間で実行されます。チームは、言語が行う保証がMMUが行う保証よりもはるかに強力であると同時に、MMUを使用するとパフォーマンスが最大30%(!!!)高くなることを発見しました。それで、あなたの言語がとにかくそれをすでにしているなら、なぜMMUを使うのですか?
イェルクWミッターク

1
OS / 400オペレーティングシステムも同じように動作します。すべてのプログラムに対して単一のフラットアドレススペースのみがあります。そして、今日実際に使用されているほとんどの言語は同じ安全特性を持っています(ECMAScript、Java、C♯、VB.NET、PHP、Perl、Python、Ruby、Clojure、Scala、Kotlin、Groovy、Ceylon、F♯、OCaml、 「Objective-C」の「Objective」部分、「C ++」の「++」部分)。レガシーCコード、およびC ++とObjective-Cのレガシー機能がなければ、仮想メモリは不要になります。
イェルクWミッターク

47

アセンブラーにコルーチンを実装し、パフォーマンスを測定しました。

最新のプロセッサーでは、コルーチン(別名Erlangプロセス)間の切り替えに約16命令と20ナノ秒かかります。また、切り替え先のプロセスをよく知っている(例:キューでメッセージを受信するプロセスは、呼び出しプロセスから受信プロセスへの直接のハンドオフとして実装できる)ので、スケジューラーは機能しません。これはO(1)操作です。

OSスレッドを切り替えるには、カーネルを呼び出すため、約500〜1000ナノ秒かかります。OSスレッドスケジューラはO(log(n))またはO(log(log(n)))時間で実行される場合があります。これは、数万または数百万のスレッドがある場合に顕著になります。

したがって、Erlangプロセスは高速であり、切り替えの基本的な操作が高速であり、スケジューラーの実行頻度も低いため、スケーリングが向上します。


33

Erlangプロセスは、他の言語のグリーンスレッドに(ほぼ)対応しています。プロセス間にOSによる強制分離はありません。(言語による分離もあるかもしれませんが、Erlangが他よりも優れた仕事をしているにもかかわらず、保護は低くなります。)それらは非常に軽量であるため、はるかに広範囲に使用できます。

一方、OSスレッドは、さまざまなCPUコアで単純にスケジュールすることができ、(ほとんど)独立したCPUバウンド処理をサポートできます。OSプロセスはOSスレッドに似ていますが、OSによる分離がはるかに強力です。これらの機能の代償は、OSスレッドと(さらにそうであれば)プロセスがより高価になることです。


違いを理解する別の方法はこれです。JVMの上にErlangの実装を書くつもりだとしたら(特におかしな提案ではありません)、各Erlangプロセスを何らかの状態のオブジェクトにすることになります。次に、Erlangプロセスを実行するスレッドインスタンスのプール(通常は、ホストシステムのコアの数に応じたサイズに設定されます。これは、実際のErlangランタイムBTWの調整可能なパラメーターです)です。次に、実行される作業を、使用可能な実際のシステムリソース全体に分散します。それは物事を行うのにかなりきちんとした方法ですが、完全に依存しています個々のErlangプロセスはそれぞれあまり機能しないという事実についてです。もちろんそれで問題ありません。Erlangは、プログラムを実行するのはそれらの全体的な集合体であるため、それらの個々のプロセスが重いものである必要がないように構成されています。

多くの点で、本当の問題は用語の1つです。Erlangがプロセスを呼び出すもの(およびCSP、CCS、特にπ計算の同じ概念に強く対応するもの)は、Cの遺産を持つ言語(C ++、Java、C#、および他の多く)プロセスまたはスレッドを呼び出します。いくつかの類似点はありますが(すべて同時実行の概念が含まれます)、同等のものはありません。だから誰かがあなたに「プロセス」を言うときは注意してください。彼らはそれを全く異なる何かを意味すると理解するかもしれません…


3
ErlangはPi微積分に近づきません。Pi微積分は、変数にバインドできるチャネル上の同期イベントを想定しています。この種の概念は、Erlangモデルにはまったく適合しません。微積分に参加してみてください。Erlangはこれに近いものですが、一部のメッセージなどにはネイティブに参加できる必要があります。それを実装したJErlang専用という論文(およびプロジェクト)がありました。
私はひどい助言を

それはすべて、パイ計算を正確にどのように見ているかに依存します(そして、同期チャネルとバッファープロセスで非同期チャネルをモデル化できます)。
ドナルフェロー

Erlangプロセスは軽量であると言っていますが、フットプリントが小さい(軽量である)理由と、OSスレッドよりもパフォーマンスが優れている理由を説明していません。
ジョナス

1
@ジョナス:一部のタイプのタスク(特に計算が多いタスク)では、OSスレッドの方が優れています。なお、これらは通常Erlangが使用されるタスクではありません。Erlangは、単純な通信タスクを多数持つことに重点を置いています。これを実行することの利点の1つは、作業の一部を処理して結果を待機するタスクのグループの場合、すべてが単一のプロセッサの単一のOSスレッドで実行できることです。これは、コンテキストスイッチを持っています。
ドナルフェロー

理論的には、非常に小さなスタックを使用し、割り当てられた他のスレッド固有のリソースの数を注意深く制御することにより、OSスレッドも非常に安価にすることができますが、実際にはかなり問題があります。(スタック要件を予測するのは少し難しいです。)そのため、OSスレッドは(CPUコアの数のオーダーの)スレッドが少なく、より重要な場合に最適になるように特に設計されています。それぞれの処理量。
ドナルフェロー

3

Jonasは、OSスレッドをErlangプロセスと比較する際にいくつかの数値を求めていたと思います。プログラミングアーランの作者であるジョーアームストロングは、しばらくの間、OSスレッドへのアーランプロセスの生成のスケーラビリティをテストしました。彼はErlangで単純なWebサーバーを作成し、マルチスレッドApacheに対してテストしました(ApacheはOSスレッドを使用しているため)。1998年にさかのぼるデータを含む古いウェブサイトがあります。私はそのサイトを正確に1回しか見つけることができませんでした。そのため、リンクを提供できません。しかし、情報はそこにあります。調査の主なポイントは、Apacheが手書きのErlangサーバーが10K以上のプロセスを処理している間に、Apacheが8Kプロセスのすぐ下に達したことを示しました。


5
私は、あなたはこの1つについて話していると思う:sics.se/~joe/apachevsyaws.htmlをしかし、私はアーランがkerlenlスレッドに比べスレッドはとても効率的にする方法を尋ねました。
ジョナス

@Jonasリンクは無効です。最後のスナップショットはこちら
アルバロg

1
「Apacheは約4,000の並列セッションで終了します。Yawsは依然として80,000を超える並列接続で機能しています。」
Nathan Long

citeseerx.ist.psu.edu/viewdoc/…にある記事全体を参照してください。実際、Apacheサーバーを停止するのは簡単でしたが、16台の攻撃マシンを使用してErlangサーバーを破壊することは不可能でした。
Bernhard

1

Erlangインタープリターはそれ自体を心配する必要があるだけなので、OSには他にも多くの心配事項があります。


0

その理由の1つは、erlangプロセスがOSではなくevm(erlang仮想マシン)で作成されるため、コストが小さくなることです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.