メインコンテンツまでスキップ

モデルのベンチマーク

項目値
カテゴリモデルと推論
難易度初級
推定所要時間5-10 minutes
ラベルbenchmark, synthetic, latency, throughput, power

第001章と第002章では、モデルを一度実行する方法、および非同期で実行する方法を示しました。この章では、次の実践的な質問に答えます。「このモデルはデバイス上でどのくらいの速度で実行されるか?」ベンチマークAPIは、意図的に小さく設計されています。モデルをロードし、測定するサンプル数を指定し、benchmark(...)を呼び出し、返されたBenchmarkReportを読み取ります。

ベンチマークでは、モデルのinput_specs()を使用して、決定的な合成入力を生成します。これにより、モデルの簡単なベンチマークテストや、コンパイルされたモデルのバリアントを比較するのに役立ちますが、カメラベンチマークではありません。カメラのデコード、実際のプリプロセスにおける変動、動的な入力サイズ、またはデータに依存する後処理の動作は含まれません。

ウォークスルー​

モデルのロード​

以前のモデルチュートリアルで使用したのと同じコンパイルされた.tar.gzアーカイブから開始します。ベンチマークでは、モデルによって宣言された入力仕様から合成テンソルを作成するため、画像は必要ありません。

アーカイブパスからsimaai::neat::Modelを構築します。

tutorials/003_benchmark_your_model/benchmark_your_model.cpp
simaai::neat::Model model(model_path);

ベンチマークの実行​

benchmark(samples)を呼び出します。このAPIは、モデルをウォームアップし、入力を1つずつ実行してレイテンシを測定した後、別の非同期プッシュ/プルのパスでスループットを測定し、概要を標準出力に出力し、同じ主要な値をBenchmarkReportとして返します。

各パスは、少なくともsamples個の入力を測定し、かつ10秒が経過するまで続くため、スループット測定パスの電力と温度の値は複数のサンプルにわたります。この下限を変更するにはBenchmarkOptions::min_duration_msを設定します。0に設定すると、各パスがsamples個の入力の後で停止する簡単なスモークテストになります。

BoxDecodeで終わる検出モデルは、BenchmarkOptionsも使用できます。original_width、original_height、およびresize_modeを設定して、BoxDecodeがモデル座標から検出をマッピングするときに使用するソース画像のジオメトリを記述します。合成テンソルはモデルの形状のままです。

simaai::neat::BenchmarkOptions options;
options.num_samples = 100;
options.original_width = 1920;
options.original_height = 1080;
options.resize_mode = simaai::neat::ResizeMode::Letterbox;
auto report = model.benchmark(options);

Pythonは、pyneat.BenchmarkOptionsを通じて、同じフィールドを公開します。元の両方の次元を設定するか、両方を省略します。省略した場合、ベンチマークは解決されたモデルのルートからジオメトリを推測します。各実行のベンチマークジオメトリは、ModelOptions内の非推奨のBoxDecodeジオメトリよりも優先されます。

tutorials/003_benchmark_your_model/benchmark_your_model.cpp
simaai::neat::BenchmarkOptions options;
options.num_samples = samples;
options.include_mla_layer_stats = has_flag(argc, argv, "--include-mla-layer-stats");
options.include_mla_stage_stats = has_flag(argc, argv, "--include-mla-stage-stats");
simaai::neat::BenchmarkReport report = model.benchmark(options);
if (report.latency_ms <= 0.0 || report.fps <= 0.0)
throw std::runtime_error("benchmark produced no measured latency/fps");

レポートを読む​

返されるレポートは、ほとんどのユーザーが必要とする主要なフィールドから始まります。これには、ミリ秒単位の平均エンドツーエンドのレイテンシ、1秒あたりのフレーム数で表されるスループット、ワット単位のSoMの平均電力とピーク電力、およびsamples個の入力の処理に必要なジュール単位のエネルギーが含まれます。throughput_duration_msは、スループット測定パスの実行時間を示します。

電力は、NeatのインストーラーがセットアップするボードのテレメトリデーモンであるSentinelから取得されます。report.telemetryには、スループット測定パスのレール電力、ダイとボードの温度、CPU使用率、MLAメモリも含まれます。各フィールドについては、モデルの実行を測定するを参照してください。Sentinelが実行されていない場合でも、ベンチマークはレイテンシとスループットを報告し、電力フィールドをゼロのままにして、report.telemetry.statusにその理由を設定します。

tutorials/003_benchmark_your_model/benchmark_your_model.cpp
std::cout << "report_latency_ms=" << report.latency_ms << "\n";
std::cout << "report_fps=" << report.fps << "\n";
std::cout << "report_avg_power_watts=" << report.avg_power_watts << "\n";
std::cout << "report_energy_joules=" << report.energy_joules << "\n";
std::cout << "report_peak_power_watts=" << report.peak_power_watts << "\n";
std::cout << "report_throughput_duration_ms=" << report.throughput_duration_ms << "\n";
std::cout << "report_telemetry_status=" << report.telemetry.status << "\n";

上級: MLA レイヤー統計とステージ統計​

2 つのオプトインオプションは、ベンチマークの後に mla-rt を実行し、コンパイル済み MLA コードの時間を測定します。どちらも既定では無効で、他の MLA ワークロードが動作していないアイドル状態の devkit が必要です。チュートリアルのコマンドに --include-mla-layer-stats または --include-mla-stage-stats を追加するか、API で次のように有効にします。

simaai::neat::BenchmarkOptions options;
options.include_mla_layer_stats = true;
options.include_mla_stage_stats = true;
auto report = model.benchmark(options);
options = pyneat.BenchmarkOptions()
options.include_mla_layer_stats = True
options.include_mla_stage_stats = True
report = model.benchmark(options)

report.mla_layer_stats.layers から、コンパイル済みの各 MLA レイヤーのゼロ始まりの stage_index、name、latency_us を読み取ります。これは、そのレイヤーだけを実行する 1 回のプロファイリング実行で測定したハードウェア実行時間です。実行時間がゼロの no-op エントリも保持されます。レイヤーのレイテンシを合計しても、ステージのレイテンシや全体のレイテンシとは一致しません。

report.mla_stage_stats.stages から、各 MLA ステージの stage_index と latency_us を読み取ります。これはそのステージを中断なしで 200 回実行したときのハードウェア時間の平均です。ステージのレイテンシには、MLA の演算と、プログラムによる DRAM の読み書きが含まれます。モデルの読み込み、ホストと EV74 の処理、パイプラインのオーバーヘッドは含まれません。

mla-rt が見つからない、失敗する、またはタイムアウトした場合でも、benchmark() は主要な測定値を返し、リストを空のままにして、report.mla_layer_stats.status または report.mla_stage_stats.status に理由を設定します。それ以外の場合、各ステータスは "not requested" または "ok" です。mla-rt の実行が失敗、タイムアウト、またはクラッシュした後は、MLA に未完了の処理が残っている可能性があるため、同じプロセスでのそれ以降のプロファイリングはその理由とともにスキップされます。MLA の回復を確認するまで、DevKit をアイドル状態に保ってください。

MLA レイヤー統計は選択した終端ポリシーに従い、選択した推論ルート内の MLA ステージだけを測定します。各ステージの stage_index はモデル全体での番号のままです。

tutorials/003_benchmark_your_model/benchmark_your_model.cpp
if (options.include_mla_layer_stats) {
std::cout << "mla_layer_stats.status=" << report.mla_layer_stats.status << "\n";
for (const auto& layer : report.mla_layer_stats.layers) {
std::cout << " stage_index=" << layer.stage_index << " name=" << layer.name
<< " latency_us=" << layer.latency_us << "\n";
}
}
if (options.include_mla_stage_stats) {
std::cout << "mla_stage_stats.status=" << report.mla_stage_stats.status << "\n";
for (const auto& stage : report.mla_stage_stats.stages) {
std::cout << " stage_index=" << stage.stage_index << " latency_us=" << stage.latency_us
<< "\n";
}
}

レポートの保存​

report.save(path) を呼び出すと、要求した場合は MLA レイヤー統計とステージ統計も含め、完全なレポートを YAML または JSON で保存できます。

auto path = report.save("results/model/yolo_benchmark.yaml");
auto json_path = report.save("results/model/yolo_benchmark.json");
path = report.save("results/model/yolo_benchmark.yaml")
json_path = report.save("results/model/yolo_benchmark.json")

拡張子が .json の場合は JSON、.yaml またはその他の末尾の場合は YAML を使用します。ファイル名は変更しません。

ファイル名を含むパスを指定してください。存在しない親ディレクトリは作成され、既存のファイルは置き換えられ、書き込まれたパスが返されます。保存は明示的に行います。benchmark() はレポートファイルを自動作成しません。

どちらのチュートリアルコマンドにも --report results/model/yolo_benchmark.yaml を追加できます。--include-mla-layer-stats または --include-mla-stage-stats と組み合わせると、主要な測定値と MLA の実行時間を一緒に保存できます。

tutorials/003_benchmark_your_model/benchmark_your_model.cpp
std::string report_path;
if (get_arg(argc, argv, "--report", report_path)) {
std::cout << "report_path=" << report.save(report_path).string() << "\n";
}

実行​

実行すると、benchmark()によって出力されるベンチマークの概要が表示され、その後に返されたレポートから同じ値が出力されます。Neatのインストールルート(share/とlib/が含まれるディレクトリ)から、Pythonと**C++(事前にビルドされたもの)**コマンドを実行します。ソースからビルドするコマンドは、リポジトリのルートから実行します。

C++ (prebuilt):

./lib/sima-neat/tutorials/tutorial_003_benchmark_your_model \
--model /tmp/resnet_50.tar.gz --samples 100

C++ (build from source):

./build.sh --all --no-doc
./build/tutorials/tutorial_003_benchmark_your_model \
--model /tmp/resnet_50.tar.gz --samples 100

予想される出力(正確な数値は、モデル、ボード、および現在の負荷によって異なります。C++ビルドでは、末尾に[OK]行も出力されます)。

NEAT Benchmark
Input: synthetic
Samples: 100
Compiled batch: 1
Measurement detail: e2e+throughput-only
Latency: 1.97 ms
FPS: 1457.04 inferences/s
Batch throughput: 1457.04 batches/s
Throughput window: 30.09 s, 43849 inputs
Power: 13.19 W avg, 13.81 W peak (SoM)
MLA power: 3.05 W avg, 3.25 W max
Energy: 0.91 J for 100 inputs
Die temp: 46.34 °C avg, 47.56 °C max
CPU usage: 12.38 % avg, 13.22 % max
MLA memory: 29.65 MiB
Linux memory: 682.88 MiB avg, 697.41 MiB max
Telemetry: Sentinel main:766066b74f49, 15 samples
report_latency_ms=1.96852
report_fps=1457.04
report_avg_power_watts=13.1875
report_energy_joules=0.905085
report_peak_power_watts=13.8125
report_throughput_duration_ms=30094.5
report_telemetry_status=ok
[OK] 003_benchmark_your_model

この章のC++ソースを、カスタムのCMakeLists.txtを使用して独自のプロジェクトに統合する方法(追加のフォルダーは不要)については、ランディングページにあるチュートリアルの実行方法を参照してください。

実践​

コンパイルされたモデルアーカイブが動作するかどうか、測定された非同期スループットはどの程度か、そしてこのターゲットにおける主要なボードの消費電力はどの程度か、といった簡単な答えが必要な場合は、このベンチマークを使用してください。

アプリケーションのパフォーマンスについては、実際のパイプラインもベンチマークしてください。合成モデルの入力は意図的に安定しているため、カメラの揺れ、コーデックのコスト、実際のプリプロセス、負荷時のホストのスケジューリング、または下流のアプリケーションロジックを表すものではありません。手動で構築した非同期実行によるキューの深さとバックプレッシャーの調整については、スループットとキューの深さの調整を参照してください。

Model::benchmark()には、具体的なinput_specs()の次元が必要です。入力の形状が動的であるか、または具体的なものでない場合、ベンチマークは形状を推測するのではなく、明確に失敗します。

完全なソース​

完全なソースプログラムを表示
tutorials/003_benchmark_your_model/benchmark_your_model.cpp
// Benchmark a compiled model with deterministic synthetic inputs.
//
// Usage:
// tutorial_003_benchmark_your_model --model /path/to/model.tar.gz [--samples 100]
// [--report results/model/yolo_benchmark.yaml] [--include-mla-layer-stats]
// [--include-mla-stage-stats]

#include "neat.h"

#include <iostream>
#include <stdexcept>
#include <string>

namespace {

bool has_flag(int argc, char** argv, const std::string& key) {
for (int i = 1; i < argc; ++i) {
if (key == argv[i])
return true;
}
return false;
}

bool get_arg(int argc, char** argv, const std::string& key, std::string& out) {
for (int i = 1; i + 1 < argc; ++i) {
if (key == argv[i]) {
out = argv[i + 1];
return true;
}
}
return false;
}

int parse_int_arg(int argc, char** argv, const std::string& key, int def) {
std::string value;
if (!get_arg(argc, argv, key, value))
return def;
return std::stoi(value);
}

} // namespace

int main(int argc, char** argv) {
try {
std::string model_path;
if (!get_arg(argc, argv, "--model", model_path)) {
std::cerr << "Usage: tutorial_003_benchmark_your_model --model <path> [--samples <n>] "
"[--report <file>] [--include-mla-layer-stats] [--include-mla-stage-stats]\n";
return 1;
}
const int samples = parse_int_arg(argc, argv, "--samples", 100);

// CORE LOGIC
simaai::neat::Model model(model_path);

simaai::neat::BenchmarkOptions options;
options.num_samples = samples;
options.include_mla_layer_stats = has_flag(argc, argv, "--include-mla-layer-stats");
options.include_mla_stage_stats = has_flag(argc, argv, "--include-mla-stage-stats");
simaai::neat::BenchmarkReport report = model.benchmark(options);
if (report.latency_ms <= 0.0 || report.fps <= 0.0)
throw std::runtime_error("benchmark produced no measured latency/fps");

std::cout << "report_latency_ms=" << report.latency_ms << "\n";
std::cout << "report_fps=" << report.fps << "\n";
std::cout << "report_avg_power_watts=" << report.avg_power_watts << "\n";
std::cout << "report_energy_joules=" << report.energy_joules << "\n";
std::cout << "report_peak_power_watts=" << report.peak_power_watts << "\n";
std::cout << "report_throughput_duration_ms=" << report.throughput_duration_ms << "\n";
std::cout << "report_telemetry_status=" << report.telemetry.status << "\n";
if (options.include_mla_layer_stats) {
std::cout << "mla_layer_stats.status=" << report.mla_layer_stats.status << "\n";
for (const auto& layer : report.mla_layer_stats.layers) {
std::cout << " stage_index=" << layer.stage_index << " name=" << layer.name
<< " latency_us=" << layer.latency_us << "\n";
}
}
if (options.include_mla_stage_stats) {
std::cout << "mla_stage_stats.status=" << report.mla_stage_stats.status << "\n";
for (const auto& stage : report.mla_stage_stats.stages) {
std::cout << " stage_index=" << stage.stage_index << " latency_us=" << stage.latency_us
<< "\n";
}
}

std::string report_path;
if (get_arg(argc, argv, "--report", report_path)) {
std::cout << "report_path=" << report.save(report_path).string() << "\n";
}

std::cout << "[OK] 003_benchmark_your_model\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}

ソース​