MXNet:Execution Engine

6737 ワード

Execution Engine


MXNetの実行エンジンは、深い学習や他の特定の分野の問題だけではない.逆に、依存関係に基づいて一連の機能操作を実行する汎用的な問題を解決するために設計されている.依存関係のある任意の2つの機能をシーケンス化する必要がある.システムの性能を向上するために、依存する機能が同時に実行できる.Note on Dependency Engineも参照できます.

Interface


実行エンジンのコアインタフェースは次のとおりです.
virtual void PushSync(Fn exec_fun, Context exec_ctx,
                      std::vector const& const_vars,
                      std::vector const& mutate_vars) = 0;

このAPIユーザは、1つの関数(exec_fun)を、そのコンテキスト情報および依存関係pushとともに実行エンジンに関連付ける.exec_ctxは、exec_funが実行するコンテキスト環境である.const_varsは、関数が読み取り権限のみの変数を表す、mutate_varsは、関数が修正可能な変数を表す.具体的な詳細を考慮せずに、エンジン保証の次のルールを実行します.
いずれかの2つが同じ変数を修正する関数は、それらのpushからエンジンへの順序に従ってシーケンス化される.

Function


エンジンを実行するために必要な関数のタイプは、次のように定義されます.
using Fn = std::function<void(RunContext)>;
RunContextには、エンジンによって決定されたランタイム情報が含まれています.
struct RunContext {
    // stream pointer which could be safely cast to
    // cudaStream_t* type
    void *stream;
};

ユーザは、第2の選択肢としてmxnet::engine::DAGEngine::Fnの定義を用いるもよく、それらの種類は同じである.
すべての関数はengine内部のスレッドによって実行されます.このモデルでは、エンジン(通常、ハードディスクの読み取り、ウェブサービス、UIなどのI/Oタスクを処理する関数)にブロック関数pushを実行することをユーザに奨励する.ブロック関数は実行スレッドを占有するとともに、このシステムのスループットを低減するためである.この場合、別のasynchronous関数タイプを提供します.
using Callback = std::function<void()>;
using AsyncFn = std::function<void(RunContext, Callback)>;
AsyncFnの関数では、ユーザは、関数の実行の終了を待つことなく、重要な計算を自分のスレッドに渡すことができる.非同期関数のCallbackが呼び出されない限り、エンジンは関数が終了するかどうかを考慮しない.

Context


ユーザは、関数の実行に必要なContextを指定することができる.このContextは、一般に、CPUまたはGPU上で実行するかどうかの関数を含む、GPUである場合、具体的にはどのGPUであるかを示す.ContextRunContextは異なる.Contextはデバイスタイプ(gpu/cpu)とデバイスidを含むが、RunContextは、実行時にのみ決定可能な情報、例えば関数がどのstream上で実行するかを含む.

VarHandle

VarHandleは、関数の依存関係を指定するために用いる.MXNet実行エンジンの設計目的は、インタフェースとMXNetの他のモジュールとの結合を解除することである.したがって、VarHandleと同様のエンジンは、関数の必要または変更される外部リソースを表すトークンをユーザに提供する.軽量レベルに設計されているため、変数を作成、削除、コピーするにはわずかなコストしかかかりません.エンジンにプッシュする関数については、ユーザは、const_vars vectorに必要な可変変数を指定し、mutate_vars vectorに修正される変数を指定する必要がある.エンジン解析関数間の依存関係を実行する唯一のルールは、次のとおりです.
*いずれかの2つが同じ変数を修正する関数は、それらのpushからエンジンへの順序に従ってシーケンス化される.
例えば、Fn1およびFn2の両方がV2を修正する場合、Fn2の後にFn1のpushがエンジンに到達すると、Fn2Fn1の後に実行されることが保証する.一方、Fn1およびFn2の両方が使用するが、V2を変更しない場合、それらの具体的な実行順序は任意である.
このような設計により、エンジンが状態変更(state-mutating)動作をスケジューリングすることができる.例えば、DNNにおける重み更新の関数は、毎回新たな重み配列を生成するのではなく、+=でその場で重みを更新することができる.
変数を作成する場合は、NewVar() APIを使用します.変数を削除するには、PushDelete APIを使用します.

Push & Wait


すべてのPush APIは非同期である.API呼び出しは、Fnが実行するかどうかにかかわらず、呼び出し後すぐに戻る.これにより、実行エンジンは、ユーザのスレッドが関数をエンジンにプッシュするときにすぐに計算を開始することができる.すべてのPush APIはthread-safeではありません.具体的には、APIを呼び出すには1つのスレッドしかないはずである.
特定のFnが完了するのを待つには、callback関数を含んで、Fnの最後に前のコールバック関数を呼び出す必要があります.
特定の変数の使用(読み取り/修正)のFnをすべて待つ場合は、WaitForVar(var) APIを呼び出す必要があります.
エンジンにプッシュされたFnがすべて終了するのを待つ場合は、WaitForAll() APIを呼び出す必要があります.

Save Object Creation Cost


いくつかの関数をエンジンに何度もプッシュする必要がある場合があります.これらの関数の計算が軽量レベルであると、Lambda式のコピーと、読み取り/書き込み変数のリストの作成にかかるオーバーヘッドの割合がかなり高くなる.OprHandleを事前に作成するためのAPIを提供します.
virtual OprHandle NewOperator(AsyncFn fn,
                              std::vector const& const_vars,
                              std::vector const& mutate_vars) = 0;

これにより、作成するたびにOprHandleをプッシュすることができます.
virtual void Push(OprHandle op, Context exec_ctx) = 0;
DeleteOperator(OprHandle op)を呼び出すことで削除することができる.ただし、実行済みであることを確認してください.

API Reference

.. doxygenclass:: mxnet::Engine
   :members:

テキストリンク:https://github.com/dmlc/mxnet/blob/master/docs/zh/architecture/dep_engine.md