Java(Strutsのような)MVCフレームワークを使用して)でWebアプリケーションを作成した経験は3年近くあります。主要な小売チェーン向けのコードを書いたことがありますが、今までマルチスレッドコードを書いたことがありません。
インタビュー中にマルチスレッディングに関するいくつかの質問を受け、通常はそれらに回答します(ほとんどが単純な質問です)。これは、現在の業界シナリオでマルチスレッドがどれほど重要であるか疑問に思いました。
それは非常に重要です。
しかし、より重要なことは、マルチスレッドは非同期問題を解決するための1つの方法にすぎないことを理解することです。現在多くの人々がソフトウェアを作成している技術環境は、(バッチ計算を実行するモノリシックアプリケーションの)歴史的なソフトウェア開発環境とは主に2つの点で異なります。
メニーコアマシンが一般的になりました。クロック速度やトランジスタ密度が桁違いに増加することはもはや予想できません。計算の価格は下がり続けますが、多くの並列処理のために下がります。その力を利用する方法を見つける必要があります。
コンピュータは現在、高度にネットワーク化されており、最新のアプリケーションは、さまざまなソースから豊富な情報を取得できることに依存しています。
計算の観点から見ると、これらの2つの要素は基本的に同じ核となるアイデアに要約されます:情報は非同期で利用できるようになります。必要な情報がマシン内の別のチップで計算されているのか、それとも世界中のチップで計算されているのかは、それほど重要ではありません。いずれにせよ、プロセッサーは1秒あたり数十億サイクルを燃やしている情報を待っているそれが有用な作業をしている可能性があるとき。
つまり、現在重要なこと、そして将来さらに重要なことは、マルチスレッド自体ではなく、非同期処理です。マルチスレッディングは、そのための1つの方法にすぎません。メモリモデルの弱いチップが広く使用されるようになると、複雑でエラーが発生しやすくなるだけで、より複雑でエラーが発生しやすくなります。
ツールベンダーにとっての課題は、顧客が将来使用する非同期インフラストラクチャに対処するために、マルチスレッドよりも何らかの方法betterを考え出すことです。
最近のプロセッサのコア数が増えるにつれて、その重要性はますます高まっています。 10年前、既存のコンピューターのほとんどに単一のプロセッサしかなかったため、マルチスレッド化はハイエンドサーバーアプリケーションでのみ重要でした。今日では、基本的なラップトップでさえマルチコアプロセッサを備えています。数年後にはモバイルデバイスでも...同時実行の潜在的なパフォーマンス上の利点を使用し、マルチスレッド環境で正しく実行するには、ますます多くのコードが必要になります。
一般的に、マルチスレッド化はすでに非常に重要であり、今後数年間でさらに重要になるでしょう(PéterTörökが指摘したように)-プロセッサが予測可能な将来に向けて拡張する方法です(より高いMHzではなくより多くのコア) 。
ただし、あなたの場合は、主にWebアプリケーションを使用しているようです。 Webアプリケーションは、その性質上、Webサーバーが各ユーザーの要求を(つまり、並行して)処理する方法により、マルチスレッド化されています。同時実行性とスレッドセーフを理解することはおそらく重要ですが(特にキャッシュやその他の共有データを扱う場合)、内部でWebアプリケーションコードをマルチスレッド化することが有益である場合(つまり、複数のワーカー)が多すぎるとは思わないでしょう。リクエストあたりのスレッド数)。その意味では、マルチスレッドの専門家であることは、Web開発者にとって本当に必要ではないと思います。これはインタビューでよく聞かれます。これは、かなり扱いにくい主題であり、多くの面接担当者は、到着する10分前にいくつかの質問をググるだけだからです。
マルチスレッディングは赤いニシンです。マルチスレッドは、実際の問題であるConcurrencyの実装の詳細です。ロックが原因で、スレッド化されたすべてのプログラムが並行しているわけではありません。
スレッドは、concurrent
プログラムを実装するための1つのモデルと実装パターンにすぎません。
たとえば、Erlangなどの言語でマルチスレッドを実行しなくても、高度にスケーラブルでフォールトトレラントなソフトウェアを作成できます。
インタビュー中にマルチスレッドについていくつか質問があります...
インタビューに合格するには、マルチスレッドが非常に重要になる可能性があります。 自己引用 、 "私たちのチームの候補者に面接するとき、私は並行性の質問をします。なぜなら、これらのスキルは私たちのプロジェクトで重要だからです(これらはではない)ですが、使用する言語の一般的な知識を評価するのが簡単になるためです...」
スレッドを活用してパフォーマンスを向上させる方法を理解することは、ほとんどの業界とアプリケーションにとって、今日のソフトウェア環境における重要なスキルです。
少なくとも、並行性に関連する問題を理解することは当然のことです。
たとえば、多くの組み込みシステムでは、すべてのアプリケーションまたは環境がそれを利用できるわけではないことは明らかです。ただし、Atomプロセッサ(その他)がそれを変更するように機能しているようです(軽量マルチコアがより一般的になり始めています)。
すでにマルチスレッドコードを記述しているようです。
ほとんどのJava Webアプリケーションは、同時に複数の要求を処理でき、複数のスレッドを使用してこれを行います。
したがって、少なくとも基本を知っていることが重要だと思います。
短い答え:とても。
より長い答え:電子(トランジスターベース)コンピューターは、テクノロジーの物理的限界に急速に近づいています。発熱と微細回路の量子効果を管理しながら、各コアからより多くのクロックを押し出すことがますます難しくなっています(回路パスは、「量子トンネリング」と呼ばれる効果が電子を作ることができるほど現代のチップ上ですでに非常に接近して配置されています)従来の電気アークの適切な条件を必要とせずに、ある回路から別の回路に「トラックをジャンプ」する)。そのため、事実上すべてのチップメーカーは、代わりに各CPUにより多くの「実行ユニット」を配置することにより、各クロックがより多くのことを実行できるようにすることに焦点を合わせています。その後、コンピューターは1クロックで1つの処理を実行する代わりに、2、4、または8を実行できます。Intelには、基本的に1つのCPUコアを2つの論理プロセッサーに分割する(いくつかの制限があります)「ハイパースレッディング」があります。事実上すべてのメーカーが少なくとも2つの個別のCPUコアを1つのCPUチップに組み込んでおり、デスクトップCPUの現在のゴールドスタンダードはチップあたり4コアです。 2つのCPUチップを使用すると8つ可能です。「クアッドクアッドコア」プロセッサ(16 EUおよびオプションのHT)用に設計されたサーバーメインボードがあり、次世代のCPUはチップあたり6または8を搭載する可能性があります。
これらすべての結果、コンピューターが計算能力を獲得する方法を最大限に活用するには、コンピューターがプログラムを「分割して征服」できるようにする必要があります。マネージ言語には、少なくともプログラムとは別にメモリ管理を処理するGCスレッドがあります。一部には、COM/OLE相互運用を処理する「遷移」スレッドがあります(パフォーマンスと同様に、管理対象の「サンドボックス」を保護するために)。それを超えて、しかし、あなたは本当にあなたのプログラムがどのように複数のことを同時に行うことができるかについて考え始める必要があり、そしてプログラムの一部を非同期で処理できるように設計された機能であなたのプログラムを設計しなければなりません。 WindowsおよびWindowsユーザーは、プログラムがバックグラウンドスレッドで長く複雑なタスクを実行することを実際に期待します。これにより、プログラムのUI(プログラムのメインスレッドで実行されます)がWindowsメッセージループに「応答」します。明らかに、並列化可能な解決策(並べ替えなど)を持つ問題は自然な候補ですが、並列化の恩恵を受ける有限種類の問題があります。
それは必要な状況では依然として重要ですが、開発中の多くのものと同様に、適切な作業に適したツールです。私はスレッディングに触れることなく3年間行きましたが、実際には、私が行うすべてのことにはいくつかの根拠があります。マルチコアプロセッサでは、スレッド化の必要性は依然として高いですが、従来の理由はすべて有効であり、レスポンシブなインターフェイスが必要であり、同期を処理し、他のことを一度に実行できるようにしたいと考えています。
これにより、現在の業界シナリオでマルチスレッディングがどれほど重要であるか疑問に思いました。
パフォーマンスが重要なフィールドでは、負荷のかかるサードパーティのコードではパフォーマンスが得られないが、私たち自身のものでは、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レジスタが表示されています。そして、16を並列に許可する512ビットレジスタの計画がすでに用意されています。
これらは、マルチスレッディングの効率と相互作用し、増大します。ただし、SIMDはマルチスレッドと同じくらい保守性を低下させる可能性があります。それらに関連するバグは、デッドロックや競合状態ほど再現および修正することが必ずしも難しいわけではありませんが、移植性は厄介であり、コードがすべての人のマシンで実行できるようにすること(およびハードウェア機能に基づいて適切な命令を使用すること)は、ぎこちない。
もう1つは、今日のコンパイラは通常、巧妙に記述されたSIMDコードに勝るものはありませんが、簡単な試みに勝るものはありません。それらは、手動で行う必要がなくなるか、少なくとも組み込み関数やまっすぐなアセンブリコード(おそらく、わずかな人間のガイダンス)を記述するほど手動で行う必要がなくなるまで改善される可能性があります。
繰り返しになりますが、ベクトル化された処理に効率的なメモリレイアウトがなければ、SIMDは役に立ちません。結局、1つのスカラーフィールドをワイドレジスタにロードして、1つの演算を実行するだけです。これらすべてのアイテムの中心にあるのは、真に効率的なメモリレイアウトへの依存です。
その他の最適化
これらのことは、アルゴリズムの焦点を超えるだけでなく、パフォーマンスへの影響が非常に小さい変更をWordが示唆している場合に、今日「マイクロ」と呼び始めることをお勧めします。
多くの場合、分岐予測のために最適化しようとすると、アルゴリズムやメモリ効率の変更が必要になります。静的予測のためのヒントとコードの再配置だけでこれを試みると、そのようなコードの初回実行が改善される傾向にあり、その効果は疑いなく無視できない場合があります。
パフォーマンス向上のためにマルチスレッドに戻る
とにかく、パフォーマンスのコンテキストからのマルチスレッドはどのくらい重要ですか?私の4コアマシンでは、理想的には約5倍高速になります(ハイパースレッディングで得られるもの)。 32コアの同僚にとっては、これはかなり重要です。そして、それは今後数年でますます重要になるでしょう。
したがって、それはかなり重要です。しかし、メモリの効率が悪く、ロックの使用を控えめにしたり、誤った共有を減らしたりすることができない場合は、問題に大量のスレッドを投げるだけでは意味がありません。
パフォーマンス以外のマルチスレッド
マルチスレッディングは、単純なスループットの種類の意味で常に純粋なパフォーマンスを示すとは限りません。場合によっては、可能なスループットコストで負荷を分散させてユーザーへの応答性を向上させたり、ユーザーが物事が完了するのを待たずに、より多くのマルチタスクを実行できるようにします(例:ファイルをダウンロードしながら閲覧を続ける)。
そのような場合、ハードウェアを最大限に活用することではなく、ユーザーエンドの設計に関するものであるため、マルチスレッドは上に向かって(おそらくメモリ効率より上に)高くなることをお勧めします。このようなシナリオでは、多くの場合、インターフェース設計と、コードベース全体を構築する方法が支配的になります。
大規模なデータ構造にアクセスするタイトループを単純に並列化するのではない場合、マルチスレッド化は非常にハードな「設計」カテゴリに進み、設計は常に実装よりも優先されます。
したがって、これらのケースでは、メモリの表現とアクセス以上に、マルチスレッディングを事前に検討することが絶対的に重要だと思います。
マルチスレッドについての警告:スレッドが増えても、効率が向上するわけではありません。適切に管理されていないと、システムの速度が低下する可能性があります。 Scalaのアクターは、Javaのスレッディングを改善し、システムの使用を最大化します(Java開発者であるため、それについて言及しています)。
編集:マルチスレッドの欠点について、次の点に注意してください。
また、 このリンク も同じように役立つ場合があります。
並行および並列プログラミングが重要になってきています。スレッドは、同時に複数の処理を実行するプログラミングモデルの1つにすぎません(かつてマルチコアプロセッサが登場する前のように、疑似並列ではありません)。スレッドは多くのリソースを共有し、プログラマーはそれらを協調させる責任があるため、マルチスレッドは(かなり公平に)複雑で危険であると批判されています。そうしないと、デバッグが困難なデッドロックが発生します。
歴史的に人々はマルチスレッドプログラミングを手作業で行うことに苦労しなければなりませんでした。それらはすべてのコアコンポーネント(スレッド、セマフォ、ミューテックス、ロックなど)を直接操作する必要がありました。
これらすべての取り組みの結果、単一のシステムにCPUを追加することでアプリケーションを拡張できました。この垂直方向のスケーラビリティは、「私が購入できる最大のサーバー」によって制限されます。
今日では、ソフトウェア設計に、より多くのフレームワークとさまざまな設計モデルを使用する方向へのシフトが見られます。 MapReduceは、バッチ処理に焦点を当てたそのようなモデルの1つです。
目標は水平方向にスケーリングすることです。より大きなサーバーを購入する代わりに、標準サーバーを追加します。
それでも、マルチスレッドプログラミングを本当に理解することは非常に重要であるという事実は変わりません。私は、誰かが競合状態を作成し、テスト中に奇妙なエラーに気づくまで、競合状態が何であるかさえ知らない状況にありました。
多くの外部アプリケーションに接続する必要がある場合があるため、外部システムとのやり取りに時間がかかり、プロセスが完了するまでエンドユーザーが待機できないバックグラウンドプロセスが発生する可能性があります。マルチスレッドが重要です。
アプリで使用しています。ダウンしている場合は、最初に外部システムに接続してから、リクエストをデータベースに保存し、スレッドをスパンしてバックグラウンドでプロセスを終了します。バッチ操作でも必要になる場合があります。