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

モデルのベンチマーク

項目
カテゴリモデルと推論
難易度初級
推定所要時間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は、非同期モデルランナーをウォームアップし、非同期プッシュ/プルウィンドウを測定し、概要を標準出力に出力し、同じ主要な値をBenchmarkReportとして返します。

サンプル数は、測定する合成入力の数です。より安定したスループットと電力の数値を得るには、より大きな数を使用します。簡単なテストを実行したい場合は、より小さな数を使用します。

BoxDecodeで終わる検出モデルは、BenchmarkOptionsも使用できます。original_widthoriginal_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::BenchmarkReport report = model.benchmark(samples);
if (report.latency_ms <= 0.0 || report.fps <= 0.0)
throw std::runtime_error("benchmark produced no measured latency/fps");

レポートを読む

返されるレポートには、ほとんどのユーザーが必要とする主要なフィールドのみが含まれます。これには、ミリ秒単位の平均エンドツーエンドのレイテンシ、1秒あたりのフレーム数で表されるスループット、利用可能な場合のワット単位の平均ボード電力、および利用可能な場合のジュール単位の測定エネルギーが含まれます。

電力テレメトリは、ボードのサポートに依存します。ランタイムが現在のターゲットで電力レールをサンプリングできない場合、ベンチマークは引き続きレイテンシとスループットを報告し、電力フィールドをゼロのままにします。

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";

実行

実行すると、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 --target tutorial_003_benchmark_your_model
./build/tutorials-standalone/tutorial_003_benchmark_your_model \
--model /tmp/resnet_50.tar.gz --samples 100

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

NEAT Benchmark
Input: synthetic
Samples: 100
Latency: 12.4 ms
FPS: 80.6
Power avg: 2.3 W
Energy: 2.8 J
report_latency_ms=12.4
report_fps=80.6
report_avg_power_watts=2.3
report_energy_joules=2.8
[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]

#include "neat.h"

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

namespace {

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>]\n";
return 1;
}
const int samples = parse_int_arg(argc, argv, "--samples", 100);

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

simaai::neat::BenchmarkReport report = model.benchmark(samples);
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 << "[OK] 003_benchmark_your_model\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}

ソース