共有データとは対照的に、メッセージパッシングによるパフォーマンスの低下

StackOverflow https://stackoverflow.com/questions/1810313

質問

最近、ロックを使用せず、Erlang などのメッセージ パッシング アプローチを使用することが話題になっています。または、関数型プログラミングと関数型プログラミングのような不変データ構造の使用についても説明します。C++/Java。

しかし、私が懸念しているのは次の点です。

  1. 私の知る限り、Erlang はメッセージ配信を保証しません。メッセージが失われる可能性があります。メッセージの損失を心配しなければならない場合、アルゴリズムとコードが肥大化し、再び複雑になるのではありませんか?どのような分散アルゴリズムを使用する場合でも、メッセージの配信保証に依存してはなりません。
  2. メッセージが複雑なオブジェクトの場合はどうなるでしょうか?メッセージをコピーして送信する場合と、それを共有の場所(両方のプロセスがアクセスできるDBなど)に保管すると言うのでしょうか?
  3. 共有状態を完全になくすことは本当にできるのでしょうか?私はそうは思わない。たとえば、DB では、同じレコードにアクセスして変更する必要があります。そこではメッセージパッシングを使用できません。ロックするか、オプティミスティック同時実行制御メカニズムを想定して、エラー時にロールバックを行う必要があります。ムネシアはどのように機能しますか?
  4. また、常に同時実行性を気にする必要があるわけではありません。どのプロジェクトにも、同時実行性やトランザクションをまったく行う必要のない大規模なコードが含まれます (ただし、パフォーマンスと速度が懸念されます)。これらのアルゴリズムの多くは共有状態に依存します (これが、参照渡しまたはポインターが非常に役立つ理由です)。

この事実を考えると、Erlang などでプログラムを書くのは、これらのことを行うことができないため、面倒です。おそらく、それはプログラムを堅牢にするでしょうが、それは線形計画問題の解決や凸包の計算などの場合に当てはまります。パフォーマンスがより重要であり、不変性を強制するなどです。同時実行性やトランザクションと何の関係もないのにアルゴリズムを変更するのは不適切な決定です。そうじゃない?

役に立ちましたか?

解決

  1. これが現実:言語/プラットフォームに関係なく、この可能性を考慮する必要があります。分散世界(現実の世界)では、物事は失敗します。それと共に生きます。

  2. もちろん費用がかかります:宇宙には無料のものはありません。ただし、「ビッグオブジェクト」をシャトルする代わりに、別のメディア(ファイル、dbなど)を使用すべきではありません。通信パイプで?いつでも「メッセージ」を使用できます。 「大きなオブジェクト」を参照するにはどこかに保存されています。

  3. もちろんそうではありません。関数型プログラミング/ Erlang OTPの背後にある考え方は、「分離」"可能な限りエリアは「共有状態」でした。操作されます。さらに、共有状態が変化する場所を明確にマークすると、テスト容易性とトレーサビリティ。

  4. あなたはポイントを逃していると思います:特効薬のようなものはありません。 Erlangを使用してアプリケーションを正常に構築できない場合は、実行しないでください。システム全体の別の部分を別の方法で、つまり異なる言語/プラットフォームを使用していつでも使用できます。この点で、Erlangは他の言語と変わりません。適切なジョブに適切なツールを使用

覚えておいてください:Erlangは、並行非同期、および分散の問題を解決するように設計されています。たとえば、メモリの共有ブロックで効率的に動作するように最適化されていません...ゲームの共有ブロック部分で動作する nif 関数とのインターフェースを考慮しない限り:-)

他のヒント

実世界のシステムは常にハイブリッドです:現代のパラダイムは、実際には、可変データと共有状態を排除しようとは考えていません。

ただし、目的は、この共有状態への同時アクセスを必要としないことです。プログラムはコンカレントとシーケンシャルに分割でき、メッセージパッシングとコンカレントパーツの新しいパラダイムを使用できます。

すべてのコードが同じ投資を受けるわけではありません:スレッドが根本的に「有害と見なされる」という懸念があります。 Apacheのようなものには、従来のコンカレントスレッドが必要な場合があり、そのような重要な技術は、完全にコンカレントな共有状態で一掃できるように、数年にわたって慎重に洗練される可能性があります。オペレーティングシステムカーネルは、「どれだけ高価でも問題を解決する」もう1つの例です。意味があります。

高速だが壊れていることにはメリットがありません:ただし、新しいコード、またはあまり注目されないコードの場合、単にスレッドではない場合があります。安全であり、真の同時実行性を処理しないため、相対的な「効率」は無関係です。 1つの方法は機能し、1つの方法は機能しません。

テスト容易性を忘れないでください:また、テストにはどのような価値がありますか?スレッドベースの共有メモリ並行性は、単にテストできません。メッセージパッシングの並行性は。これで、1つのパラダイムをテストできますが、他のパラダイムはテストできないという状況になりました。だから、コードがテストされたことを知ることの価値は何ですか?他のコードがあらゆる状況で機能するかどうかさえ知らないことの危険性は?

質問にはいくつかの暗黙の仮定があります。すべてのデータが1つのマシンに適合し、アプリケーションが本質的に1つの場所にローカライズされていると仮定します。

アプリケーションが大きすぎて 1 台のマシンに収まらない場合はどうなりますか?アプリケーションが 1 台のマシンを超えた場合はどうなりますか?

アプリケーションが1つのマシンに適合し、1つのマシンが成長したらすぐにプログラミングする方法をプログラムする方法の1つを持ちたくありません。

フォールトトレラントなアプリケーションを作成したい場合はどうなりますか?何かをフォールトトレラントにするには、少なくとも 2 台の物理的に分離されたマシンが必要です そして共有なし。共有とデータベースについて話すときは、MySQLクラスターのようなものが物理的に分離されたマシンのデータの同期コピーを維持することにより、断層トレンスを正確に達成することを省略します。表面 - エルランはこれを公開します。

フォールト トレランスとスケーラビリティに対応するために、プログラミング方法を突然変更するべきではありません。

Erlang は主にフォールト トレラントなアプリケーションを構築するために設計されました。

マルチコアの共有データには独自の問題があります - 共有データにアクセスするとロックを取得する必要があります - グローバルロック(最も簡単なアプローチ)を使用すると、共有にアクセスしているときにすべてのコアを停止することになりますデータ。マルチコアでの共有データアクセスは、キャッシュの問題のために問題が発生する可能性があります。コアにローカルデータキャッシュがある場合、「他のプロセッサキャッシュで)「遠く」データにアクセスすると非常に高価になる可能性があります。

多くの問題が本質的に分散されており、データが同時に1か所で利用できることはありません。この種の問題は、Erlangの考え方によく適合しています。

分散設定では、「メッセージ配信の保証」は 不可能 - 宛先マシンがクラッシュした可能性があります。したがって、Erlangはメッセージ配信を保証することはできません - 別のアプローチが必要です - システムは、メッセージの配信に失敗したかどうか(ただし、リンクメカニズムを使用した場合にのみ)を指示します。

純粋な数のクランチの場合、エルランは適切ではありません - しかし、ハイブリッドシステムでは、エルランは計算が利用可能なプロセッサにどのように分布するかを管理するのに適しているため、エルランが問題の分布と断層に耐える側面を管理する多くのシステムが見られますが、問題自体は別の言語で解決さ​​れます。

および他の言語が使用されている

Erlangに対する誤解についてのコメント:

  • Erlangは、メッセージが失われないこと、および送信された順序で到着することを保証します。基本的なエラー状況は、マシンAがマシンBと通信できないことです。リンクがトリガーされ、システムノードダウンメッセージがそのリンクに登録されたプロセスに送信されます。静かにドロップされるものはありません。プロセスは「クラッシュ」します;スーパーバイザー(存在する場合)はそれらを再起動しようとします。
  • オブジェクトは変更できないため、常にコピーされます。不変性を確保する1つの方法は、他のアーランプロセスのヒープに値をコピーすることです。別の方法は、共有ヒープ内のオブジェクト、それらへのメッセージ参照を割り当て、単にそれらを変更する操作がないようにすることです。 Erlangは、パフォーマンスを最初に実現します!共有ヒープをガベージコレクションするためにすべてのプロセスを停止する必要がある場合、リアルタイムが低下します。 Javaに問い合わせてください。
  • Erlangには共有状態があります。 Erlangは誇りに思っていませんが、実用的です。一例として、ローカルプロセスレジストリがあります。これは、システムプロセスを再起動して古い名前を要求できるように、名前をプロセスにマップするグローバルマップです。 Erlangは、可能であれば共有状態を回避するように試みます。パブリックなETSテーブルも別の例です。
  • はい、Erlangが遅すぎる場合があります。これはすべての言語で発生します。 Javaが遅すぎる場合があります。 C ++が遅すぎる場合があります。深刻なSIMDベースのベクトル数学を開始するためにゲームのタイトループをアセンブリにドロップしなければならなかったからといって、それが重要なときに高速な唯一の言語であるため、すべてをアセンブリで記述する必要があるとは推測できません。重要なのは、優れたパフォーマンスを備えたシステムを作成できることであり、Erlangは非常にうまく管理しています。 yawsまたはrabbitmqのベンチマークをご覧ください。

あなたの事実はErlangに関する事実ではありません。 Erlangプログラミングが苦痛だと思ったとしても、他の人がそのおかげで素晴らしいソフトウェアを作成していることに気付くでしょう。 ErlangでIRCサーバーを作成するか、非常に並行して何かを作成する必要があります。二度とErlangを使用するつもりがないとしても、並行性について別の方法で考えることを学んだでしょう。しかし、もちろん、Erlangはとても簡単です。

Erlangを理解していない人は、それをひどく再実装する運命にあります。

さて、オリジナルはLispについてでしたが、...本当です!

  

たとえばDBでは、同じレコードにアクセスして変更する必要があります

ただし、これはDBによって処理されます。データベースのユーザーとして、クエリを実行するだけで、データベースは確実に独立して実行されます。

パフォーマンスに関しては、共有状態を排除するための最も重要なことの1つは、新しい最適化を有効にすることです。共有状態は特に効率的ではありません。同じキャッシュラインを介してコアが競合し、データはメモリに書き込まれる必要があります。そうしないと、データはレジスタまたはCPUキャッシュに留まる可能性があります。

多くのコンパイラの最適化は、副作用や共有状態がないことにも依存しています。

これらのことを保証するより厳密な言語では、Cのようなものよりも多くの最適化を実行する必要があると言えますが、コンパイラーがこれらの最適化をはるかに簡単に実装できるようになります。

シングルスレッドコードでは、同時実行の問題に類似した多くの懸念が生じます。最新のCPUはパイプライン化され、命令を順不同で実行し、サイクルごとに3〜4個実行できます。そのため、シングルスレッドプログラムであっても、コンパイラとCPUがどの命令をインターリーブして並列に実行できるかを判断できることが重要です。

  1. Erlangは、同期呼び出し用のスーパーバイザーとgen_serverコールバックを提供します。したがって、メッセージが配信されない場合、それについて知ることができます。 。
  2. 通常、プロセスが同じノードにある場合、メッセージ受け渡し言語はデータのコピーを最適化します。そのため、共有メモリを使用して行うことはできません。とにかく
  3. プロセスによって保持される状態は、再帰的な末尾呼び出しでプロセス自身に渡されることによって保持されます。また、もちろん、メッセージを介して渡される状態もあります。私はmnesiaをあまり使用しませんが、トランザクションデータベースであるため、操作をmnesiaに渡す(および返す)と、確実に処理が完了することが保証されます。
  4. ポートまたはドライバーを使用して、このようなアプリケーションをアーランに簡単に結び付けることができるのは、そのためです。最も簡単なのはポートであり、Unixパイプによく似ていますが、パフォーマンスはそれほど優れていないと思いますが...前述したように、通常、VM /コンパイラがメモリコピーアウトを最適化すると、メッセージの受け渡しは単にポインターの受け渡しになります。

正確を期すために、共有は、データを可能な限り正規化する方法です。即時に、変更を通知するメッセージを送信しますが、常にポーリングでバックアップします。メッセージはドロップ、複製、順序変更、遅延されます-依存しないでください。

速度が心配な場合は、まずシングルスレッドを実行し、昼光を調整します。次に、複数のコアがあり、作業を分割する方法を知っている場合は、並列処理を使用します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top