1use anyhow::Result;
2use depyler_annotations::{Architecture, LambdaAnnotations, LambdaEventType};
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone)]
8pub struct LambdaOptimizer {
9 strategies: HashMap<OptimizationStrategy, OptimizationConfig>,
10 performance_targets: PerformanceTargets,
11}
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub enum OptimizationStrategy {
15 BinarySize,
16 ColdStart,
17 PreWarming,
18 MemoryUsage,
19 CompileTime,
20}
21
22#[derive(Debug, Clone)]
23pub struct OptimizationConfig {
24 pub enabled: bool,
25 pub aggressive_mode: bool,
26 pub size_threshold_kb: Option<u32>,
27 pub cold_start_threshold_ms: Option<u32>,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct PerformanceTargets {
32 pub max_cold_start_ms: u32,
33 pub max_binary_size_kb: u32,
34 pub max_memory_usage_mb: u16,
35 pub target_throughput_rps: Option<u32>,
36}
37
38#[derive(Debug, Clone)]
39pub struct OptimizationPlan {
40 pub profile_overrides: HashMap<String, String>,
41 pub cargo_flags: Vec<String>,
42 pub rustc_flags: Vec<String>,
43 pub pre_warm_code: String,
44 pub init_array_code: String,
45 pub dependency_optimizations: Vec<DependencyOptimization>,
46}
47
48#[derive(Debug, Clone)]
49pub struct DependencyOptimization {
50 pub crate_name: String,
51 pub features: Vec<String>,
52 pub disabled_features: Vec<String>,
53 pub replacement: Option<String>,
54}
55
56impl Default for PerformanceTargets {
57 fn default() -> Self {
58 Self {
59 max_cold_start_ms: 50, max_binary_size_kb: 2048, max_memory_usage_mb: 128, target_throughput_rps: None,
63 }
64 }
65}
66
67impl Default for OptimizationConfig {
68 fn default() -> Self {
69 Self {
70 enabled: true,
71 aggressive_mode: false,
72 size_threshold_kb: Some(1024),
73 cold_start_threshold_ms: Some(100),
74 }
75 }
76}
77
78impl Default for LambdaOptimizer {
79 fn default() -> Self {
80 Self::new()
81 }
82}
83
84impl LambdaOptimizer {
85 pub fn new() -> Self {
86 let mut strategies = HashMap::new();
87
88 strategies.insert(
89 OptimizationStrategy::BinarySize,
90 OptimizationConfig {
91 enabled: true,
92 aggressive_mode: true,
93 size_threshold_kb: Some(1024),
94 cold_start_threshold_ms: None,
95 },
96 );
97
98 strategies.insert(
99 OptimizationStrategy::ColdStart,
100 OptimizationConfig {
101 enabled: true,
102 aggressive_mode: true,
103 size_threshold_kb: None,
104 cold_start_threshold_ms: Some(50),
105 },
106 );
107
108 strategies.insert(
109 OptimizationStrategy::PreWarming,
110 OptimizationConfig {
111 enabled: true,
112 aggressive_mode: false,
113 size_threshold_kb: None,
114 cold_start_threshold_ms: None,
115 },
116 );
117
118 strategies.insert(
119 OptimizationStrategy::MemoryUsage,
120 OptimizationConfig {
121 enabled: true,
122 aggressive_mode: false,
123 size_threshold_kb: None,
124 cold_start_threshold_ms: None,
125 },
126 );
127
128 Self {
129 strategies,
130 performance_targets: PerformanceTargets::default(),
131 }
132 }
133
134 pub fn with_targets(mut self, targets: PerformanceTargets) -> Self {
135 self.performance_targets = targets;
136 self
137 }
138
139 pub fn enable_aggressive_mode(mut self) -> Self {
140 for config in self.strategies.values_mut() {
141 config.aggressive_mode = true;
142 }
143 self
144 }
145
146 pub fn generate_optimization_plan(
148 &self,
149 annotations: &LambdaAnnotations,
150 ) -> Result<OptimizationPlan> {
151 let mut plan = OptimizationPlan {
152 profile_overrides: HashMap::new(),
153 cargo_flags: Vec::new(),
154 rustc_flags: Vec::new(),
155 pre_warm_code: String::new(),
156 init_array_code: String::new(),
157 dependency_optimizations: Vec::new(),
158 };
159
160 if self.is_strategy_enabled(&OptimizationStrategy::BinarySize) {
162 self.apply_binary_size_optimizations(&mut plan, annotations)?;
163 }
164
165 if self.is_strategy_enabled(&OptimizationStrategy::ColdStart)
167 && annotations.cold_start_optimize
168 {
169 self.apply_cold_start_optimizations(&mut plan, annotations)?;
170 }
171
172 if self.is_strategy_enabled(&OptimizationStrategy::PreWarming) {
174 self.apply_pre_warming_optimizations(&mut plan, annotations)?;
175 }
176
177 if self.is_strategy_enabled(&OptimizationStrategy::MemoryUsage) {
179 self.apply_memory_optimizations(&mut plan, annotations)?;
180 }
181
182 Ok(plan)
183 }
184
185 fn apply_binary_size_optimizations(
186 &self,
187 plan: &mut OptimizationPlan,
188 annotations: &LambdaAnnotations,
189 ) -> Result<()> {
190 plan.profile_overrides
192 .insert("opt-level".to_string(), "z".to_string());
193 plan.profile_overrides
194 .insert("lto".to_string(), "true".to_string());
195 plan.profile_overrides
196 .insert("codegen-units".to_string(), "1".to_string());
197 plan.profile_overrides
198 .insert("panic".to_string(), "abort".to_string());
199 plan.profile_overrides
200 .insert("strip".to_string(), "true".to_string());
201 plan.profile_overrides
202 .insert("overflow-checks".to_string(), "false".to_string());
203 plan.profile_overrides
204 .insert("incremental".to_string(), "false".to_string());
205
206 plan.rustc_flags.extend(vec![
208 "-C link-arg=-s".to_string(), "-C opt-level=z".to_string(), "-C codegen-units=1".to_string(), "-C lto=fat".to_string(), "-C embed-bitcode=no".to_string(), "-C panic=abort".to_string(), ]);
215
216 match annotations.architecture {
218 Architecture::Arm64 => {
219 plan.rustc_flags
220 .push("-C target-cpu=neoverse-n1".to_string());
221 }
222 Architecture::X86_64 => {
223 plan.rustc_flags.push("-C target-cpu=haswell".to_string());
224 }
225 }
226
227 plan.dependency_optimizations.extend(vec![
229 DependencyOptimization {
230 crate_name: "serde".to_string(),
231 features: vec!["derive".to_string()],
232 disabled_features: vec!["std".to_string()],
233 replacement: None,
234 },
235 DependencyOptimization {
236 crate_name: "tokio".to_string(),
237 features: vec!["rt".to_string(), "macros".to_string()],
238 disabled_features: vec!["full".to_string(), "test-util".to_string()],
239 replacement: None,
240 },
241 ]);
242
243 Ok(())
244 }
245
246 fn apply_cold_start_optimizations(
247 &self,
248 plan: &mut OptimizationPlan,
249 annotations: &LambdaAnnotations,
250 ) -> Result<()> {
251 let mut pre_warm_code = String::new();
253
254 if let Some(ref event_type) = annotations.event_type {
255 match event_type {
256 LambdaEventType::ApiGatewayProxyRequest
257 | LambdaEventType::ApiGatewayV2HttpRequest => {
258 pre_warm_code.push_str(
259 r#"
260 // Pre-warm API Gateway types
261 let _ = std::hint::black_box(serde_json::from_str::<serde_json::Value>("{{}}"));
262 let _ = std::hint::black_box(std::collections::HashMap::<String, String>::new());
263
264 // Pre-allocate response buffers
265 let mut response_buf = Vec::with_capacity(4096);
266 response_buf.push(0);
267 std::mem::forget(response_buf);
268"#,
269 );
270 }
271 LambdaEventType::SqsEvent => {
272 pre_warm_code.push_str(
273 r#"
274 // Pre-warm SQS types
275 let _ = std::hint::black_box(Vec::<String>::with_capacity(10));
276 let _ = std::hint::black_box(String::with_capacity(1024));
277"#,
278 );
279 }
280 LambdaEventType::S3Event => {
281 pre_warm_code.push_str(
282 r#"
283 // Pre-warm S3 types
284 let _ = std::hint::black_box(std::path::PathBuf::new());
285 let _ = std::hint::black_box(String::with_capacity(512));
286"#,
287 );
288 }
289 _ => {}
290 }
291 }
292
293 pre_warm_code.push_str(
295 r#"
296 // Pre-warm common allocations
297 let _ = std::hint::black_box(serde_json::Value::Null);
298
299 // Initialize thread-local storage
300 thread_local! {{
301 static BUFFER: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(Vec::with_capacity(8192));
302 }}
303 BUFFER.with(|_| {{}});
304"#
305 );
306
307 plan.pre_warm_code = pre_warm_code;
308
309 plan.init_array_code = r#"
311#[link_section = ".init_array"]
312static INIT: extern "C" fn() = {{
313 extern "C" fn init() {{
314 // Pre-warm critical allocators
315 let _ = std::hint::black_box(Vec::<u8>::with_capacity(1024));
316 let _ = std::hint::black_box(String::with_capacity(512));
317
318 // Initialize mimalloc if enabled
319 #[cfg(feature = "mimalloc")]
320 {{
321 use mimalloc::MiMalloc;
322 let _ = std::hint::black_box(&MiMalloc);
323 }}
324 }}
325 init
326}};
327"#
328 .to_string();
329
330 plan.profile_overrides
332 .insert("opt-level".to_string(), "3".to_string());
333
334 Ok(())
335 }
336
337 fn apply_pre_warming_optimizations(
338 &self,
339 plan: &mut OptimizationPlan,
340 annotations: &LambdaAnnotations,
341 ) -> Result<()> {
342 for path in &annotations.pre_warm_paths {
344 plan.pre_warm_code.push_str(&format!(
345 " // Pre-warm path: {path}\n let _ = std::hint::black_box(String::from(\"{path}\"));\n"
346 ));
347 }
348
349 if annotations.custom_serialization {
351 plan.pre_warm_code.push_str(
352 r#"
353 // Pre-warm custom serialization paths
354 let _ = std::hint::black_box(serde_json::to_string(&serde_json::Value::Null));
355 let _ = std::hint::black_box(serde_json::from_str::<serde_json::Value>("null"));
356"#,
357 );
358 }
359
360 Ok(())
361 }
362
363 fn apply_memory_optimizations(
364 &self,
365 plan: &mut OptimizationPlan,
366 annotations: &LambdaAnnotations,
367 ) -> Result<()> {
368 plan.dependency_optimizations.push(DependencyOptimization {
370 crate_name: "mimalloc".to_string(),
371 features: vec!["local_dynamic_tls".to_string()],
372 disabled_features: vec!["debug".to_string()],
373 replacement: None,
374 });
375
376 if annotations.memory_size <= 128 {
378 plan.pre_warm_code.push_str(&format!(
379 r#"
380 // Memory-constrained optimization
381 if std::env::var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE").unwrap_or_default() == "{}" {{
382 // Conservative pre-allocation for low memory
383 let _ = std::hint::black_box(Vec::<u8>::with_capacity(512));
384 }}
385"#,
386 annotations.memory_size
387 ));
388 }
389
390 plan.rustc_flags
392 .push("-C link-arg=-Wl,-z,stack-size=131072".to_string()); Ok(())
395 }
396
397 pub fn generate_lambda_profile(&self, plan: &OptimizationPlan) -> String {
399 let mut profile = String::from("\n[profile.lambda]\ninherits = \"release\"\n");
400
401 for (key, value) in &plan.profile_overrides {
402 profile.push_str(&format!("{key} = {value}\n"));
403 }
404
405 profile.push_str("\n[profile.lambda.package.\"*\"]\n");
407 profile.push_str("opt-level = \"z\"\n");
408 profile.push_str("debug = false\n");
409
410 profile
411 }
412
413 pub fn generate_optimized_build_script(
415 &self,
416 plan: &OptimizationPlan,
417 annotations: &LambdaAnnotations,
418 ) -> String {
419 let mut script = format!(
420 r#"#!/bin/bash
421# Generated optimized build script for AWS Lambda
422
423set -e
424
425echo "Building optimized Lambda function..."
426echo "Target: {} MB memory, {} architecture"
427
428"#,
429 annotations.memory_size,
430 match annotations.architecture {
431 Architecture::Arm64 => "ARM64",
432 Architecture::X86_64 => "x86_64",
433 }
434 );
435
436 script.push_str("# Optimization environment variables\n");
438 script.push_str(&format!(
439 "export RUSTFLAGS=\"{}\"\n",
440 plan.rustc_flags.join(" ")
441 ));
442 script.push_str("export CARGO_PROFILE_LAMBDA_LTO=true\n");
443 script.push_str("export CARGO_PROFILE_LAMBDA_PANIC=\"abort\"\n");
444 script.push_str("export CARGO_PROFILE_LAMBDA_CODEGEN_UNITS=1\n");
445
446 let arch_flag = match annotations.architecture {
448 Architecture::Arm64 => "--arm64",
449 Architecture::X86_64 => "--x86-64",
450 };
451
452 script.push_str(&format!(
453 r#"
454# Build with cargo-lambda
455cargo lambda build \
456 --profile lambda \
457 {arch_flag} \
458 --output-format zip
459
460"#
461 ));
462
463 script.push_str(
465 r#"
466# Post-build optimizations
467BINARY_PATH="target/lambda/*/bootstrap"
468
469if command -v strip > /dev/null; then
470 echo "Stripping binary..."
471 strip $BINARY_PATH
472fi
473
474if command -v upx > /dev/null; then
475 echo "Compressing binary with UPX..."
476 upx --best --lzma $BINARY_PATH || echo "UPX compression failed, continuing..."
477fi
478
479# Size reporting
480BINARY_SIZE=$(du -h $BINARY_PATH | cut -f1)
481echo "Final binary size: $BINARY_SIZE"
482
483# Cold start benchmark (if available)
484if command -v hyperfine > /dev/null; then
485 echo "Running cold start benchmark..."
486 # This would require a test harness
487 echo "Benchmark skipped - implement with your test harness"
488fi
489
490echo "Build completed successfully!"
491"#,
492 );
493
494 script
495 }
496
497 pub fn generate_performance_monitoring(&self, _annotations: &LambdaAnnotations) -> String {
499 format!(
500 r#"
501use std::time::Instant;
502
503#[cfg(feature = "performance-monitoring")]
504mod performance {{
505 use super::*;
506
507 pub struct PerformanceMonitor {{
508 start_time: Instant,
509 cold_start: bool,
510 }}
511
512 impl PerformanceMonitor {{
513 pub fn new() -> Self {{
514 Self {{
515 start_time: Instant::now(),
516 cold_start: std::env::var("_LAMBDA_START_TIME").is_err(),
517 }}
518 }}
519
520 pub fn log_cold_start(&self) {{
521 if self.cold_start {{
522 let duration = self.start_time.elapsed();
523 eprintln!("MONITORING cold_start_duration_ms:{{}}", duration.as_millis());
524
525 if duration.as_millis() > {} {{
526 eprintln!("WARNING: Cold start exceeded target of {}ms", {});
527 }}
528 }}
529 }}
530
531 pub fn log_memory_usage(&self) {{
532 // This would require a memory profiling crate
533 if let Ok(memory_info) = std::fs::read_to_string("/proc/self/status") {{
534 for line in memory_info.lines() {{
535 if line.starts_with("VmRSS:") {{
536 eprintln!("MONITORING memory_usage:{{}}", line);
537 break;
538 }}
539 }}
540 }}
541 }}
542 }}
543}}
544"#,
545 self.performance_targets.max_cold_start_ms,
546 self.performance_targets.max_cold_start_ms,
547 self.performance_targets.max_cold_start_ms,
548 )
549 }
550
551 fn is_strategy_enabled(&self, strategy: &OptimizationStrategy) -> bool {
553 self.strategies
554 .get(strategy)
555 .is_some_and(|config| config.enabled)
556 }
557
558 pub fn estimate_performance_impact(&self, plan: &OptimizationPlan) -> PerformanceEstimate {
560 let mut estimate = PerformanceEstimate::default();
561
562 if plan.profile_overrides.contains_key("opt-level") {
564 estimate.binary_size_reduction_percent += 25.0;
565 }
566 if plan.rustc_flags.iter().any(|f| f.contains("lto=fat")) {
567 estimate.binary_size_reduction_percent += 15.0;
568 }
569 if plan.rustc_flags.iter().any(|f| f.contains("strip")) {
570 estimate.binary_size_reduction_percent += 30.0;
571 }
572
573 if !plan.pre_warm_code.is_empty() {
575 estimate.cold_start_improvement_percent += 40.0;
576 }
577 if !plan.init_array_code.is_empty() {
578 estimate.cold_start_improvement_percent += 20.0;
579 }
580
581 if plan
583 .dependency_optimizations
584 .iter()
585 .any(|d| d.crate_name == "mimalloc")
586 {
587 estimate.memory_improvement_percent += 15.0;
588 }
589
590 estimate
591 }
592}
593
594#[derive(Debug, Clone, Default)]
595pub struct PerformanceEstimate {
596 pub binary_size_reduction_percent: f32,
597 pub cold_start_improvement_percent: f32,
598 pub memory_improvement_percent: f32,
599 pub compile_time_impact_percent: f32,
600}
601
602#[cfg(test)]
603mod tests {
604 use super::*;
605 use depyler_annotations::LambdaAnnotations;
606
607 #[test]
608 fn test_optimization_plan_generation() {
609 let optimizer = LambdaOptimizer::new();
610 let annotations = LambdaAnnotations {
611 cold_start_optimize: true,
612 event_type: Some(LambdaEventType::ApiGatewayProxyRequest),
613 ..Default::default()
614 };
615
616 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
617
618 assert!(!plan.profile_overrides.is_empty());
619 assert!(!plan.rustc_flags.is_empty());
620 assert!(!plan.pre_warm_code.is_empty());
621 }
622
623 #[test]
624 fn test_binary_size_optimizations() {
625 let mut optimizer = LambdaOptimizer::new();
626 optimizer
628 .strategies
629 .get_mut(&OptimizationStrategy::ColdStart)
630 .unwrap()
631 .enabled = false;
632 let annotations = LambdaAnnotations::default();
633
634 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
635
636 assert!(plan.profile_overrides.get("opt-level").unwrap() == "z");
637 assert!(plan.profile_overrides.get("lto").unwrap() == "true");
638 assert!(plan.rustc_flags.iter().any(|f| f.contains("link-arg=-s")));
639 }
640
641 #[test]
642 fn test_cold_start_optimizations() {
643 let optimizer = LambdaOptimizer::new();
644 let annotations = LambdaAnnotations {
645 cold_start_optimize: true,
646 event_type: Some(LambdaEventType::SqsEvent),
647 ..Default::default()
648 };
649
650 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
651
652 assert!(plan.pre_warm_code.contains("Pre-warm SQS types"));
653 assert!(!plan.init_array_code.is_empty());
654 }
655
656 #[test]
657 fn test_memory_optimizations() {
658 let optimizer = LambdaOptimizer::new();
659 let annotations = LambdaAnnotations {
660 memory_size: 128,
661 ..Default::default()
662 };
663
664 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
665
666 assert!(plan
667 .dependency_optimizations
668 .iter()
669 .any(|d| d.crate_name == "mimalloc"));
670 assert!(plan
671 .pre_warm_code
672 .contains("Memory-constrained optimization"));
673 }
674
675 #[test]
676 fn test_lambda_profile_generation() {
677 let optimizer = LambdaOptimizer::new();
678 let annotations = LambdaAnnotations::default();
679 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
680
681 let profile = optimizer.generate_lambda_profile(&plan);
682
683 assert!(profile.contains("[profile.lambda]"));
684 assert!(profile.contains("opt-level = \"z\""));
685 assert!(profile.contains("lto = true"));
686 }
687
688 #[test]
689 fn test_build_script_generation() {
690 let optimizer = LambdaOptimizer::new();
691 let annotations = LambdaAnnotations::default();
692 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
693
694 let script = optimizer.generate_optimized_build_script(&plan, &annotations);
695
696 assert!(script.contains("cargo lambda build"));
697 assert!(script.contains("export RUSTFLAGS"));
698 assert!(script.contains("upx --best"));
699 }
700
701 #[test]
702 fn test_performance_estimate() {
703 let optimizer = LambdaOptimizer::new();
704 let annotations = LambdaAnnotations::default();
705 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
706
707 let estimate = optimizer.estimate_performance_impact(&plan);
708
709 assert!(estimate.binary_size_reduction_percent > 0.0);
710 assert!(estimate.cold_start_improvement_percent >= 0.0);
711 }
712
713 #[test]
714 fn test_aggressive_mode() {
715 let optimizer = LambdaOptimizer::new().enable_aggressive_mode();
716
717 for config in optimizer.strategies.values() {
718 assert!(config.aggressive_mode);
719 }
720 }
721
722 #[test]
723 fn test_custom_performance_targets() {
724 let targets = PerformanceTargets {
725 max_cold_start_ms: 25,
726 max_binary_size_kb: 1024,
727 max_memory_usage_mb: 64,
728 target_throughput_rps: Some(1000),
729 };
730
731 let optimizer = LambdaOptimizer::new().with_targets(targets);
732 assert_eq!(optimizer.performance_targets.max_cold_start_ms, 25);
733 assert_eq!(optimizer.performance_targets.max_binary_size_kb, 1024);
734 assert_eq!(
735 optimizer.performance_targets.target_throughput_rps,
736 Some(1000)
737 );
738 }
739
740 #[test]
745 fn test_optimization_strategy_equality() {
746 assert_eq!(
747 OptimizationStrategy::BinarySize,
748 OptimizationStrategy::BinarySize
749 );
750 assert_ne!(
751 OptimizationStrategy::BinarySize,
752 OptimizationStrategy::ColdStart
753 );
754 }
755
756 #[test]
757 fn test_optimization_strategy_hash() {
758 use std::collections::HashSet;
759 let mut set = HashSet::new();
760 set.insert(OptimizationStrategy::BinarySize);
761 set.insert(OptimizationStrategy::ColdStart);
762 set.insert(OptimizationStrategy::PreWarming);
763 set.insert(OptimizationStrategy::MemoryUsage);
764 set.insert(OptimizationStrategy::CompileTime);
765 assert_eq!(set.len(), 5);
766 }
767
768 #[test]
769 fn test_optimization_config_default() {
770 let config = OptimizationConfig::default();
771 assert!(config.enabled);
772 assert!(!config.aggressive_mode);
773 assert_eq!(config.size_threshold_kb, Some(1024));
774 assert_eq!(config.cold_start_threshold_ms, Some(100));
775 }
776
777 #[test]
778 fn test_performance_targets_default() {
779 let targets = PerformanceTargets::default();
780 assert_eq!(targets.max_cold_start_ms, 50);
781 assert_eq!(targets.max_binary_size_kb, 2048);
782 assert_eq!(targets.max_memory_usage_mb, 128);
783 assert!(targets.target_throughput_rps.is_none());
784 }
785
786 #[test]
787 fn test_lambda_optimizer_default() {
788 let optimizer = LambdaOptimizer::default();
789 assert!(optimizer
790 .strategies
791 .contains_key(&OptimizationStrategy::BinarySize));
792 assert!(optimizer
793 .strategies
794 .contains_key(&OptimizationStrategy::ColdStart));
795 assert!(optimizer
796 .strategies
797 .contains_key(&OptimizationStrategy::PreWarming));
798 assert!(optimizer
799 .strategies
800 .contains_key(&OptimizationStrategy::MemoryUsage));
801 }
802
803 #[test]
804 fn test_lambda_optimizer_strategies_count() {
805 let optimizer = LambdaOptimizer::new();
806 assert_eq!(optimizer.strategies.len(), 4);
807 }
808
809 #[test]
810 fn test_is_strategy_enabled_true() {
811 let optimizer = LambdaOptimizer::new();
812 assert!(optimizer.is_strategy_enabled(&OptimizationStrategy::BinarySize));
813 assert!(optimizer.is_strategy_enabled(&OptimizationStrategy::ColdStart));
814 }
815
816 #[test]
817 fn test_is_strategy_enabled_disabled() {
818 let mut optimizer = LambdaOptimizer::new();
819 optimizer
820 .strategies
821 .get_mut(&OptimizationStrategy::PreWarming)
822 .unwrap()
823 .enabled = false;
824 assert!(!optimizer.is_strategy_enabled(&OptimizationStrategy::PreWarming));
825 }
826
827 #[test]
828 fn test_is_strategy_enabled_missing() {
829 let optimizer = LambdaOptimizer::new();
830 assert!(!optimizer.is_strategy_enabled(&OptimizationStrategy::CompileTime));
831 }
832
833 #[test]
834 fn test_generate_lambda_profile_contains_header() {
835 let optimizer = LambdaOptimizer::new();
836 let annotations = LambdaAnnotations::default();
837 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
838 let profile = optimizer.generate_lambda_profile(&plan);
839 assert!(profile.contains("[profile.lambda]"));
840 }
841
842 #[test]
843 fn test_generate_lambda_profile_contains_package_override() {
844 let optimizer = LambdaOptimizer::new();
845 let annotations = LambdaAnnotations::default();
846 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
847 let profile = optimizer.generate_lambda_profile(&plan);
848 assert!(profile.contains("[profile.lambda.package.\"*\"]"));
849 }
850
851 #[test]
852 fn test_build_script_arm64() {
853 let optimizer = LambdaOptimizer::new();
854 let annotations = LambdaAnnotations {
855 architecture: Architecture::Arm64,
856 ..Default::default()
857 };
858 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
859 let script = optimizer.generate_optimized_build_script(&plan, &annotations);
860 assert!(script.contains("--arm64"));
861 assert!(script.contains("ARM64"));
862 }
863
864 #[test]
865 fn test_build_script_x86_64() {
866 let optimizer = LambdaOptimizer::new();
867 let annotations = LambdaAnnotations {
868 architecture: Architecture::X86_64,
869 ..Default::default()
870 };
871 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
872 let script = optimizer.generate_optimized_build_script(&plan, &annotations);
873 assert!(script.contains("--x86-64"));
874 assert!(script.contains("x86_64"));
875 }
876
877 #[test]
878 fn test_build_script_contains_strip() {
879 let optimizer = LambdaOptimizer::new();
880 let annotations = LambdaAnnotations::default();
881 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
882 let script = optimizer.generate_optimized_build_script(&plan, &annotations);
883 assert!(script.contains("strip"));
884 }
885
886 #[test]
887 fn test_build_script_contains_memory_size() {
888 let optimizer = LambdaOptimizer::new();
889 let annotations = LambdaAnnotations {
890 memory_size: 256,
891 ..Default::default()
892 };
893 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
894 let script = optimizer.generate_optimized_build_script(&plan, &annotations);
895 assert!(script.contains("256 MB memory"));
896 }
897
898 #[test]
899 fn test_performance_monitoring_code() {
900 let optimizer = LambdaOptimizer::new();
901 let annotations = LambdaAnnotations::default();
902 let code = optimizer.generate_performance_monitoring(&annotations);
903 assert!(code.contains("PerformanceMonitor"));
904 assert!(code.contains("cold_start_duration_ms"));
905 }
906
907 #[test]
908 fn test_performance_monitoring_memory_usage() {
909 let optimizer = LambdaOptimizer::new();
910 let annotations = LambdaAnnotations::default();
911 let code = optimizer.generate_performance_monitoring(&annotations);
912 assert!(code.contains("log_memory_usage"));
913 assert!(code.contains("VmRSS"));
914 }
915
916 #[test]
917 fn test_estimate_performance_with_lto() {
918 let mut plan = OptimizationPlan {
919 profile_overrides: HashMap::new(),
920 cargo_flags: vec![],
921 rustc_flags: vec!["-Clto=fat".to_string()],
922 pre_warm_code: String::new(),
923 init_array_code: String::new(),
924 dependency_optimizations: vec![],
925 };
926 plan.profile_overrides
927 .insert("opt-level".to_string(), "z".to_string());
928
929 let optimizer = LambdaOptimizer::new();
930 let estimate = optimizer.estimate_performance_impact(&plan);
931 assert!(estimate.binary_size_reduction_percent >= 40.0); }
933
934 #[test]
935 fn test_estimate_performance_with_strip() {
936 let plan = OptimizationPlan {
937 profile_overrides: HashMap::new(),
938 cargo_flags: vec![],
939 rustc_flags: vec!["-Cstrip=symbols".to_string()],
940 pre_warm_code: String::new(),
941 init_array_code: String::new(),
942 dependency_optimizations: vec![],
943 };
944
945 let optimizer = LambdaOptimizer::new();
946 let estimate = optimizer.estimate_performance_impact(&plan);
947 assert!(estimate.binary_size_reduction_percent >= 30.0);
948 }
949
950 #[test]
951 fn test_estimate_performance_with_prewarm() {
952 let plan = OptimizationPlan {
953 profile_overrides: HashMap::new(),
954 cargo_flags: vec![],
955 rustc_flags: vec![],
956 pre_warm_code: "// Some prewarm code".to_string(),
957 init_array_code: String::new(),
958 dependency_optimizations: vec![],
959 };
960
961 let optimizer = LambdaOptimizer::new();
962 let estimate = optimizer.estimate_performance_impact(&plan);
963 assert!(estimate.cold_start_improvement_percent >= 40.0);
964 }
965
966 #[test]
967 fn test_estimate_performance_with_init_array() {
968 let plan = OptimizationPlan {
969 profile_overrides: HashMap::new(),
970 cargo_flags: vec![],
971 rustc_flags: vec![],
972 pre_warm_code: String::new(),
973 init_array_code: "// Some init code".to_string(),
974 dependency_optimizations: vec![],
975 };
976
977 let optimizer = LambdaOptimizer::new();
978 let estimate = optimizer.estimate_performance_impact(&plan);
979 assert!(estimate.cold_start_improvement_percent >= 20.0);
980 }
981
982 #[test]
983 fn test_estimate_performance_with_mimalloc() {
984 let plan = OptimizationPlan {
985 profile_overrides: HashMap::new(),
986 cargo_flags: vec![],
987 rustc_flags: vec![],
988 pre_warm_code: String::new(),
989 init_array_code: String::new(),
990 dependency_optimizations: vec![DependencyOptimization {
991 crate_name: "mimalloc".to_string(),
992 features: vec![],
993 disabled_features: vec![],
994 replacement: None,
995 }],
996 };
997
998 let optimizer = LambdaOptimizer::new();
999 let estimate = optimizer.estimate_performance_impact(&plan);
1000 assert!(estimate.memory_improvement_percent >= 15.0);
1001 }
1002
1003 #[test]
1004 fn test_performance_estimate_default() {
1005 let estimate = PerformanceEstimate::default();
1006 assert_eq!(estimate.binary_size_reduction_percent, 0.0);
1007 assert_eq!(estimate.cold_start_improvement_percent, 0.0);
1008 assert_eq!(estimate.memory_improvement_percent, 0.0);
1009 assert_eq!(estimate.compile_time_impact_percent, 0.0);
1010 }
1011
1012 #[test]
1013 fn test_s3_event_prewarm() {
1014 let optimizer = LambdaOptimizer::new();
1015 let annotations = LambdaAnnotations {
1016 cold_start_optimize: true,
1017 event_type: Some(LambdaEventType::S3Event),
1018 ..Default::default()
1019 };
1020
1021 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1022 assert!(plan.pre_warm_code.contains("S3") || !plan.pre_warm_code.is_empty());
1023 }
1024
1025 #[test]
1026 fn test_dynamodb_event_prewarm() {
1027 let optimizer = LambdaOptimizer::new();
1028 let annotations = LambdaAnnotations {
1029 cold_start_optimize: true,
1030 event_type: Some(LambdaEventType::DynamodbEvent),
1031 ..Default::default()
1032 };
1033
1034 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1035 assert!(!plan.pre_warm_code.is_empty());
1036 }
1037
1038 #[test]
1039 fn test_sns_event_prewarm() {
1040 let optimizer = LambdaOptimizer::new();
1041 let annotations = LambdaAnnotations {
1042 cold_start_optimize: true,
1043 event_type: Some(LambdaEventType::SnsEvent),
1044 ..Default::default()
1045 };
1046
1047 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1048 assert!(!plan.pre_warm_code.is_empty());
1049 }
1050
1051 #[test]
1052 fn test_eventbridge_event_prewarm() {
1053 let optimizer = LambdaOptimizer::new();
1054 let annotations = LambdaAnnotations {
1055 cold_start_optimize: true,
1056 event_type: Some(LambdaEventType::EventBridgeEvent(None)),
1057 ..Default::default()
1058 };
1059
1060 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1061 assert!(!plan.pre_warm_code.is_empty());
1062 }
1063
1064 #[test]
1065 fn test_api_gateway_v2_event_prewarm() {
1066 let optimizer = LambdaOptimizer::new();
1067 let annotations = LambdaAnnotations {
1068 cold_start_optimize: true,
1069 event_type: Some(LambdaEventType::ApiGatewayV2HttpRequest),
1070 ..Default::default()
1071 };
1072
1073 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1074 assert!(!plan.pre_warm_code.is_empty());
1075 }
1076
1077 #[test]
1078 fn test_low_memory_optimizations() {
1079 let optimizer = LambdaOptimizer::new();
1080 let annotations = LambdaAnnotations {
1081 memory_size: 64, ..Default::default()
1083 };
1084
1085 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1086 assert!(plan.pre_warm_code.contains("Memory") || !plan.dependency_optimizations.is_empty());
1088 }
1089
1090 #[test]
1091 fn test_high_memory_optimizations() {
1092 let optimizer = LambdaOptimizer::new();
1093 let annotations = LambdaAnnotations {
1094 memory_size: 1024, ..Default::default()
1096 };
1097
1098 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1099 assert!(!plan.profile_overrides.is_empty());
1101 }
1102
1103 #[test]
1104 fn test_optimization_plan_default_has_rustc_flags() {
1105 let optimizer = LambdaOptimizer::new();
1106 let annotations = LambdaAnnotations::default();
1107
1108 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1109 assert!(!plan.rustc_flags.is_empty() || !plan.profile_overrides.is_empty());
1111 }
1112
1113 #[test]
1114 fn test_dependency_optimization_struct() {
1115 let dep_opt = DependencyOptimization {
1116 crate_name: "serde".to_string(),
1117 features: vec!["derive".to_string()],
1118 disabled_features: vec!["std".to_string()],
1119 replacement: Some("miniserde".to_string()),
1120 };
1121
1122 assert_eq!(dep_opt.crate_name, "serde");
1123 assert_eq!(dep_opt.features.len(), 1);
1124 assert_eq!(dep_opt.disabled_features.len(), 1);
1125 assert!(dep_opt.replacement.is_some());
1126 }
1127
1128 #[test]
1129 fn test_optimization_plan_struct() {
1130 let plan = OptimizationPlan {
1131 profile_overrides: HashMap::new(),
1132 cargo_flags: vec!["--release".to_string()],
1133 rustc_flags: vec!["-Copt-level=z".to_string()],
1134 pre_warm_code: "prewarm".to_string(),
1135 init_array_code: "init".to_string(),
1136 dependency_optimizations: vec![],
1137 };
1138
1139 assert_eq!(plan.cargo_flags.len(), 1);
1140 assert_eq!(plan.rustc_flags.len(), 1);
1141 assert!(!plan.pre_warm_code.is_empty());
1142 assert!(!plan.init_array_code.is_empty());
1143 }
1144
1145 #[test]
1146 fn test_with_targets_chaining() {
1147 let targets1 = PerformanceTargets {
1148 max_cold_start_ms: 10,
1149 ..Default::default()
1150 };
1151 let targets2 = PerformanceTargets {
1152 max_cold_start_ms: 20,
1153 ..Default::default()
1154 };
1155
1156 let optimizer = LambdaOptimizer::new()
1157 .with_targets(targets1)
1158 .with_targets(targets2);
1159
1160 assert_eq!(optimizer.performance_targets.max_cold_start_ms, 20);
1162 }
1163
1164 #[test]
1165 fn test_enable_aggressive_mode_chaining() {
1166 let optimizer = LambdaOptimizer::new()
1167 .enable_aggressive_mode()
1168 .enable_aggressive_mode();
1169
1170 for config in optimizer.strategies.values() {
1171 assert!(config.aggressive_mode);
1172 }
1173 }
1174
1175 #[test]
1176 fn test_cold_start_optimize_disabled() {
1177 let optimizer = LambdaOptimizer::new();
1178 let annotations = LambdaAnnotations {
1179 cold_start_optimize: false,
1180 event_type: Some(LambdaEventType::SqsEvent),
1181 ..Default::default()
1182 };
1183
1184 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1185 assert!(!plan.profile_overrides.is_empty());
1188 }
1189
1190 #[test]
1191 fn test_no_event_type_optimization() {
1192 let optimizer = LambdaOptimizer::new();
1193 let annotations = LambdaAnnotations {
1194 cold_start_optimize: true,
1195 event_type: None,
1196 ..Default::default()
1197 };
1198
1199 let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1200 assert!(!plan.profile_overrides.is_empty());
1202 }
1203}