PCIeによる共同処理
Neat PCIe ホスト API を使用すると、ホストマシン上のアプリケーションからテンソルまたは画像を接続された Modalix PCIe カードに送信し、推論結果を受信できます。ホストマシンがアプリケーションの I/O とオーケストレーションを担当し、カードがコンパイルされたモデルと、それに設定された前処理または後処理を実行する場合に使用します。
これは、DevKit 上で直接実行される Neat Library とは別の API です。
パブリックな型は、simaai::neat::pcie C++ 名前空間と pyneatpcie Python パッケージにあります。
ホストマシンにcore/pciehostをインストールしてください。Neat SDKコンテナ内やModalix PCIeカード上にはインストールしないでください。このページを使用する前に、PCIeホストをインストールします。の手順に従ってください。
コプロセッシングの仕組み
1つのpcie::Modelは、1つの物理的なPCIeキューで実行される1つのコンパイル済みモデルを表します。
- コンストラクタは、ローカルのモデルアーカイブを読み込み、その入力と出力を公開します。 契約書を出力します。
build()は、アーカイブを PCIe 仮想ネットワーク経由でカードにアップロードします。 カード側のパイプラインを開始し、準備が完了するまで待ちます。run()またはpush()を実行すると、入力ペイロードが PCIe 経由で送信されます。- このカードは、前処理、推論、および設定された後処理を実行します。
run()またはpull()は、出力テンソルをホストに返します。close()は、カード側のパイプラインを停止し、キューを解放します。
モデルアーカイブは、build() の過程で転送されます。推論ペイロードと結果は、PCIeデータ転送を使用します。
接続を設定します。
ConnectionOptions は、このモデルで使用されるカードとキューを識別します。
| フィールド | デフォルト | 目的 |
|---|---|---|
card_host | 空の | 明示的な SSH/SCP アドレス。空の場合、カード N 使用します 10.0.N.2. |
card_id | 0 | カード番号がホスト PCIe プラグインに渡されました。 |
user | sima | カード側の SSH および SCP を使用するユーザー。 |
queue | 0 | 共同処理キュー。0 から 3 まで。 |
max_inflight | 10 | 結果を待機中の、最大許容入力数。 |
1枚のカードに対しては、デフォルト設定を使用してください。 10.0.0.2 キュー0に設定。 card_host
カードが異なる管理アドレスを使用する場合、明示的に指定します。
#include <simaai/neat/pcie/Model.h>
namespace pcie = simaai::neat::pcie;
pcie::ConnectionOptions connection;
connection.card_host = "10.0.0.2";
connection.card_id = 0;
connection.queue = 0;
connection.max_inflight = 10;
モデルを検査し、構築する。
構築はローカルで行われ、カードの処理は開始されません。入力を割り当てる前に、info() を確認し、その後、共同処理セッションを開始するために、build() を一度呼び出します。
pcie::Model model("model.tar.gz", {}, connection);
const pcie::ModelInfo info = model.info();
for (const auto& input : info.inputs) {
std::cout << input.name << " requires " << input.size_bytes << " bytes\n";
}
model.build(/*readiness_timeout_ms=*/180000);
input_specs()とoutput_specs()は、それぞれ同じリストを返します。
running()は、正常にビルドが完了するとtrueになり、close()後にfalseに戻ります。
同期推論を実行します。
最も単純なリクエスト/レスポンスの流れには、run() を使用してください。まずモデルを構築し、アプリケーションの障害時に無限に待機しないように、有限のタイムアウトを設定してください。
以下の例は、報告された入力データ型が FP32 であるモデルの入力を作成します。
const auto& input_spec = info.inputs.front();
if (input_spec.dtype != "FP32") {
throw std::runtime_error("this example requires an FP32 model input");
}
std::vector<float> values(input_spec.size_bytes / sizeof(float), 0.0f);
pcie::Tensor input = pcie::Tensor::from_vector(
std::move(values), input_spec.shape, input_spec.name);
pcie::TensorList outputs = model.run(input, /*timeout_ms=*/30000);
model.close();
複数の入力を持つモデルの場合、論理的な入力ごとに1つのTensorを、info().inputsによって報告された順序とルート名で渡します。
run()のタイムアウトは、待機を停止しますが、カードによってすでに受け入れられた入力の処理をキャンセルするわけではありません。タイムアウトが発生した後、残っている結果を処理するためにpull()を使用するか、新しいリクエストシーケンスを開始する前にclose()を呼び出します。
プッシュとプルを使用したパイプラインのリクエスト
入力の準備と推論を並行して行う必要がある場合は、push()とpull()を使用してください。
max_inflightは、まだ完了していない処理の最大数を制限します。結果を速やかに取得し、処理を継続できるようにします。
std::size_t pushed = 0;
std::size_t pulled = 0;
while (pulled < inputs.size()) {
while (pushed < inputs.size() && pushed - pulled < 10) {
model.push(inputs[pushed++]);
}
auto outputs = model.pull(/*timeout_ms=*/30000);
if (!outputs) {
throw std::runtime_error("PCIe inference timed out");
}
consume(*outputs);
++pulled;
}
push() は、max_inflight が上限に達するまで待機します。そのため、結果を取得する前に、設定されたウィンドウを超えるデータを送信しないでください。pull() は、このモデルで利用可能な次の結果を返します。run() を呼び出す前に、push() で送信されたすべての結果を処理してください。