Skip to content

Commit e5532fa

Browse files
authored
refactor configuration loading: add ModelsConfig schema for directory-based model discovery (#25)
* refactor configuration loading: add `ModelsConfig` schema for directory-based model discovery with precision inference, update pipeline to support new format, and ensure backward compatibility * add tests for `ModelsConfig` schema: cover directory-based model discovery, precision inference, legacy formats, and edge cases in configuration loading and pipeline integration * remove unused precision and tags fields from model definitions in tests and example configuration * add example configuration for benchmarking Raspberry Pi with OpenVINO via SSH
1 parent dce2a4b commit e5532fa

8 files changed

Lines changed: 897 additions & 12 deletions

File tree

experiments/android_example.yaml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,17 @@ device:
3131
use_root: false
3232

3333
models:
34-
- name: "resnet50"
35-
path: "models/resnet50_fp16.xml" # UPDATE THIS PATH
36-
precision: "FP16"
37-
tags:
38-
framework: "tensorflow"
39-
task: "classification"
34+
directories:
35+
- "/path/to/models" # UPDATE THIS PATH - directory containing model files
36+
- "/path/to/additional/models" # UPDATE THIS PATH - additional model directories
37+
extensions:
38+
- ".xml" # OpenVINO IR format
39+
- ".onnx" # ONNX format
40+
- ".pb" # TensorFlow format
41+
- ".tflite" # TensorFlow Lite format
42+
models: # Optional: explicit models in addition to directory scanning
43+
- name: "custom_model"
44+
path: "/path/to/custom/model.xml" # UPDATE THIS PATH
4045

4146
run:
4247
repeats: 3
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Raspberry Pi example configuration
2+
# This configuration demonstrates benchmarking on ARM-based Raspberry Pi devices via SSH
3+
#
4+
# Prerequisites:
5+
# 1. Raspberry Pi with Raspberry Pi OS (64-bit recommended)
6+
# 2. SSH access enabled on the Pi
7+
# 3. OpenVINO ARM build or cross-compilation setup
8+
# 4. Model files in OpenVINO IR format (.xml + .bin)
9+
#
10+
# To use this configuration:
11+
# 1. Update IP address in device.host
12+
# 2. Update username and password (or use SSH key)
13+
# 3. Update model directories paths
14+
# 4. For better security, use SSH key authentication instead of password
15+
# 5. Run: ovmobilebench all -c experiments/raspberry_pi_example.yaml
16+
#
17+
# Security note: Avoid committing passwords to version control.
18+
# Consider using SSH keys or environment variables for production.
19+
20+
project:
21+
name: raspberry-pi-benchmark
22+
run_id: rpi-perf-test
23+
description: Performance benchmarking on Raspberry Pi with OpenVINO
24+
25+
# Raspberry Pi SSH device configuration
26+
device:
27+
kind: linux_ssh # SSH connection to Linux ARM device
28+
host: 192.168.1.100 # UPDATE THIS: Raspberry Pi IP address
29+
username: pi # UPDATE THIS: SSH username (default 'pi' for Raspberry Pi OS)
30+
password: raspberry # UPDATE THIS: SSH password (default 'raspberry' for older RPi OS)
31+
# Optional: specify SSH key file instead of password
32+
# key_filename: /home/user/.ssh/id_rsa
33+
# Optional: specify SSH port (default 22)
34+
# port: 22
35+
push_dir: /home/pi/ovmobilebench # Remote directory for benchmark files
36+
37+
# Build configuration for ARM cross-compilation
38+
build:
39+
enabled: true
40+
openvino_repo: /path/to/openvino # UPDATE THIS PATH
41+
# ARM-specific build settings
42+
cmake_args:
43+
- -DCMAKE_BUILD_TYPE=Release
44+
- -DENABLE_SAMPLES=ON
45+
- -DENABLE_TESTS=OFF
46+
- -DTARGET_ARM=ON
47+
48+
# Model configuration with directory scanning
49+
models:
50+
directories:
51+
- "/path/to/models" # UPDATE THIS PATH - directory containing model files
52+
- "/path/to/optimized/models" # UPDATE THIS PATH - ARM-optimized models
53+
extensions:
54+
- ".xml" # OpenVINO IR format
55+
- ".onnx" # ONNX format
56+
models: # Optional: explicit models for specific testing
57+
- name: "rpi_optimized_model"
58+
path: "/path/to/rpi_specific/model.xml" # UPDATE THIS PATH
59+
60+
# Benchmark run configuration optimized for Raspberry Pi
61+
run:
62+
repeats: 3 # Multiple runs for statistical accuracy
63+
warmup: true # Perform warmup run before benchmarking
64+
timeout_sec: 300 # 5 minute timeout per benchmark
65+
cooldown_sec: 2 # Brief cooldown between runs
66+
matrix:
67+
# Conservative iteration counts for ARM performance
68+
niter: [50, 100] # Number of iterations
69+
api: ["sync"] # Synchronous inference API
70+
nireq: [1, 2] # Number of inference requests
71+
nstreams: ["1", "2"] # Number of streams
72+
device: ["CPU"] # CPU inference only (most RPi don't have GPU support)
73+
infer_precision: ["FP32", "FP16"] # Test both precisions if supported
74+
threads: [1, 2, 4] # Thread counts suitable for RPi (1-4 cores)
75+
76+
# Package configuration
77+
package:
78+
extra_files:
79+
# Include ARM-specific libraries if needed
80+
- "/usr/lib/aarch64-linux-gnu/libopenvino*.so*"
81+
82+
# Results reporting
83+
report:
84+
sinks:
85+
- type: json
86+
path: experiments/results/raspberry_pi_results.json
87+
- type: csv
88+
path: experiments/results/raspberry_pi_results.csv
89+
tags:
90+
device_type: raspberry_pi
91+
architecture: arm64
92+
os: raspberry_pi_os
93+
test_suite: performance_benchmark
94+
95+
# Optional: Environment-specific settings
96+
# environment:
97+
# - name: OMP_NUM_THREADS
98+
# value: "4"
99+
# - name: OPENVINO_LOG_LEVEL
100+
# value: "2"

ovmobilebench/config/loader.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import yaml
77

8-
from ovmobilebench.config.schema import Experiment
8+
from ovmobilebench.config.schema import Experiment, ModelItem, ModelsConfig
99

1010

1111
def load_yaml(path: Path) -> dict[str, Any]:
@@ -18,11 +18,69 @@ def load_yaml(path: Path) -> dict[str, Any]:
1818
return data
1919

2020

21+
def scan_model_directories(models_config: ModelsConfig) -> list[ModelItem]:
22+
"""Scan directories for model files based on configured extensions."""
23+
model_list = []
24+
25+
# Add explicitly defined models first
26+
if models_config.models:
27+
model_list.extend(models_config.models)
28+
29+
# Scan directories for models
30+
if models_config.directories:
31+
for directory in models_config.directories:
32+
dir_path = Path(directory)
33+
if not dir_path.exists():
34+
print(f"Warning: Model directory '{directory}' does not exist, skipping...")
35+
continue
36+
37+
# Search for models with specified extensions
38+
for ext in models_config.extensions:
39+
for model_file in dir_path.rglob(f"*{ext}"):
40+
# Skip if it's already in explicit models list
41+
if any(m.path == str(model_file) for m in model_list):
42+
continue
43+
44+
# Create model item from discovered file
45+
model_name = model_file.stem
46+
# Try to infer precision from filename
47+
precision = None
48+
if "fp16" in model_name.lower() or "f16" in model_name.lower():
49+
precision = "FP16"
50+
elif "fp32" in model_name.lower() or "f32" in model_name.lower():
51+
precision = "FP32"
52+
elif "int8" in model_name.lower() or "i8" in model_name.lower():
53+
precision = "INT8"
54+
55+
# Only add .xml files for now (OpenVINO format)
56+
if ext == ".xml":
57+
model_list.append(
58+
ModelItem(
59+
name=model_name,
60+
path=str(model_file),
61+
precision=precision,
62+
tags={"source": "directory_scan", "directory": directory},
63+
)
64+
)
65+
66+
return model_list
67+
68+
2169
def load_experiment(config_path: Path | str) -> Experiment:
2270
"""Load and validate experiment configuration."""
2371
if isinstance(config_path, str):
2472
config_path = Path(config_path)
2573
data = load_yaml(config_path)
74+
75+
# Process models configuration if it's the new format
76+
if "models" in data and isinstance(data["models"], dict):
77+
# Convert dict to ModelsConfig
78+
models_config = ModelsConfig(**data["models"])
79+
# Scan directories and get full model list
80+
model_list = scan_model_directories(models_config)
81+
# Replace models section with the expanded list for backward compatibility
82+
data["models"] = [m.model_dump() for m in model_list]
83+
2684
return Experiment(**data)
2785

2886

ovmobilebench/config/schema.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,23 @@ def validate_model_path(cls, v):
110110
return v
111111

112112

113+
class ModelsConfig(BaseModel):
114+
"""Models configuration - supports both individual models and directories."""
115+
116+
directories: list[str] | None = Field(None, description="Directories to scan for models")
117+
extensions: list[str] = Field(
118+
default=[".xml", ".onnx", ".pb", ".tflite", ".bin"],
119+
description="Model file extensions to search for",
120+
)
121+
models: list[ModelItem] | None = Field(None, description="Individual model configurations")
122+
123+
@model_validator(mode="after")
124+
def validate_models_config(self):
125+
if not self.directories and not self.models:
126+
raise ValueError("Either 'directories' or 'models' must be specified")
127+
return self
128+
129+
113130
class RunMatrix(BaseModel):
114131
"""Run matrix configuration."""
115132

@@ -173,7 +190,7 @@ class Experiment(BaseModel):
173190
build: BuildConfig
174191
package: PackageConfig = Field(default_factory=lambda: PackageConfig())
175192
device: DeviceConfig
176-
models: list[ModelItem]
193+
models: ModelsConfig | list[ModelItem]
177194
run: RunConfig = Field(
178195
default_factory=lambda: RunConfig(
179196
repeats=3,
@@ -193,6 +210,20 @@ class Experiment(BaseModel):
193210
)
194211
report: ReportConfig
195212

213+
def get_model_list(self) -> list[ModelItem]:
214+
"""Get list of models, handling both formats."""
215+
if isinstance(self.models, list):
216+
# Legacy format - list of ModelItem
217+
return self.models
218+
elif isinstance(self.models, ModelsConfig):
219+
# New format - ModelsConfig
220+
model_list = []
221+
if self.models.models:
222+
model_list.extend(self.models.models)
223+
# Directory scanning will be handled by the loader
224+
return model_list
225+
return []
226+
196227
def expand_matrix_for_model(self, model: ModelItem) -> list[dict[str, Any]]:
197228
"""Expand run matrix for a specific model."""
198229
combos = []
@@ -223,6 +254,7 @@ def expand_matrix_for_model(self, model: ModelItem) -> list[dict[str, Any]]:
223254
def get_total_runs(self) -> int:
224255
"""Calculate total number of benchmark runs."""
225256
total = 0
226-
for model in self.models:
257+
model_list = self.get_model_list()
258+
for model in model_list:
227259
total += len(self.expand_matrix_for_model(model)) * self.run.repeats
228260
return total * len(self.device.serials or ["default"])

ovmobilebench/pipeline.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def package(self) -> Path | None:
6565
# Create package
6666
packager = Packager(
6767
self.config.package,
68-
self.config.models,
68+
self.config.get_model_list(),
6969
self.artifacts_dir / "packages",
7070
)
7171

@@ -132,7 +132,7 @@ def run(
132132
)
133133

134134
# Run for each model
135-
for model in self.config.models:
135+
for model in self.config.get_model_list():
136136
logger.info(f"Running model: {model.name}")
137137

138138
# Warmup if enabled

0 commit comments

Comments
 (0)