use crate::error::{BenchError, Result};
use crate::scenarios::BenchmarkScenario;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MlTask {
Classification,
Detection,
Segmentation,
InstanceSegmentation,
}
pub struct OnnxInferenceScenario {
model_path: PathBuf,
input_shape: Vec<usize>,
batch_size: usize,
task_type: MlTask,
warmup_iterations: usize,
benchmark_iterations: usize,
}
impl OnnxInferenceScenario {
pub fn new<P>(model_path: P, input_shape: Vec<usize>) -> Self
where
P: Into<PathBuf>,
{
Self {
model_path: model_path.into(),
input_shape,
batch_size: 1,
task_type: MlTask::Classification,
warmup_iterations: 10,
benchmark_iterations: 100,
}
}
pub fn with_batch_size(mut self, batch_size: usize) -> Self {
self.batch_size = batch_size;
self
}
pub fn with_task_type(mut self, task_type: MlTask) -> Self {
self.task_type = task_type;
self
}
pub fn with_warmup_iterations(mut self, iterations: usize) -> Self {
self.warmup_iterations = iterations;
self
}
pub fn with_benchmark_iterations(mut self, iterations: usize) -> Self {
self.benchmark_iterations = iterations;
self
}
}
impl BenchmarkScenario for OnnxInferenceScenario {
fn name(&self) -> &str {
"onnx_inference"
}
fn description(&self) -> &str {
"Benchmark ONNX model inference performance"
}
fn setup(&mut self) -> Result<()> {
if !self.model_path.exists() {
return Err(BenchError::scenario_failed(
self.name(),
format!("Model file does not exist: {}", self.model_path.display()),
));
}
if self.input_shape.is_empty() {
return Err(BenchError::InvalidConfiguration(
"Input shape cannot be empty".to_string(),
));
}
Ok(())
}
fn execute(&mut self) -> Result<()> {
#[cfg(feature = "ml")]
{
}
#[cfg(not(feature = "ml"))]
{
return Err(BenchError::missing_dependency("oxigdal-ml", "ml"));
}
Ok(())
}
fn teardown(&mut self) -> Result<()> {
Ok(())
}
}
pub struct BatchProcessingScenario {
model_path: PathBuf,
input_dir: PathBuf,
batch_sizes: Vec<usize>,
task_type: MlTask,
}
impl BatchProcessingScenario {
pub fn new<P1, P2>(model_path: P1, input_dir: P2) -> Self
where
P1: Into<PathBuf>,
P2: Into<PathBuf>,
{
Self {
model_path: model_path.into(),
input_dir: input_dir.into(),
batch_sizes: vec![1, 4, 8, 16, 32],
task_type: MlTask::Classification,
}
}
pub fn with_batch_sizes(mut self, sizes: Vec<usize>) -> Self {
self.batch_sizes = sizes;
self
}
pub fn with_task_type(mut self, task_type: MlTask) -> Self {
self.task_type = task_type;
self
}
}
impl BenchmarkScenario for BatchProcessingScenario {
fn name(&self) -> &str {
"batch_processing"
}
fn description(&self) -> &str {
"Benchmark batch processing performance with different batch sizes"
}
fn setup(&mut self) -> Result<()> {
if !self.model_path.exists() {
return Err(BenchError::scenario_failed(
self.name(),
format!("Model file does not exist: {}", self.model_path.display()),
));
}
if !self.input_dir.exists() {
return Err(BenchError::scenario_failed(
self.name(),
format!(
"Input directory does not exist: {}",
self.input_dir.display()
),
));
}
Ok(())
}
fn execute(&mut self) -> Result<()> {
#[cfg(feature = "ml")]
{
}
#[cfg(not(feature = "ml"))]
{
return Err(BenchError::missing_dependency("oxigdal-ml", "ml"));
}
Ok(())
}
fn teardown(&mut self) -> Result<()> {
Ok(())
}
}
pub struct PreprocessingScenario {
input_dir: PathBuf,
preprocessing_steps: Vec<PreprocessingStep>,
image_count: usize,
}
#[derive(Debug, Clone, Copy)]
pub enum PreprocessingStep {
Resize,
Normalize,
ColorConversion,
Augmentation,
}
impl PreprocessingScenario {
pub fn new<P>(input_dir: P) -> Self
where
P: Into<PathBuf>,
{
Self {
input_dir: input_dir.into(),
preprocessing_steps: vec![PreprocessingStep::Resize, PreprocessingStep::Normalize],
image_count: 100,
}
}
pub fn with_steps(mut self, steps: Vec<PreprocessingStep>) -> Self {
self.preprocessing_steps = steps;
self
}
pub fn with_image_count(mut self, count: usize) -> Self {
self.image_count = count;
self
}
}
impl BenchmarkScenario for PreprocessingScenario {
fn name(&self) -> &str {
"preprocessing"
}
fn description(&self) -> &str {
"Benchmark image preprocessing performance"
}
fn setup(&mut self) -> Result<()> {
if !self.input_dir.exists() {
return Err(BenchError::scenario_failed(
self.name(),
format!(
"Input directory does not exist: {}",
self.input_dir.display()
),
));
}
Ok(())
}
fn execute(&mut self) -> Result<()> {
#[cfg(feature = "ml")]
{
}
#[cfg(not(feature = "ml"))]
{
return Err(BenchError::missing_dependency("oxigdal-ml", "ml"));
}
Ok(())
}
fn teardown(&mut self) -> Result<()> {
Ok(())
}
}
pub struct PostprocessingScenario {
#[allow(dead_code)]
task_type: MlTask,
result_count: usize,
nms_threshold: f32,
}
impl PostprocessingScenario {
pub fn new(task_type: MlTask) -> Self {
Self {
task_type,
result_count: 1000,
nms_threshold: 0.5,
}
}
pub fn with_result_count(mut self, count: usize) -> Self {
self.result_count = count;
self
}
pub fn with_nms_threshold(mut self, threshold: f32) -> Self {
self.nms_threshold = threshold;
self
}
}
impl BenchmarkScenario for PostprocessingScenario {
fn name(&self) -> &str {
"postprocessing"
}
fn description(&self) -> &str {
"Benchmark postprocessing performance (NMS, etc.)"
}
fn setup(&mut self) -> Result<()> {
Ok(())
}
fn execute(&mut self) -> Result<()> {
#[cfg(feature = "ml")]
{
}
#[cfg(not(feature = "ml"))]
{
return Err(BenchError::missing_dependency("oxigdal-ml", "ml"));
}
Ok(())
}
fn teardown(&mut self) -> Result<()> {
Ok(())
}
}
pub struct EndToEndPipelineScenario {
model_path: PathBuf,
input_dir: PathBuf,
#[allow(dead_code)]
task_type: MlTask,
batch_size: usize,
pipeline_count: usize,
}
impl EndToEndPipelineScenario {
pub fn new<P1, P2>(model_path: P1, input_dir: P2, task_type: MlTask) -> Self
where
P1: Into<PathBuf>,
P2: Into<PathBuf>,
{
Self {
model_path: model_path.into(),
input_dir: input_dir.into(),
task_type,
batch_size: 4,
pipeline_count: 50,
}
}
pub fn with_batch_size(mut self, batch_size: usize) -> Self {
self.batch_size = batch_size;
self
}
pub fn with_pipeline_count(mut self, count: usize) -> Self {
self.pipeline_count = count;
self
}
}
impl BenchmarkScenario for EndToEndPipelineScenario {
fn name(&self) -> &str {
"end_to_end_pipeline"
}
fn description(&self) -> &str {
"Benchmark end-to-end inference pipeline (preprocessing + inference + postprocessing)"
}
fn setup(&mut self) -> Result<()> {
if !self.model_path.exists() {
return Err(BenchError::scenario_failed(
self.name(),
format!("Model file does not exist: {}", self.model_path.display()),
));
}
if !self.input_dir.exists() {
return Err(BenchError::scenario_failed(
self.name(),
format!(
"Input directory does not exist: {}",
self.input_dir.display()
),
));
}
Ok(())
}
fn execute(&mut self) -> Result<()> {
#[cfg(feature = "ml")]
{
}
#[cfg(not(feature = "ml"))]
{
return Err(BenchError::missing_dependency("oxigdal-ml", "ml"));
}
Ok(())
}
fn teardown(&mut self) -> Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_onnx_inference_scenario_creation() {
let scenario = OnnxInferenceScenario::new("/tmp/model.onnx", vec![1, 3, 224, 224])
.with_batch_size(8)
.with_task_type(MlTask::Segmentation)
.with_warmup_iterations(20);
assert_eq!(scenario.name(), "onnx_inference");
assert_eq!(scenario.batch_size, 8);
assert_eq!(scenario.warmup_iterations, 20);
}
#[test]
fn test_batch_processing_scenario_creation() {
let scenario = BatchProcessingScenario::new("/tmp/model.onnx", "/tmp/images")
.with_batch_sizes(vec![2, 4, 8])
.with_task_type(MlTask::Detection);
assert_eq!(scenario.name(), "batch_processing");
assert_eq!(scenario.batch_sizes.len(), 3);
}
#[test]
fn test_preprocessing_scenario_creation() {
let scenario = PreprocessingScenario::new("/tmp/images")
.with_steps(vec![
PreprocessingStep::Resize,
PreprocessingStep::Normalize,
PreprocessingStep::ColorConversion,
])
.with_image_count(50);
assert_eq!(scenario.name(), "preprocessing");
assert_eq!(scenario.preprocessing_steps.len(), 3);
assert_eq!(scenario.image_count, 50);
}
#[test]
fn test_postprocessing_scenario_creation() {
let scenario = PostprocessingScenario::new(MlTask::Detection)
.with_result_count(500)
.with_nms_threshold(0.4);
assert_eq!(scenario.name(), "postprocessing");
assert_eq!(scenario.result_count, 500);
assert_eq!(scenario.nms_threshold, 0.4);
}
#[test]
fn test_end_to_end_pipeline_scenario_creation() {
let scenario =
EndToEndPipelineScenario::new("/tmp/model.onnx", "/tmp/images", MlTask::Classification)
.with_batch_size(16)
.with_pipeline_count(100);
assert_eq!(scenario.name(), "end_to_end_pipeline");
assert_eq!(scenario.batch_size, 16);
assert_eq!(scenario.pipeline_count, 100);
}
}