web-dev-qa-db-ja.com

マルチスレッドジョブキューマネージャー

インタラクティブなアプリケーションでCPUを大量に消費するマルチタスク可能なジョブを管理する必要があります。背景と同じように、私の特定のアプリケーションはエンジニアリング設計インターフェースです。ユーザーがモデルのさまざまなパラメーターとオプションを微調整すると、複数のシミュレーションがバックグラウンドで実行され、結果は完了時に表示されます。ユーザーがまだ値を編集している場合でも同様です。複数のシミュレーションにはさまざまな時間がかかるため(ミリ秒、5秒、10分)、基本的にはフィードバックをできるだけ早く表示する必要がありますが、以前に開始したが現在は不要になったジョブを中止することがよくあります。ユーザーの変更のうち、すでに無効になっています。ユーザーの変更が異なると、計算が無効になる可能性があるため、いつでも10の異なるシミュレーションを実行できます。一部のシミュレーションには、依存関係のある複数の部分があります(シミュレーションAとBは別々に計算できますが、シミュレーションCをシードするためにそれらの結果が必要なので、Cを開始する前にAとBの両方が最初に終了するのを待つ必要があります)。

この種のアプリケーションを処理するためのコードレベルの方法は、ある種のマルチスレッドジョブキューであると私はかなり確信しています。これには、実行のためのジョブの送信、タスクの優先順位の設定、ジョブの終了の待機、依存関係の指定(このジョブを実行しますが、ジョブXとジョブYの終了後のみ)、一部の基準に一致するジョブのサブセットのキャンセル、クエリの実行などの機能が含まれます。ジョブは残り、ワーカースレッド数と優先順位の設定などが行われます。また、マルチプラットフォームのサポートも非常に便利です。

これらはソフトウェアの新しいアイデアや要望ではありませんが、アプリケーションの設計の初期段階にあり、そのようなタスクを管理するために使用するライブラリを選択する必要があります。私は過去にCで自分の粗いスレッドマネージャーを書いたことがありますが(通過儀礼だと思います)、以前のハックではなく、最新のツールを使用して作業を行いたいと思っています。

最初に考えたのは OpenMP まで実行することですが、それが私が望むものかどうかはわかりません。 OpenMPは、細かいレベルでの並列化、ループの自動展開などに最適です。マルチプラットフォームであると同時に、#pragmasを使用してコードに侵入します。ただし、ほとんどの場合、大規模なタスクの管理用には設計されていません。特に、保留中のジョブのキャンセルや依存関係の指定はできません。はい、可能ですが、エレガントではありません。

Google Chromeは、最も些細なタスクでもこのようなジョブマネージャーを使用します。 設計目標は、ユーザーインタラクションスレッドをできるだけ軽くて機敏に保つことであるようです。 Chromeソースを見ると、これは一般的なライブラリではないようですが、デザインがどのようになっているのかを見るのは興味深いことです。非同期起動を使用して対話を高速に保ちます。これは私が行っていることと同様になりつつあります。

さらに他のオプションがあります:

Surge.Act: ジョブを定義するためのBoostのようなライブラリ。これはOpenMPに基づいて構築されていますが、依存関係の連鎖を可能にします。これは素晴らしいことです。質問したり、仕事をキャンセルしたりできるマネージャーがいるような気がしません。古いプロジェクトなので、頼るのは怖いです。

ジョブキュー 私が考えているものに非常に近いですが、これは5年前の記事であり、サポートされているライブラリではありません。

Boost.threads プラットフォームに依存しない同期がありますが、それはジョブマネージャーではありません。 [〜#〜] poco [〜#〜] は、タスクを起動するための非常にクリーンなデザインですが、タスクを連鎖させるための完全なマネージャーではありません。 (多分私はPOCOを過小評価しています)。

ですから、利用できるオプションはありますが、私は満足しておらず、自分のライブラリをもう一度ロールしたいという衝動を感じています。しかし、私はむしろすでに存在しているものを使用したいと思います。 (ここではSOとネット上で)検索した後でも、これはしばしば必要とされる一種のツールであるに違いないと思いますが、正しいと感じるものは何も見つかりませんでした。確かにいくつかあります。コミュニティライブラリまたは少なくとも一般的なデザイン。SOいくつかの 投稿ジョブキュー がありますが、適合しないようです。

ここでの私の投稿は、私が見逃した既存のツール、および/またはそのようなマルチスレッドジョブキューをどのようにロールしたかをすべて尋ねることです。

35
Marc Ditto

あなたと同様の要件を満たすために、独自のジョブキューシステムを構築する必要がありました(UIスレッドは常に33ミリ秒以内に応答する必要があり、ジョブは15〜15000ミリ秒で実行できます)。 。

残念ながら、私たちのコードはプロプライエタリと同じくらいプロプライエタリですが、最も顕著な機能のいくつかを提供できます。

  • プログラムの開始時に、コアごとに1つのスレッドを開始します。それぞれがグローバルジョブキューから作業をプルします。ジョブは、関数オブジェクトと関連データのグロブで構成されます(実際にはfunc_ptrとvoid *の詳細)。高速クライアントループであるスレッド0はジョブでの作業を許可されていませんが、残りは可能な限り取得します。
  • ジョブキュー自体は、 ロックフリーの単一リンクリスト (Visual Studio 1つに付属 )などのロックレスデータ構造である必要があります。ミューテックスの使用は避けてください。キューの競合は驚くほど高く、ミューテックスの取得にはコストがかかります。
  • ジョブに必要なすべてのデータをジョブオブジェクト自体にパックします。ジョブからメインヒープにポインタを戻さないようにします。メインヒープでは、ジョブとロックの間の競合や、その他の遅くて煩わしいものすべてに対処する必要があります。たとえば、すべてのシミュレーションパラメータはジョブのローカルデータblobに入れる必要があります。結果の構造は明らかにジョブよりも長持ちするものである必要があります。これは、a)実行が終了した後でもジョブオブジェクトにぶら下がる(メインスレッドからコンテンツを使用できるようにする)か、b)のいずれかで対処できます。ジョブごとに特別に結果構造を割り当て、ジョブのデータオブジェクトにポインタを詰め込みます。結果自体はジョブ内に存在しませんが、これにより、ジョブに出力メモリへの排他的アクセスが効果的に与えられるため、ロックをいじくり回す必要はありません。

  • 実際、私は少し上を単純化しています。どのジョブがどのコアで実行されるかを正確に振り分ける必要があるため、各コアは独自のジョブキューを取得しますが、それはおそらく不要です。

17
Crashworks

Boost.threadsに基づいて自分でロールしました。非常に小さなコードを書くことでどれだけの価値が得られたかに、私は非常に驚いていました。既製のものが見つからない場合は、自分でロールすることを恐れないでください。 Boost.threadsと独自に作成してからの経験の間では、覚えているよりも簡単かもしれません。

事前に作成されたオプションについては、 Chromium が非常に使いやすいライセンスであるため、独自のジェネリックライブラリをそのコードにロールバックできる可能性があることを忘れないでください。

5
Ryan Graham

Microsoftは、Concurrency Runtime、Parallel Pattern Library、およびAsynchronous AgentsLibraryと呼ばれるVisualStudio 2010の次のバージョンの一連のテクノロジに取り組んでおり、おそらく役立つでしょう。 Concurrency Runtimeは、ポリシーベースのスケジューリングを提供します。つまり、複数のスケジューラインスタンスを管理および構成できます(スレッドプールに似ていますが、インスタンス間のアフィニティ化と負荷分散を備えています)。並列パターンライブラリは、タスクベースのプログラミングとSTLを使用した並列ループを提供します。プログラミングモデル。エージェントライブラリは、アクターベースのプログラミングモデルを提供し、同時データフローパイプラインの構築、つまり上記の依存関係の管理をサポートしています。残念ながら、これはまだリリースされていないので、 チームブログ で読むか、いくつかのビデオを見ることができます channel9 利用可能な非常に大きなCTPもありますダウンロードも。

今日の解決策を探しているなら、Intelのスレッドビルディングブロックとboostのスレッドライブラリはどちらも優れたライブラリであり、現在利用可能です。 JustSoftwareSolutions は、C++ 0xドラフトに一致するstd :: threadの実装をリリースしました。もちろん、きめ細かいループベースの並列処理を検討している場合は、OpenMPが広く利用可能です。

他の人々がほのめかしている本当の課題は、作業を正しく識別して同時実行に適したタスクに分解し(つまり、保護されていない共有状態がない)、それらの間の依存関係を理解し​​、ボトルネックで発生する可能性のある競合を最小限に抑えることです(ボトルネックが- 共有状態の保護 またはワークキューのディスパッチループの競合が少ないかロックフリーであることを確認します)...そして、実装の詳細がコードの残りの部分に漏れるのをスケジュールせずにこれを行います。

-リック

4
Rick

threadpool のようなものはあなたに役立ちますか?これはboost :: threadsに基づいており、基本的に、ワーカー関数をプールされたスレッドに渡す単純なスレッドタスクキューを実装します。

3
greyfade

フローベースプログラミング -非同期コンポーネント間でストリーミングされるデータチャンクに基づいています。 JavaバージョンとC#バージョンのドライバーに加えて、いくつかのプリコーディングされたコンポーネントがあります。これは本質的にマルチスレッドです。実際、タイミング制約を追加することはできますが、シングルスレッドコードはコンポーネント内にあります。標準のスケジューリングルールに従います。必要なレベルには細かすぎる場合がありますが、ここで使用できるものがある場合があります。

2
Paul Morrison

Intel Thread Building Blocks をご覧ください。私はそれがあなたが望むことをすることを信じており、バージョン2ではオープンソースです。

1
Shane Powell

そこにはたくさんの分散リソースマネージャーがあります。ほぼすべての要件を満たすソフトウェアは Sun Grid Engine です。 SGEは、世界最大のスーパーコンピューターのいくつかで使用されており、活発に開発されています。

TorquePlatform LSF 、および Condor にも同様のソリューションがあります。

あなたはあなた自身を転がしたいと思うかもしれないように聞こえますが、上記のすべてにたくさんの機能があります。

1
AdamK

boost :: future (ただし、これも参照してください discussion および proposal )は、並列処理の非常に優れた基盤のように見えます(特にC-depends-on-A-and-Bタイプの状況に対して優れたサポートを提供しているようです)。

私はOpenMPを少し見ましたが、(あなたのように)Fortran/C数値コード以外ではうまくいくとは確信していませんでした。 Intelの スレッディングビルディングブロック 私にはもっと面白そうだった。

それに関して言えば、boost :: threadの上に 自分でロールする するのはそれほど難しいことではありません。 [説明:スレッド ファーム (ほとんどの人はそれをプールと呼びます)は、ファンクター(タスクまたはジョブ)のスレッドセーフ--​​ キュー から作業を引き出します。使用例については、 tests および benchmark を参照してください。優先度のあるタスクを(オプションで)サポートすること、およびタスクを実行するとより多くのタスクがワークキューに生成される可能性がある場合(これにより、すべての作業が実際にいつ完了するかを知ることが少し問題になります。「保留中」への参照)、さらに複雑になります。ケースに対処できるものです)。とにかくあなたにいくつかのアイデアを与えるかもしれません。]

1
timday

私はほぼ同じ要件を探していました。私は4xっぽいメカニズムでゲームに取り組んでおり、行われることのさまざまな部分をスケジュールすることで、私の脳はほとんど爆発しました。プレーヤーがアクティブにロードしたシステム/リージョンに応じて、さまざまな時間解像度で、さまざまな程度の実際のシミュレーションを実行する必要がある複雑な一連の作業があります。つまり、プレーヤーがシステム間を移動するときに、システムを現在の高解像度シミュレーションにロードし、最後のシステムを低解像度シミュレーションにオフロードし、システムのアクティブ/非アクティブ領域に対して同じことを行う必要があります。さまざまなシミュレーションは、各エンティティのプロファイルに基づく人口、政治、軍事、および経済活動の大きなリストです。これまでの私の問題とアプローチについて説明しようと思います。あなたや他の誰かのために代替案を説明するのに役立つことを願っています。私が構築している構造の大まかな概要は、以下を使用します。

  1. cpp-taskflow (最新のC++並列タスクプログラミングライブラリ)ジョブ構築パーツとして使用されるモジュールのライブラリを作成します。各エントリには、初期化と破棄のためのAPIと、通信用のポインタがあります。 cpp-taskflow APIを使用してネストできるように記述し、ジョブの作成時にすべての依存関係を設定しますが、ライブ調整の手段を提供し、キルスイッチを使用できるようにしたいと考えています。私が作成しているもののほとんどは、ステートマシンのディシジョンツリー、またはビヘイビアツリーのステートマシンであるため、ジョブデータ構造は、実際の統計とオブジェクト値を指す時間分解能タグ付きデータの設定と状態になります。
  2. FlatBuffers このライブラリを使用して、「ジョブリストエントリ」と「オブジェクトラッパー」システムを構築したいと考えています。ジョブキューの各エントリは、実行する必要のある作業(モジュールの設定)を記述し、実行する必要のある作業のデータ(またはデータへの共有ポインタ)を含むフラットバッファオブジェクトになります。オブジェクトストレージフラットバッファには、エンティティテーブルを表すデータが含まれます。私にとって、実際のデータのほとんどは、決定/作業が必要な配列になります。また、スレッド間の通信/制御チャネルとしてフラットバッファーを使用することも検討しています。私は、他のすべての人が通信するマスター「ルーター」スレッドを作成するか、それぞれが独自のスレッドを含み、何らかの発見メカニズムを持っていることに悩まされています。
  3. SQLite アクティブなリージョン/システムのみがより高い解像度の作業を行う必要があるため、ゲームが作成するバックグラウンドジョブリストの一部(数千のシステムとそのエンティティ用)はかなり大きく、長寿命になります。数十万から数百万のジョブ(私の頭の中では大きい)で、それぞれが完了するまでに未知の時間を必要とします。私の場合、彼ら全員がそうする限り(長いキャンペーン)、彼らがいつ終わるかは気にしません。各スレッドがメモリ内のsqliteデータベースのテーブルをジョブキューとして取得することを計画しています。各エントリには、フラットバッファ作業のblob、完了時に通知するバッファへのポインタ、更新用の制御バッファへのポインタ、およびジョブアイテム(場所、データ範囲、優先度)を装飾するその他のフィールドが含まれます。ジョブエントリは新しいジョブを作成し、アイテムがデータベースに消費されるときに作成されます。これにより、ジョブ間の関係関係を作成し、ジョブをやり直したり更新したり、ジョブとその依存関係を削除したり、優先度や依存関係を更新/並べ替えたりする必要がある場合に、クエリを作成することができます。これはすべてsqliteデータベースで使用されているため、いつでもすべてをディスクにダンプして後でリロードしたり、ディスクへの接続とディスクからの処理に切り替えたりすることができます。さらに、これにより、通常はさまざまな種類のコンテナーが必要になる多くの検索および順序付けのアルゴリズム作業にアクセスできます。 SQLクエリを使用できることで、ジョブを処理するための多くのオプションが得られます。

通信キュー(データベースとして)は、対応するスレッドを介してアクセスする必要があるか(各スレッドには独自のメッセージングデータベースが含まれ、モジュールAPIにはアクセス用に抽象化されたロック/ミューテックスがあります)、またはすべてを持っているかどうかについて私が引き裂かれています更新、追加/削除、およびいくつかのマスタールータースレッドを介した通信を1つの大きなデータベースに。ミューテックスとロックに関して、どれが頭痛の種を最小限に抑えるかはわかりません。数日で、スバッファープールとルックアップテーブルへの共有ポインターのモンスタースパゲッティビーストを作成しました。そのため、各スレッドには独自のバッファーがあり、バッファーを分離していました。その時、私はsqliteを維持しながら巨大なリストをオフロードすることにしました。次に、他のすべてのフラットバッファオブジェクトをテーブルにフィードしないのはなぜかと思いました。

各モジュールのほとんどすべてをdbに含めることで、作業が必要なデータのビューを表すSQLステートメントを記述したり、データの処理方法をオンザフライでピボットしたりできます。ジョブ自体がデータベースにあるということは、それらに対しても同じことができることを意味します。 SQLiteにはマルチスレッドアクセスがあるため、SQLiteをマルチスレッドジョブキューマネージャーとして使用するのはそれほど難しいことではありません。

要約すると、Cpp-Taskflowを使用すると、依存関係の連鎖とジョブプールのマルチスレッドを使用して複雑なネストされたループを設定できます。箱から出して、必要な構造のほとんどが付属しています。 FlatBuffersを使用すると、ジョブ宣言とオブジェクトラッパーを作成して、1つの作業単位としてストリームバッファーに簡単にフィードし、ジョブスレッド間で渡すことができます。また、SQLiteを使用すると、ストリームバッファージョブにタグを付けてblobエントリにキューに入れることができます。これにより、最小限の作業で追加、検索、注文、更新、削除が可能になります。また、保存と再読み込みも簡単にできます。スナップショットとロールバックも実行可能である必要があります。データベースのイベントの順序と解決に頭を悩ませておく必要があります。

編集:しかし、これを一粒の塩で取ってください、私はCrashworksが説明したことを達成しようとしているので、あなたの質問を見つけました。アフィニティを使用して長寿命のスレッドを開き、マスタースレッドにCpp-Taskflow階層の作業の大部分を実行させ、他のスレッドにジョブを供給することを考えています。私はまだジョブキュー/制御通信のsqlite手法を使用していません。これは、これまでの私の計画です。

誰かがこれがお役に立てば幸いです。

0
KindaSorta

あなたがC++ライブラリを探しているかどうかはわかりませんが(私はあなたがそうだと思います)、DougLeaのJava 7のフォーク/結合フレームワークはかなり気の利いたものであり、あなたとまったく同じことをしますおそらく、C++で実装するか、事前に実装されたライブラリを見つけることができるでしょう。

詳細はこちら: http://artisans-serverintellect-com.si-eioswww6.com/default.asp?W1

0
Itay

おそらくパンチに少し遅れていますが、ThreadWeaverも見てください: http://en.wikipedia.org/wiki/ThreadWeaver

0
user133281