Consistent formatting pass: line wrapping, import sorting, trailing whitespace removal, let-chain indentation, merged derive attributes, and unsafe block reformatting. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
582 lines
21 KiB
Rust
582 lines
21 KiB
Rust
//! Edge deployment validation and cross-platform compatibility
|
|
//!
|
|
//! This module provides deployment validation and compatibility checking for:
|
|
//! - ARM, RISC-V, WebAssembly, mobile GPU, and `IoT` platforms
|
|
//! - Memory and compute requirement estimation
|
|
//! - Performance prediction and validation
|
|
//! - Cross-platform deployment optimization
|
|
|
|
use crate::Result;
|
|
use crate::revolutionary::EdgeTarget;
|
|
use crate::revolutionary::edge_capabilities::EdgeCapabilities;
|
|
use std::collections::HashMap;
|
|
use std::time::Duration;
|
|
|
|
/// Deployment validation result for specific target
|
|
#[derive(Debug, Clone)]
|
|
pub struct DeploymentValidation {
|
|
pub compatible: bool,
|
|
pub memory_requirement_mb: u64,
|
|
pub compute_requirement: f32,
|
|
pub estimated_performance: PerformanceEstimate,
|
|
pub validation_time: Duration,
|
|
pub issues: Vec<String>,
|
|
}
|
|
|
|
/// Performance estimation for target platform
|
|
#[derive(Debug, Clone)]
|
|
pub struct PerformanceEstimate {
|
|
pub throughput_tokens_per_sec: f32,
|
|
pub latency_ms: f32,
|
|
pub memory_bandwidth_gb_s: f32,
|
|
pub power_efficiency_tokens_per_watt: f32,
|
|
}
|
|
|
|
/// Edge deployment validator
|
|
#[derive(Debug)]
|
|
pub struct EdgeDeploymentValidator {
|
|
capabilities: EdgeCapabilities,
|
|
}
|
|
|
|
impl EdgeDeploymentValidator {
|
|
#[must_use]
|
|
pub fn new(capabilities: EdgeCapabilities) -> Self {
|
|
Self { capabilities }
|
|
}
|
|
|
|
pub fn validate_deployment(
|
|
&self,
|
|
targets: &[EdgeTarget],
|
|
) -> Result<HashMap<EdgeTarget, DeploymentValidation>> {
|
|
let mut validation = HashMap::new();
|
|
for target in targets {
|
|
let validation_result = self.validate_target_compatibility(target)?;
|
|
validation.insert(target.clone(), validation_result);
|
|
}
|
|
Ok(validation)
|
|
}
|
|
|
|
fn validate_target_compatibility(&self, target: &EdgeTarget) -> Result<DeploymentValidation> {
|
|
let start_time = std::time::Instant::now();
|
|
let compatibility_checks = match target {
|
|
EdgeTarget::ARM => self.validate_arm_compatibility(),
|
|
EdgeTarget::RISCV => self.validate_riscv_compatibility(),
|
|
EdgeTarget::WASM => self.validate_wasm_compatibility(),
|
|
EdgeTarget::Mobile => self.validate_mobile_compatibility(),
|
|
EdgeTarget::MobileGPU => self.validate_mobile_gpu_compatibility(),
|
|
EdgeTarget::FPGA => self.validate_fpga_compatibility(),
|
|
EdgeTarget::IoT => self.validate_iot_compatibility(),
|
|
EdgeTarget::Embedded => self.validate_embedded_compatibility(),
|
|
EdgeTarget::Custom(name) => self.validate_custom_compatibility(name),
|
|
};
|
|
|
|
let validation_time = start_time.elapsed();
|
|
Ok(DeploymentValidation {
|
|
compatible: compatibility_checks.is_ok(),
|
|
memory_requirement_mb: self.estimate_memory_requirement(target),
|
|
compute_requirement: self.estimate_compute_requirement(target),
|
|
estimated_performance: self.estimate_performance(target),
|
|
validation_time,
|
|
issues: if compatibility_checks.is_err() {
|
|
vec![format!(
|
|
"Compatibility issue: {:?}",
|
|
compatibility_checks.unwrap_err()
|
|
)]
|
|
} else {
|
|
vec![]
|
|
},
|
|
})
|
|
}
|
|
|
|
/// Validate ARM platform compatibility
|
|
/// Requires: NEON SIMD support, minimum 512MB RAM, at least 2 compute units
|
|
fn validate_arm_compatibility(&self) -> Result<()> {
|
|
use crate::revolutionary::edge_capabilities::SIMDClass;
|
|
|
|
// ARM requires NEON SIMD for efficient inference
|
|
if !matches!(
|
|
self.capabilities.simd_support,
|
|
SIMDClass::NEON | SIMDClass::AVX
|
|
) {
|
|
return Err(crate::TransformerError::Generic(
|
|
"ARM deployment requires NEON SIMD support".into(),
|
|
));
|
|
}
|
|
|
|
// Minimum memory requirement for ARM deployment
|
|
if self.capabilities.memory_mb < 512 {
|
|
return Err(crate::TransformerError::Generic(format!(
|
|
"ARM deployment requires at least 512MB RAM, got {}MB",
|
|
self.capabilities.memory_mb
|
|
)));
|
|
}
|
|
|
|
// Need at least 2 compute units for efficient ARM inference
|
|
if self.capabilities.compute_units < 2 {
|
|
return Err(crate::TransformerError::Generic(
|
|
"ARM deployment requires at least 2 compute units".into(),
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validate RISC-V platform compatibility
|
|
/// Requires: RVV (RISC-V Vector) support preferred, minimum 256MB RAM
|
|
fn validate_riscv_compatibility(&self) -> Result<()> {
|
|
// RISC-V works best with RVV, but can fallback to scalar
|
|
// We just warn if no vector support
|
|
if self.capabilities.memory_mb < 256 {
|
|
return Err(crate::TransformerError::Generic(format!(
|
|
"RISC-V deployment requires at least 256MB RAM, got {}MB",
|
|
self.capabilities.memory_mb
|
|
)));
|
|
}
|
|
|
|
// RISC-V is still emerging - lower compute requirements
|
|
if self.capabilities.compute_units < 1 {
|
|
return Err(crate::TransformerError::Generic(
|
|
"RISC-V deployment requires at least 1 compute unit".into(),
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validate WebAssembly platform compatibility
|
|
/// Requires: WASM SIMD support for performance, memory limits apply
|
|
fn validate_wasm_compatibility(&self) -> Result<()> {
|
|
use crate::revolutionary::edge_capabilities::SIMDClass;
|
|
|
|
// WASM has a 4GB memory limit per module in practice
|
|
if self.capabilities.memory_mb > 4096 {
|
|
// This is actually fine, we just won't use all of it
|
|
}
|
|
|
|
// WASM requires at least 128MB for model + runtime
|
|
if self.capabilities.memory_mb < 128 {
|
|
return Err(crate::TransformerError::Generic(format!(
|
|
"WASM deployment requires at least 128MB RAM, got {}MB",
|
|
self.capabilities.memory_mb
|
|
)));
|
|
}
|
|
|
|
// WASM SIMD significantly improves performance
|
|
if !matches!(
|
|
self.capabilities.simd_support,
|
|
SIMDClass::WASM_SIMD | SIMDClass::AVX | SIMDClass::NEON
|
|
) {
|
|
// Not an error, but performance will be degraded
|
|
tracing::warn!("WASM deployment without SIMD will have reduced performance");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validate mobile platform compatibility
|
|
/// Balanced requirements for smartphone deployment
|
|
fn validate_mobile_compatibility(&self) -> Result<()> {
|
|
use crate::revolutionary::edge_capabilities::{EdgeClass, PowerClass};
|
|
|
|
// Mobile requires at least mid-tier device capabilities
|
|
if matches!(self.capabilities.edge_class, EdgeClass::IoT) {
|
|
return Err(crate::TransformerError::Generic(
|
|
"Mobile deployment not suitable for IoT-class devices".into(),
|
|
));
|
|
}
|
|
|
|
// Mobile deployment needs reasonable memory
|
|
if self.capabilities.memory_mb < 1024 {
|
|
return Err(crate::TransformerError::Generic(format!(
|
|
"Mobile deployment requires at least 1GB RAM, got {}MB",
|
|
self.capabilities.memory_mb
|
|
)));
|
|
}
|
|
|
|
// Power budget should support at least standard battery operation
|
|
if matches!(self.capabilities.power_budget, PowerClass::UltraLowPower) {
|
|
return Err(crate::TransformerError::Generic(
|
|
"Mobile deployment requires at least low power budget".into(),
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validate mobile GPU platform compatibility
|
|
/// Requires GPU compute capabilities on mobile devices
|
|
fn validate_mobile_gpu_compatibility(&self) -> Result<()> {
|
|
// Mobile GPU requires more memory for GPU buffers
|
|
if self.capabilities.memory_mb < 2048 {
|
|
return Err(crate::TransformerError::Generic(format!(
|
|
"Mobile GPU deployment requires at least 2GB RAM, got {}MB",
|
|
self.capabilities.memory_mb
|
|
)));
|
|
}
|
|
|
|
// GPU deployment needs multiple compute units
|
|
if self.capabilities.compute_units < 4 {
|
|
return Err(crate::TransformerError::Generic(
|
|
"Mobile GPU deployment requires at least 4 compute units".into(),
|
|
));
|
|
}
|
|
|
|
// High-end or mid-tier devices only
|
|
use crate::revolutionary::edge_capabilities::EdgeClass;
|
|
if matches!(
|
|
self.capabilities.edge_class,
|
|
EdgeClass::Low | EdgeClass::IoT
|
|
) {
|
|
return Err(crate::TransformerError::Generic(
|
|
"Mobile GPU deployment requires mid-tier or high-end device".into(),
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validate FPGA platform compatibility
|
|
/// Specialized requirements for FPGA acceleration
|
|
fn validate_fpga_compatibility(&self) -> Result<()> {
|
|
// FPGA requires significant memory for bitstream and buffers
|
|
if self.capabilities.memory_mb < 4096 {
|
|
return Err(crate::TransformerError::Generic(format!(
|
|
"FPGA deployment requires at least 4GB RAM, got {}MB",
|
|
self.capabilities.memory_mb
|
|
)));
|
|
}
|
|
|
|
// FPGA typically has wall power
|
|
use crate::revolutionary::edge_capabilities::PowerClass;
|
|
if matches!(
|
|
self.capabilities.power_budget,
|
|
PowerClass::LowPower | PowerClass::UltraLowPower
|
|
) {
|
|
return Err(crate::TransformerError::Generic(
|
|
"FPGA deployment requires at least standard battery power budget".into(),
|
|
));
|
|
}
|
|
|
|
// Check for FPGA-specific optimization flags
|
|
if !self
|
|
.capabilities
|
|
.optimization_flags
|
|
.contains_key("fpga_bitstream")
|
|
{
|
|
tracing::warn!("FPGA deployment: no bitstream optimization flag set");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validate `IoT` platform compatibility
|
|
/// Ultra-constrained resource requirements
|
|
fn validate_iot_compatibility(&self) -> Result<()> {
|
|
// IoT deployment is for constrained devices
|
|
// Very minimal requirements - just need to fit in limited memory
|
|
if self.capabilities.memory_mb < 32 {
|
|
return Err(crate::TransformerError::Generic(format!(
|
|
"IoT deployment requires at least 32MB RAM, got {}MB",
|
|
self.capabilities.memory_mb
|
|
)));
|
|
}
|
|
|
|
// IoT doesn't require SIMD - scalar is acceptable
|
|
// IoT doesn't require multiple compute units
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validate embedded platform compatibility
|
|
/// Requirements for embedded Linux/RTOS systems
|
|
fn validate_embedded_compatibility(&self) -> Result<()> {
|
|
// Embedded systems need modest memory
|
|
if self.capabilities.memory_mb < 64 {
|
|
return Err(crate::TransformerError::Generic(format!(
|
|
"Embedded deployment requires at least 64MB RAM, got {}MB",
|
|
self.capabilities.memory_mb
|
|
)));
|
|
}
|
|
|
|
// At least 1 compute unit required
|
|
if self.capabilities.compute_units < 1 {
|
|
return Err(crate::TransformerError::Generic(
|
|
"Embedded deployment requires at least 1 compute unit".into(),
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validate custom platform compatibility
|
|
/// Checks for platform-specific optimization flags
|
|
fn validate_custom_compatibility(&self, name: &str) -> Result<()> {
|
|
// Check if custom platform has required configuration
|
|
let platform_key = format!("custom_{name}");
|
|
if !self
|
|
.capabilities
|
|
.optimization_flags
|
|
.contains_key(&platform_key)
|
|
{
|
|
tracing::warn!(
|
|
"Custom platform '{}' has no specific optimization flags configured",
|
|
name
|
|
);
|
|
}
|
|
|
|
// Basic sanity check - need at least some resources
|
|
if self.capabilities.memory_mb < 16 {
|
|
return Err(crate::TransformerError::Generic(format!(
|
|
"Custom platform '{name}' requires at least 16MB RAM"
|
|
)));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Estimate memory requirement in MB for target platform
|
|
fn estimate_memory_requirement(&self, target: &EdgeTarget) -> u64 {
|
|
// Base memory for model weights (assuming quantized model)
|
|
let base_model_memory: u64 = 256; // MB for a small quantized model
|
|
|
|
// Platform-specific overhead multipliers
|
|
let overhead_multiplier = match target {
|
|
EdgeTarget::ARM => 1.5, // Runtime + NEON buffers
|
|
EdgeTarget::RISCV => 1.3, // Smaller runtime
|
|
EdgeTarget::WASM => 2.0, // JavaScript runtime + linear memory
|
|
EdgeTarget::Mobile => 1.8, // OS overhead + app framework
|
|
EdgeTarget::MobileGPU => 2.5, // GPU memory + CPU copy
|
|
EdgeTarget::FPGA => 3.0, // Bitstream + double buffering
|
|
EdgeTarget::IoT => 1.1, // Minimal overhead
|
|
EdgeTarget::Embedded => 1.2, // Small RTOS overhead
|
|
EdgeTarget::Custom(_) => 1.5, // Conservative estimate
|
|
};
|
|
|
|
// Scale by available compute (more compute = can handle larger models)
|
|
let compute_factor = (f64::from(self.capabilities.compute_units) / 4.0)
|
|
.min(2.0)
|
|
.max(0.5);
|
|
|
|
let estimated = (base_model_memory as f64 * overhead_multiplier * compute_factor) as u64;
|
|
|
|
// Ensure we don't exceed available memory
|
|
estimated.min(self.capabilities.memory_mb)
|
|
}
|
|
|
|
/// Estimate compute requirement as normalized score (0-10)
|
|
fn estimate_compute_requirement(&self, target: &EdgeTarget) -> f32 {
|
|
use crate::revolutionary::edge_capabilities::SIMDClass;
|
|
|
|
// Base compute score (higher = more demanding)
|
|
let base_score = match target {
|
|
EdgeTarget::ARM => 3.0,
|
|
EdgeTarget::RISCV => 4.0, // Less mature tooling
|
|
EdgeTarget::WASM => 5.0, // Interpreted overhead
|
|
EdgeTarget::Mobile => 3.5,
|
|
EdgeTarget::MobileGPU => 2.0, // GPU offload helps
|
|
EdgeTarget::FPGA => 1.5, // Hardware acceleration
|
|
EdgeTarget::IoT => 6.0, // Very constrained
|
|
EdgeTarget::Embedded => 5.0,
|
|
EdgeTarget::Custom(_) => 4.0,
|
|
};
|
|
|
|
// Adjust based on SIMD support
|
|
let simd_factor = match self.capabilities.simd_support {
|
|
SIMDClass::AVX => 0.6, // Best - AVX-512/AVX2
|
|
SIMDClass::NEON => 0.7, // Good - ARM NEON
|
|
SIMDClass::WASM_SIMD => 0.8, // Decent - WASM SIMD
|
|
SIMDClass::RVV => 0.75, // Good when available
|
|
SIMDClass::None => 1.0, // No acceleration
|
|
};
|
|
|
|
// Adjust based on compute units
|
|
let compute_factor = 1.0 / (1.0 + (self.capabilities.compute_units as f32 - 1.0) * 0.1);
|
|
|
|
(base_score * simd_factor * compute_factor).min(10.0)
|
|
}
|
|
|
|
/// Estimate performance characteristics for target platform
|
|
fn estimate_performance(&self, target: &EdgeTarget) -> PerformanceEstimate {
|
|
use crate::revolutionary::edge_capabilities::{EdgeClass, PowerClass, SIMDClass};
|
|
|
|
// Base throughput (tokens/sec) for a reference model
|
|
let base_throughput = match target {
|
|
EdgeTarget::ARM => 50.0,
|
|
EdgeTarget::RISCV => 30.0,
|
|
EdgeTarget::WASM => 25.0,
|
|
EdgeTarget::Mobile => 40.0,
|
|
EdgeTarget::MobileGPU => 150.0,
|
|
EdgeTarget::FPGA => 200.0,
|
|
EdgeTarget::IoT => 5.0,
|
|
EdgeTarget::Embedded => 15.0,
|
|
EdgeTarget::Custom(_) => 30.0,
|
|
};
|
|
|
|
// Scale by compute units
|
|
let compute_scale = (self.capabilities.compute_units as f32).sqrt();
|
|
|
|
// Scale by SIMD capability
|
|
let simd_scale: f32 = match self.capabilities.simd_support {
|
|
SIMDClass::AVX => 4.0,
|
|
SIMDClass::NEON => 3.0,
|
|
SIMDClass::WASM_SIMD => 2.0,
|
|
SIMDClass::RVV => 2.5,
|
|
SIMDClass::None => 1.0,
|
|
};
|
|
|
|
// Scale by device class
|
|
let class_scale = match self.capabilities.edge_class {
|
|
EdgeClass::HighEnd => 2.0,
|
|
EdgeClass::Mid => 1.0,
|
|
EdgeClass::Low => 0.5,
|
|
EdgeClass::IoT => 0.2,
|
|
};
|
|
|
|
let throughput = base_throughput * compute_scale * simd_scale.sqrt() * class_scale;
|
|
|
|
// Latency is inverse of throughput with some fixed overhead
|
|
let fixed_overhead_ms = match target {
|
|
EdgeTarget::WASM => 5.0, // JS interop overhead
|
|
EdgeTarget::FPGA => 2.0, // Programming overhead
|
|
EdgeTarget::IoT => 10.0, // Slow memory
|
|
_ => 1.0,
|
|
};
|
|
let latency = fixed_overhead_ms + (1000.0 / throughput);
|
|
|
|
// Memory bandwidth estimate (GB/s)
|
|
let memory_bandwidth = match target {
|
|
EdgeTarget::MobileGPU => 50.0,
|
|
EdgeTarget::FPGA => 100.0,
|
|
EdgeTarget::ARM => 25.0,
|
|
EdgeTarget::Mobile => 20.0,
|
|
EdgeTarget::WASM => 10.0,
|
|
EdgeTarget::IoT => 2.0,
|
|
EdgeTarget::Embedded => 5.0,
|
|
_ => 15.0,
|
|
} * (self.capabilities.memory_mb as f32 / 2048.0)
|
|
.sqrt()
|
|
.min(2.0);
|
|
|
|
// Power efficiency (tokens per watt)
|
|
let base_efficiency = match self.capabilities.power_budget {
|
|
PowerClass::Unlimited => 100.0, // Doesn't care about power
|
|
PowerClass::HighBattery => 500.0,
|
|
PowerClass::StandardBattery => 1000.0,
|
|
PowerClass::LowPower => 2000.0,
|
|
PowerClass::UltraLowPower => 5000.0, // Very efficient
|
|
};
|
|
|
|
// Adjust efficiency by throughput
|
|
let power_efficiency = base_efficiency * (throughput / 100.0).sqrt();
|
|
|
|
PerformanceEstimate {
|
|
throughput_tokens_per_sec: throughput,
|
|
latency_ms: latency,
|
|
memory_bandwidth_gb_s: memory_bandwidth,
|
|
power_efficiency_tokens_per_watt: power_efficiency,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(all(test, feature = "disabled_tests"))]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::revolutionary::edge_capabilities::{
|
|
EdgeCapabilityDetector, EdgeClass, NetworkClass, PowerClass, SIMDClass,
|
|
};
|
|
|
|
fn create_test_capabilities() -> EdgeCapabilities {
|
|
EdgeCapabilities {
|
|
compute_units: 4,
|
|
memory_mb: 2048,
|
|
simd_support: SIMDClass::NEON,
|
|
power_budget: PowerClass::StandardBattery,
|
|
network: NetworkClass::WiFi,
|
|
edge_class: EdgeClass::Mid,
|
|
optimization_flags: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_deployment_validator_creation() {
|
|
let capabilities = create_test_capabilities();
|
|
let validator = EdgeDeploymentValidator::new(capabilities);
|
|
assert_eq!(validator.capabilities.compute_units, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_deployment_validation() {
|
|
let capabilities = create_test_capabilities();
|
|
let validator = EdgeDeploymentValidator::new(capabilities);
|
|
let targets = vec![EdgeTarget::ARM, EdgeTarget::WASM];
|
|
let validation = validator.validate_deployment(&targets).unwrap();
|
|
|
|
assert_eq!(validation.len(), 2);
|
|
assert!(validation.contains_key(&EdgeTarget::ARM));
|
|
assert!(validation.contains_key(&EdgeTarget::WASM));
|
|
}
|
|
|
|
#[test]
|
|
fn test_validation_structure() {
|
|
let validation = DeploymentValidation {
|
|
compatible: true,
|
|
memory_requirement_mb: 1024,
|
|
compute_requirement: 1.0,
|
|
estimated_performance: PerformanceEstimate {
|
|
throughput_tokens_per_sec: 100.0,
|
|
latency_ms: 10.0,
|
|
memory_bandwidth_gb_s: 25.0,
|
|
power_efficiency_tokens_per_watt: 1000.0,
|
|
},
|
|
validation_time: Duration::from_millis(100),
|
|
issues: vec![],
|
|
};
|
|
|
|
assert!(validation.compatible);
|
|
assert_eq!(validation.memory_requirement_mb, 1024);
|
|
assert_eq!(
|
|
validation.estimated_performance.throughput_tokens_per_sec,
|
|
100.0
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_performance_estimate() {
|
|
let estimate = PerformanceEstimate {
|
|
throughput_tokens_per_sec: 150.0,
|
|
latency_ms: 6.67,
|
|
memory_bandwidth_gb_s: 30.0,
|
|
power_efficiency_tokens_per_watt: 1500.0,
|
|
};
|
|
|
|
assert_eq!(estimate.throughput_tokens_per_sec, 150.0);
|
|
assert_eq!(estimate.latency_ms, 6.67);
|
|
assert_eq!(estimate.memory_bandwidth_gb_s, 30.0);
|
|
assert_eq!(estimate.power_efficiency_tokens_per_watt, 1500.0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_all_target_compatibility() {
|
|
let capabilities = create_test_capabilities();
|
|
let validator = EdgeDeploymentValidator::new(capabilities);
|
|
let all_targets = vec![
|
|
EdgeTarget::ARM,
|
|
EdgeTarget::RISCV,
|
|
EdgeTarget::WASM,
|
|
EdgeTarget::MobileGPU,
|
|
EdgeTarget::IoT,
|
|
EdgeTarget::Embedded,
|
|
];
|
|
|
|
let validation = validator.validate_deployment(&all_targets).unwrap();
|
|
assert_eq!(validation.len(), all_targets.len());
|
|
|
|
for target in &all_targets {
|
|
let result = validation.get(target).unwrap();
|
|
assert!(result.memory_requirement_mb > 0);
|
|
assert!(result.compute_requirement > 0.0);
|
|
assert!(result.validation_time.as_millis() >= 0);
|
|
}
|
|
}
|
|
}
|