Перейти до основного вмісту

Проведіть тестування вашої моделі

ПолеЗначення
КатегоріяМоделі та інференс
СкладністьПочатковий
Орієнтовний час читання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 прогріває модель, вимірює затримку, подаючи вхідні дані по одному (single-flight), потім вимірює пропускну здатність в окремому асинхронному проході передачі/отримання (push/pull), виводить зведену інформацію в stdout і повертає ті самі основні значення у 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. Вкажіть обидва вихідні розміри або залиште обидва поля порожніми; якщо їх не вказано, еталонна програма визначає геометрію на основі обчисленого маршруту моделі. Геометрія, що використовується під час кожного запуску еталонної програми, має пріоритет над застарілою геометрією BoxDecode у ModelOptions.

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

Ознайомтеся зі звітом​

Звіт, що повертається, починається з основних полів, які потрібні більшості користувачів: середня затримка від кінця до кінця в мілісекундах, пропускна здатність у кадрах за секунду, середня й пікова потужність SoM у ватах та енергія в джоулях, потрібна для обробки samples вхідних даних. throughput_duration_ms показує, скільки тривав прохід вимірювання пропускної здатності.

Дані про потужність надходять від Sentinel — демона телеметрії плати, який налаштовує інсталятор Neat. 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​

Дві необов’язкові опції запускають mla-rt після бенчмарку, щоб виміряти час скомпільованого коду MLA. Обидві типово вимкнені й потребують незайнятого devkit без інших навантажень MLA. Додайте --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 доступні stage_index із нульовою нумерацією, name і latency_us для кожного скомпільованого шару MLA: апаратний час виконання з одного проходу профілювання, який запускає лише цей шар. Зберігаються також no-op записи з нульовим часом. Сума затримок шарів не дорівнює ні затримці стадії, ні загальній затримці.

У report.mla_stage_stats.stages доступні stage_index і latency_us для кожної стадії MLA: середній апаратний час 200 безперервних запусків цієї стадії. Затримка стадії містить обчислення MLA та читання й записи DRAM програми. Вона не враховує завантаження моделі, обробку на хості та EV74 і накладні витрати конвеєра.

Якщо mla-rt відсутній, завершується з помилкою або перевищує час очікування, benchmark() однаково повертає основні вимірювання, залишає список порожнім і записує причину в report.mla_layer_stats.status або report.mla_stage_stats.status. В інших випадках кожен статус має значення "not requested" або "ok". Після запуску mla-rt, який завершився з помилкою, перевищив час очікування або аварійно завершився, MLA може ще мати незавершену роботу, тому подальше профілювання в тому самому процесі пропускається з цією причиною. Тримайте DevKit вільним, доки відновлення MLA не буде перевірено.

Статистика шарів 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), щоб записати повний звіт у YAML або JSON, включно зі статистикою шарів і стадій MLA, якщо її запитано:

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(), після чого будуть надруковані ті самі значення з отриманого звіту. Запустіть команди Python і C++ (попередньо скомпільовані) зNeat встановити в кореневу директорію **(директорію, яка містить) share/ і lib/); виконайте команди збірки з вихідного коду з кореневої директорії репозиторію.

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

Джерело​