MVCフレームワーク(ストラットなど)を使用してJavaでWebアプリケーションを作成した経験が3年近くあります。私は主要な小売チェーン向けのコードを書いていますが、これまでマルチスレッドコードを書いたことはありません。
インタビュー中にマルチスレッドに関するいくつかの質問があり、通常はそれらに回答します(ほとんどが単純な質問です)。これにより、現在の業界シナリオでマルチスレッドがどれほど重要か疑問に思いました。
MVCフレームワーク(ストラットなど)を使用してJavaでWebアプリケーションを作成した経験が3年近くあります。私は主要な小売チェーン向けのコードを書いていますが、これまでマルチスレッドコードを書いたことはありません。
インタビュー中にマルチスレッドに関するいくつかの質問があり、通常はそれらに回答します(ほとんどが単純な質問です)。これにより、現在の業界シナリオでマルチスレッドがどれほど重要か疑問に思いました。
回答:
それは非常に重要です。
しかし、より重要なことは、マルチスレッドが非同期の問題を解決するための1つの方法にすぎないことを理解することです。多くの人々が現在ソフトウェアを作成している技術環境は、2つの重要な点で歴史的なソフトウェア開発環境(バッチ計算を実行するモノリシックアプリケーション)とは異なります。
現在、メニーコアマシンが一般的です。クロック速度やトランジスタ密度が桁違いに増加するとはもはや予想できません。計算の価格は下がり続けますが、多くの並列処理のために下がります。その力を活用する方法を見つける必要があります。
現在、コンピューターは非常にネットワーク化されており、最新のアプリケーションはさまざまなソースから豊富な情報を取得できることに依存しています。
計算の観点から見ると、これらの2つの要素は基本的に同じコアアイデアに要約されます。つまり、情報は非同期的に利用できるようになります。必要な情報がマシンの別のチップで計算されているのか、世界中のチップで計算されているのかは重要ではありません。いずれにせよ、あなたのプロセッサはそこに座って、有用な仕事をしている可能性があるときに情報を待つために 1秒間に数十億サイクルを燃やしています。
したがって、現在重要であり、将来さらに重要になるのは、それ自体がマルチスレッドではなく、非同期性に対処することです。マルチスレッドはそのための1つの方法にすぎません。複雑でエラーが発生しやすい方法ですが、弱いメモリモデルチップがより広く使用されるにつれて、より複雑でエラーが発生しやすくなります。
ツールベンダーにとっての課題は、お客様が将来使用する非同期インフラストラクチャに対処するために、マルチスレッドよりも優れた方法を考え出すことです。
concurrency
asynchronous
行動よりも重要です。並行性なしで非同期(つまり、単一のコアCPU上の複数のスレッド)asynchronous
を使用することは、セマンティックの代替ではありませんconcurrency
。
最近のプロセッサにはますます多くのコアが搭載されているため、これはますます重要になっています。10年前、既存のコンピューターのほとんどには単一のプロセッサしかなかったため、マルチスレッドはハイエンドサーバーアプリケーションでのみ重要でした。最近では、基本的なラップトップでさえマルチコアプロセッサを備えています。数年後にはモバイルデバイスでも...並行性の潜在的なパフォーマンスの利点を活用し、マルチスレッド環境で正しく実行するには、ますます多くのコードが必要になります。
一般に、マルチスレッドはすでに非常に重要であり、今後数年でさらに重要になります(PéterTörökが指摘)-近い将来にプロセッサがどのようにスケーリングするか(より高いMHzではなくより多くのコア) 。
ただし、あなたの場合、主にWebアプリケーションで作業しているようです。Webアプリケーションは、その性質上、Webサーバーが各ユーザーの要求を処理する方法(つまり、並列)によりマルチスレッド化されています。並行性とスレッドセーフ(特にキャッシュやその他の共有データを扱う場合)を理解することはおそらく重要ですが、内部でWebアプリケーションコードをマルチスレッド化するのが有益なケース(たとえば、複数のワーカーリクエストごとのスレッド)。その意味で、マルチスレッドの専門家であることは、Web開発者にとって本当に必要ではないと思います。インタビューではよく尋ねられますが、それは非常に難しいテーマであり、また多くのインタビュアーがそこに着く10分前にいくつかの質問をグーグルで検索するからです。
マルチスレッドは赤いニシンです。マルチスレッドは、実際の問題であるConcurrencyの実装の詳細です。すべてのスレッド化されたプログラムがロックのために並行処理されるわけではありません。
スレッドは、concurrent
プログラムを実装するための1つのモデルおよび実装パターンにすぎません。
たとえば、Erlangなどの言語でマルチスレッドを実行することなく、高度にスケーラブルでフォールトトレラントなソフトウェアを作成できます。
インタビュー中にマルチスレッドに関する質問がいくつかあります...
インタビューに合格するには、マルチスレッドが非常に重要かもしれません。自己を引用、「私たちのチームのために候補者を面接するとき、私は尋ねる同時実行の問題ではなく、これらのスキルは我々のプロジェクトにおいて重要である(これらは理由ではない)が、これらは何とか私たちが使用している言語の一般的な知識を評価するためにそれを容易にするので...」
短い答え:非常に。
より長い答え:電子(トランジスタベース)コンピューターは、テクノロジーの物理的限界に近づいています。発熱と微視的回路の量子効果を管理しながら、各コアからより多くのクロックを絞り出すことがますます難しくなっています(回路パスは、「量子トンネリング」と呼ばれる効果が電子を作ることができるように、最近のチップ上ですでに非常に近くに配置されています従来の電気アークの適切な条件を必要とせずに、ある回路から別の回路に「トラックをジャンプ」します。そのため、実質的にすべてのチップメーカーは、各CPUにより多くの「実行ユニット」を配置することにより、各クロックがより多くの処理を行えるようにすることに注力しています。その後、コンピューターがクロックごとに1つだけを実行する代わりに、2、4、または8を実行できます。Intelには「HyperThreading」、基本的に1つのCPUコアを2つの論理プロセッサに分割します(いくつかの制限があります)。事実上、すべてのメーカーが少なくとも2つの別個のCPUコアを1つのCPUチップに組み込んでおり、デスクトップCPUの現在のゴールドスタンダードはチップあたり4コアです。2つのCPUチップを使用すると8つ可能です。「クアッドクアッドコア」プロセッサ用に設計されたサーバーメインボード(16 EUとオプションのHT)があり、次世代のCPUにはチップあたり6または8が搭載される可能性があります。
これらすべての結果は、コンピューターが計算能力を獲得する方法を最大限に活用するには、コンピューターがプログラムを「分割して征服」できるようにする必要があるということです。マネージ言語には、プログラムとは別にメモリ管理を処理するGCスレッドが少なくとも1つあります。また、COM / OLE相互運用を処理する「遷移」スレッドもあります(管理された「サンドボックス」を保護するためのパフォーマンスと同じくらい)。ただし、それを超えて、プログラムが複数のことを同時に行う方法について考え始め、プログラムの一部を非同期で処理できるように設計された機能を使用してプログラムを設計する必要があります。WindowsおよびWindowsユーザーは、プログラムがバックグラウンドスレッドで長く複雑なタスクを実行することを実際に期待します。プログラムのUI(プログラムのメインスレッドで実行される)がWindowsメッセージループに「応答」するようにします。明らかに、並列化可能な解決策(ソートなど)を持つ問題は自然な候補ですが、並列化の恩恵を受ける問題の種類は限られています。
マルチスレッドに関する警告:スレッドが増えても、効率が向上するわけではありません。適切に管理されていないと、システムの速度が低下する可能性があります。Scalaのアクターは、Javaのスレッド化を改善し、システムの使用率を最大化します(Java開発者であると述べています)。
編集: マルチスレッドの欠点について留意すべきことがいくつかあります:
また、このリンクはほぼ同じように役立つ場合があります。
これにより、現在の業界のシナリオでマルチスレッドがどれほど重要か疑問に思いましたか?
パフォーマンスが重要なフィールドであり、パフォーマンスが重いコードを実行しているサードパーティのコードから来ているのではなく、私たち自身のものである場合、CPUの観点から重要度の高い順に物事を検討する傾向があります(GPUは私が勝ったワイルドカードです入らない):
リストは重要度だけでなく、メンテナンスへの影響、それらがどれだけ単純か(そうでない場合は、事前に検討する価値がある)、リスト上の他のユーザーとの相互作用など、他の多くのダイナミクスに基づいていることに注意してください
メモリ効率
ほとんどは、アルゴリズムよりもメモリ効率を選択したことに驚くかもしれません。これは、メモリ効率がこのリストの他の4つのすべての項目と相互作用するためです。また、多くの場合、「実装」カテゴリではなく「設計」カテゴリで考慮されるためです。メモリ効率を理解するには、リスト上の4つの項目すべてを考慮する必要があり、他の4つの項目もすべてメモリ効率を考慮する必要があるため、鶏肉または卵の問題が少しあります。しかし、それはすべての中心にあります。
たとえば、線形時間シーケンシャルアクセスと一定時間の後方への挿入を提供し、小さな要素以外には何も提供しないデータ構造が必要な場合、ここで到達する素朴な選択はリンクリストです。それはメモリ効率を無視しています。ミックスのメモリ効率を考慮すると、このシナリオでは、成長可能な配列ベースの構造またはより連続したノード(例:ノードに128個の要素を格納するノード)のように、または少なくともプールアロケーターによってリンクされたリンクリスト。これらは、同じアルゴリズムの複雑さにもかかわらず、劇的なエッジを持っています。同様に、メモリの効率のためにアルゴリズムの複雑さが劣っていても、マージソートよりも配列のクイックソートを選択することがよくあります。
同様に、メモリアクセスパターンが非常にきめ細かく、本質的に分散しているため、コードの最もきめ細かいレベルでロックしている間、誤った共有の量を最大化してしまうと、効率的なマルチスレッドを実現できません。したがって、メモリの効率はマルチスレッドの効率を倍増させます。スレッドを最大限に活用するための前提条件です。
リスト上の上記の各項目はすべてデータとの複雑な相互作用があり、データの表現方法に焦点を当てることは、最終的にメモリ効率の静脈内にあります。上記のいずれも、データを表現したりデータにアクセスしたりする不適切な方法でボトルネックになる可能性があります。
メモリ効率が非常に重要なもう1つの理由は、コードベース全体に適用できることです。一般的に、人々があちこちのちょっとした作業から非効率性が蓄積すると想像するとき、それはプロファイラーをつかむ必要があるという兆候です。しかし、低遅延フィールドまたは非常に限られたハードウェアを扱うフィールドは、プロファイリングの後でも、割り当て、コピー、メモリへのアクセス。通常、これは、コードベース全体が、コードベース全体に適用されるまったく新しい一連の標準につながる可能性のあるパフォーマンスの問題の影響を受けやすい場合に限られ、メモリ効率が中心になります。
アルゴリズム
ソートアルゴリズムの選択により、ソートに数か月かかる大量の入力とソートに数秒かかる入力を区別できるため、これはほぼ当然のことです。少なくとも1,000,000個のコアマシン(この場合はメモリ)効率がさらに重要になります)。
しかし、その分野の有能な人なら誰でも錐台カリングに加速構造を使用することを知っているので、それは私の個人リストの一番上にはありません。接頭辞ベースの検索の基数ツリーは赤ちゃんのものです。私たちが取り組んでいる分野のこの種の基本的な知識がなければ、アルゴリズムの効率は確かにトップに上がりますが、多くの場合、アルゴリズムの効率は些細なことです。
また、一部の分野では新しいアルゴリズムの発明が必要になる場合があります(例:メッシュ処理では、以前は存在しなかったため、または他の製品の同様の機能の実装は独自の秘密であり、論文で公開されていないため、数百を発明しなければなりませんでした)。ただし、問題解決の部分を過ぎて正しい結果を得る方法を見つけたら、効率が目標になると、実際にそれを得る唯一の方法は、データ(メモリ)とのやり取りを考慮することです。メモリ効率を理解しないと、新しいアルゴリズムは、単純化されたより洗練されたアルゴリズムを得るためにメモリ効率をもう少し考慮するだけで、高速化する無駄な努力で不必要に複雑になる可能性があります。
最後に、アルゴリズムはメモリ効率よりも「実装」カテゴリに属する傾向があります。多くの場合、最初から最適でないアルゴリズムを使用しても、後知恵で改善する方が簡単です。たとえば、劣悪な画像処理アルゴリズムは、多くの場合、コードベース内の1つのローカルな場所に実装されています。後でより良いものと交換することができます。ただし、すべての画像処理アルゴリズムがPixel
準最適なメモリ表現を持つインターフェイスに関連付けられているが、それを修正する唯一の方法が複数のピクセルの表現方法を変更することである場合(単一のピクセルではない場合)、 SOLと完全にコードベースを書き換える必要がありますImage
インタフェース。ソートアルゴリズムの置き換えについても同じことが言えます。通常は実装の詳細ですが、ソートされるデータの基になる表現やメッセージの受け渡し方法を完全に変更するには、インターフェイスの再設計が必要になる場合があります。
マルチスレッド
マルチスレッドは、ハードウェアの特性に応じたマイクロレベルの最適化であるため、パフォーマンスの観点では難しいものですが、ハードウェアは実際にその方向にスケーリングしています。すでに32コアのピアがあります(4コアしかありません)。
しかし、マルチスレッドは、目的がソフトウェアの高速化に使用される場合、おそらく専門家に知られている最も危険なマイクロ最適化の1つです。本質的に不確定であるため、競合状態は可能な限り最も致命的なバグです(デバッグコンテキスト以外の最も不便な時間に、開発者のマシンで数か月に1回しか表示されない場合があります)。そのため、特にマルチスレッドに関連するバグは、最も慎重なテストのレーダーの下でも簡単に飛ぶ可能性があるため、保守性とコードの潜在的な正確性に関して、おそらく最もマイナスの低下があります。
それにもかかわらず、それは非常に重要になっています。現在のコア数を考えると、メモリ効率のようなもの(常に100倍高速になることがあります)のようなものではない場合がありますが、コアはますます増えています。もちろん、100コアのマシンであっても、メモリ効率はリストの一番上に置きます。これがないと、スレッドの効率は一般に不可能だからです。プログラムはそのようなマシン上で100個のスレッドを使用できますが、効率的なメモリ表現とアクセスパターン(ロックパターンに結びつく)が不足しているため、まだ遅いです。
SIMD
SIMDは、レジスタが実際に広くなり、さらに広くなる計画があるため、少し厄介です。元々、64ビットMMXレジスタの後に、4つのSPFP操作を並行して実行できる128ビットXMMレジスタがありました。現在、8つの並列処理が可能な256ビットYMMレジスタがあります。また、512個のレジスタを16個並列に使用できるようにする計画が既にあります。
これらは、マルチスレッドの効率と相互作用し、増加します。ただし、SIMDは、マルチスレッドと同様に保守性を低下させる可能性があります。それらに関連するバグは、デッドロックや競合状態ほど再現や修正が難しいとは限りませんが、移植性は厄介であり、すべてのマシンでコードを実行できることを保証します(そして、ハードウェア機能に基づいて適切な命令を使用します)ぎこちない。
もう1つは、今日のコンパイラは通常、専門的に作成されたSIMDコードに勝るものではありませんが、単純な試みに簡単に勝るものです。それらは、手動で行う必要がなくなるまで、または少なくとも組み込み関数やストレートアセンブリコードを書くために手動で行う必要がなくなるまで改善される可能性があります(おそらく少し人間のガイダンス)。
繰り返しますが、ベクトル化処理に効率的なメモリレイアウトがないと、SIMDは役に立ちません。最終的には、1つのスカラーフィールドをワイドレジスタにロードして、1つの操作を行うだけです。これらすべての項目の中心にあるのは、メモリレイアウトへの依存が本当に効率的であることです。
その他の最適化
これらは、アルゴリズムの焦点を超えるだけでなく、パフォーマンスにわずかな影響を与える変更に向かっていることを示唆している場合、最近「マイクロ」と呼ぶようになります。
多くの場合、分岐予測の最適化を試みるには、アルゴリズムまたはメモリ効率の変更が必要です。たとえば、静的予測のヒントやコードの再配置だけでこれを試みた場合、そのようなコードの初回実行を改善するだけで、効果が疑わしい場合しばしば完全に無視できない。
パフォーマンスのためのマルチスレッドに戻る
とにかく、パフォーマンスコンテキストからのマルチスレッドはどれほど重要ですか?4コアのマシンでは、理想的には約5倍高速になります(ハイパースレッディングで得られるもの)。32個のコアを持つ同僚にとっては、それはかなり重要です。そして、今後数年間でますます重要になります。
とても重要です。しかし、メモリの効率がなければロックを控えめに使用できるようにしたり、誤った共有を減らしたりするために、問題に多数のスレッドを投げるだけでは役に立ちません。
パフォーマンス外のマルチスレッド
マルチスレッドとは、単純なスループットという意味でのパフォーマンスだけではありません。ユーザーへの応答性を向上させるために、スループットのコストを考えながら負荷のバランスをとったり、ユーザーが処理の完了を待たずにマルチタスクを実行したりできるようにする場合があります(例:ファイルのダウンロード中にブラウジングを続行します)。
そのような場合、ハードウェアを最大限に活用するのではなく、ユーザーエンドの設計に関するものであるため、マルチスレッドは上位に向かって(おそらくメモリ効率よりも)高くなることをお勧めします。多くの場合、インターフェイス設計と、このようなシナリオでコードベース全体を構築する方法を支配します。
大規模なデータ構造にアクセスするタイトループを単純に並列化するのではない場合、マルチスレッドは本当にハードコアな「設計」カテゴリに進み、設計は常に実装より優先されます。
そのため、これらの場合、マルチスレッド化を前もって考慮することは絶対に重要であり、メモリの表現とアクセスよりも重要です。
歴史的に、人々は手作業でマルチスレッドプログラミングを行うことに苦労しなければなりませんでした。すべてのコアコンポーネント(スレッド、セマフォ、ミューテックス、ロックなど)を直接操作する必要がありました。
これらのすべての努力の結果、単一のシステムに追加のcpusを追加することで、アプリケーションを拡張できました。この垂直スケーラビリティは、「購入できる最大のサーバー」によって制限されます。
今日、私はソフトウェア設計のためにより多くのフレームワークと異なる設計モデルを使用することへのシフトを見ています。MapReduceは、バッチ処理に焦点を合わせたこのようなモデルの1つです。
目標は水平方向のスケーリングです。大きなサーバーを購入する代わりに、標準サーバーを追加します。
つまり、マルチスレッドプログラミングを本当に理解することは非常に重要であるという事実が残っています。私は、誰かが競合状態を作成し、テスト中に奇妙なエラーに気付くまで競合状態が何であるかさえ知らない状況にありました。
私のマシンには8つのコアがあります。タスクマネージャーでは、60個のプロセスが実行されています。VSのように、最大98個のスレッドを使用するものもあります。Outlookは26を使用します。メモリ使用量の大部分は、これらのアイドルスレッドのそれぞれに割り当てられたスタックです。
Outlookが応答するのを待つ必要がないように、私は個人的に300コアのコンピューターが出てくるのを待っています。もちろん、それまでにOutlookは301スレッドを使用します。
マルチスレッドは、特定の時間にコンピューター上で唯一の重要なプロセスとなるシステム(計算エンジンなど)を構築する場合にのみ重要です。デスクトップアプリは、利用可能なすべてのコアを使い果たすことなく、おそらくユーザーに有利に働くでしょう。要求/応答モデルを使用するWebアプリは、本質的にマルチスレッドです。
これは、フレームワークと言語の設計者、およびバックエンドシステムプログラマにとって重要であり、アプリケーションビルダーにとってはそれほど重要ではありません。ただし、ロックや非同期コードの作成など、いくつかの基本的な概念を理解することはおそらく価値があります。