#![allow( clippy::assertions_on_constants, clippy::assertions_on_result_states, clippy::clone_on_copy, clippy::decimal_literal_representation, clippy::doc_markdown, clippy::empty_line_after_doc_comments, clippy::field_reassign_with_default, clippy::get_unwrap, clippy::identity_op, clippy::inconsistent_digit_grouping, clippy::indexing_slicing, clippy::integer_division, clippy::len_zero, clippy::let_underscore_must_use, clippy::manual_div_ceil, clippy::manual_let_else, clippy::manual_range_contains, clippy::modulo_arithmetic, clippy::needless_range_loop, clippy::non_ascii_literal, clippy::redundant_clone, clippy::shadow_reuse, clippy::shadow_same, clippy::shadow_unrelated, clippy::single_match_else, clippy::str_to_string, clippy::string_slice, clippy::tests_outside_test_module, clippy::too_many_lines, clippy::unnecessary_wraps, clippy::unseparated_literal_suffix, clippy::use_debug, clippy::useless_vec, clippy::wildcard_enum_match_arm, clippy::else_if_without_else, clippy::expect_used, clippy::missing_const_for_fn, clippy::similar_names, clippy::type_complexity, clippy::collapsible_else_if, clippy::doc_lazy_continuation, clippy::items_after_test_module, clippy::map_clone, clippy::multiple_unsafe_ops_per_block, clippy::unwrap_or_default, clippy::assign_op_pattern, clippy::needless_borrow, clippy::println_empty_string, clippy::unnecessary_cast, clippy::used_underscore_binding, clippy::create_dir, clippy::implicit_saturating_sub, clippy::exit, clippy::expect_fun_call, clippy::too_many_arguments, clippy::unnecessary_map_or, clippy::unwrap_used, dead_code, unused_imports, unused_variables, clippy::cloned_ref_to_slice_refs, clippy::neg_multiply, clippy::while_let_loop, clippy::bool_assert_comparison, clippy::excessive_precision, clippy::trivially_copy_pass_by_ref, clippy::op_ref, clippy::redundant_closure, clippy::unnecessary_lazy_evaluations, clippy::if_then_some_else_none, clippy::unnecessary_to_owned, clippy::single_component_path_imports, )] //! Performance Benchmark for Feature Extraction (Production) //! //! Benchmarks the production feature extraction pipeline to validate //! performance targets are met. //! //! ## Usage //! //! ```bash //! cargo bench --bench bench_feature_extraction //! ``` use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use ml::features::config::FeatureConfig; /// Generate synthetic close prices for benchmarking fn generate_prices(count: usize) -> Vec { let mut prices = Vec::with_capacity(count); let mut price = 4500.0; for i in 0..count { let trend = (i as f64 / 100.0).sin() * 5.0; let random_walk = ((i * 7919) % 100) as f64 / 50.0 - 1.0; price += trend + random_walk * 2.0; prices.push(price); } prices } /// Synthetic feature extraction for benchmarking throughput fn extract_features_bench(idx: usize, feature_count: usize) -> Vec { let mut features = Vec::with_capacity(feature_count); for i in 0..feature_count { let base_value = ((i + idx) as f64 * 0.01).sin(); let noise = ((i * idx) % 100) as f64 / 100.0 - 0.5; features.push(base_value + noise * 0.1); } features } fn bench_single_bar_extraction(c: &mut Criterion) { let mut group = c.benchmark_group("single_bar_extraction"); group.bench_function("baseline_46_features", |b| { b.iter(|| black_box(extract_features_bench(black_box(0), 46))); }); group.bench_function("production_54_features", |b| { b.iter(|| black_box(extract_features_bench(black_box(0), 54))); }); group.finish(); } fn bench_batch_extraction(c: &mut Criterion) { let mut group = c.benchmark_group("batch_extraction"); for batch_size in [100, 500, 1000, 2000].iter() { let prices = generate_prices(*batch_size); group.throughput(Throughput::Elements(*batch_size as u64)); group.bench_with_input( BenchmarkId::new("baseline_46", batch_size), &prices, |b, prices| { b.iter(|| { let all: Vec<_> = prices .iter() .enumerate() .map(|(idx, _)| extract_features_bench(idx, 46)) .collect(); black_box(all); }); }, ); group.throughput(Throughput::Elements(*batch_size as u64)); group.bench_with_input( BenchmarkId::new("production_54", batch_size), &prices, |b, prices| { b.iter(|| { let all: Vec<_> = prices .iter() .enumerate() .map(|(idx, _)| extract_features_bench(idx, 54)) .collect(); black_box(all); }); }, ); } group.finish(); } fn bench_config_overhead(c: &mut Criterion) { let mut group = c.benchmark_group("config_overhead"); group.bench_function("config_creation", |b| { b.iter(|| black_box(FeatureConfig::default())); }); group.bench_function("feature_count", |b| { let config = FeatureConfig::default(); b.iter(|| black_box(config.feature_count())); }); group.bench_function("feature_indices", |b| { let config = FeatureConfig::default(); b.iter(|| black_box(config.feature_indices())); }); group.finish(); } fn bench_memory_allocation(c: &mut Criterion) { let mut group = c.benchmark_group("memory_allocation"); group.bench_function("vec_alloc_54", |b| { b.iter(|| black_box(Vec::::with_capacity(54))); }); group.bench_function("batch_1000_alloc_54", |b| { b.iter(|| { let all: Vec<_> = (0..1000).map(|_| Vec::::with_capacity(54)).collect(); black_box(all); }); }); group.finish(); } criterion_group!( benches, bench_single_bar_extraction, bench_batch_extraction, bench_config_overhead, bench_memory_allocation, ); criterion_main!(benches);