clock_hash/
performance.rs

1//! Performance benchmarking utilities for ClockHash-256
2//!
3//! This module provides tools for measuring and validating the performance
4//! characteristics of ClockHash-256 implementations.
5
6#[cfg(feature = "std")]
7use std::time::Instant;
8
9/// Measure the throughput of ClockHash-256 for a given data size
10///
11/// # Arguments
12///
13/// * `data_size` - Size of data to hash in bytes
14/// * `iterations` - Number of iterations to measure
15///
16/// # Returns
17///
18/// Returns the throughput in MB/s
19#[cfg(feature = "std")]
20pub fn measure_throughput(data_size: usize, iterations: usize) -> f64 {
21    use crate::clockhash256;
22
23    let data = vec![0xAAu8; data_size];
24    let mut total_time = 0u128;
25
26    // Warm up
27    for _ in 0..10 {
28        let _ = clockhash256(&data);
29    }
30
31    // Measure
32    for _ in 0..iterations {
33        let start = Instant::now();
34        let _hash = clockhash256(&data);
35        total_time += start.elapsed().as_nanos();
36    }
37
38    let avg_time_ns = total_time as f64 / iterations as f64;
39    let throughput_mbs = (data_size as f64 / 1_000_000.0) / (avg_time_ns / 1_000_000_000.0);
40
41    throughput_mbs
42}
43
44/// Benchmark different input sizes and return performance characteristics
45///
46/// # Returns
47///
48/// Returns a vector of (data_size, throughput_mb_s) tuples
49#[cfg(feature = "std")]
50pub fn benchmark_sizes() -> Vec<(usize, f64)> {
51    let sizes = [64, 1024, 8192, 65536, 524288]; // 64B to 512KB
52    let iterations = [1000, 100, 50, 10, 5]; // Fewer iterations for larger sizes
53
54    sizes
55        .iter()
56        .zip(iterations.iter())
57        .map(|(&size, &iters)| (size, measure_throughput(size, iters)))
58        .collect()
59}
60
61/// Verify that performance meets minimum requirements
62///
63/// # Arguments
64///
65/// * `target_throughput` - Minimum required throughput in MB/s
66///
67/// # Returns
68///
69/// Returns `true` if performance meets the target
70#[cfg(feature = "std")]
71pub fn verify_performance(target_throughput: f64) -> bool {
72    let results = benchmark_sizes();
73
74    // Check if any size meets the target (focus on larger sizes for steady-state performance)
75    results
76        .iter()
77        .any(|&(_, throughput)| throughput >= target_throughput)
78}
79
80/// Get performance statistics
81///
82/// # Returns
83///
84/// Returns basic performance statistics as a formatted string
85#[cfg(feature = "std")]
86pub fn performance_stats() -> String {
87    let results = benchmark_sizes();
88
89    let mut stats = String::from("ClockHash-256 Performance Benchmark Results:\n");
90    stats.push_str("Size (bytes) | Throughput (MB/s)\n");
91    stats.push_str("-------------|-----------------\n");
92
93    for &(size, throughput) in &results {
94        stats.push_str(&format!("{:>11} | {:>15.2}\n", size, throughput));
95    }
96
97    // Add summary
98    if let Some((_, max_throughput)) = results.iter().max_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
99    {
100        stats.push_str(&format!("\nPeak throughput: {:.2} MB/s\n", max_throughput));
101
102        if *max_throughput >= 100.0 {
103            stats.push_str("✓ Performance target met (>= 100 MB/s)\n");
104        } else {
105            stats.push_str("⚠ Performance below target (< 100 MB/s)\n");
106        }
107    }
108
109    stats
110}
111
112/// Memory usage estimation for ClockHash-256 operations
113///
114/// # Arguments
115///
116/// * `input_size` - Size of input data in bytes
117///
118/// # Returns
119///
120/// Returns estimated memory usage in bytes
121pub fn estimate_memory_usage(input_size: usize) -> usize {
122    // ClockHasher struct: 8 * 8 (state) + 128 (buffer) + 8 (buffer_len) + 8 (message_len) = ~168 bytes
123    let hasher_size = 168;
124
125    // Input data size
126    let input_size = input_size;
127
128    // Padding overhead (up to 128 bytes for padding)
129    let padding_overhead = 128;
130
131    hasher_size + input_size + padding_overhead
132}
133
134/// CPU feature detection for performance optimization
135///
136/// # Returns
137///
138/// Returns a string describing detected CPU features relevant to ClockHash-256
139#[cfg(feature = "std")]
140pub fn cpu_features() -> String {
141    let mut features = Vec::new();
142
143    // Check for SIMD availability (this is a simplified check)
144    if cfg!(target_feature = "avx2") {
145        features.push("AVX2 available".to_string());
146    } else {
147        features.push("AVX2 not available".to_string());
148    }
149
150    if cfg!(target_feature = "avx512f") {
151        features.push("AVX-512 available".to_string());
152    }
153
154    if let Ok(arch) = std::env::var("CARGO_CFG_TARGET_ARCH") {
155        features.push(format!("Target architecture: {}", arch));
156    }
157
158    features.join(", ")
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    #[cfg(feature = "std")]
167    fn test_measure_throughput() {
168        let throughput = measure_throughput(1024, 10);
169        assert!(throughput > 0.0, "Throughput should be positive");
170        println!("Measured throughput: {:.2} MB/s", throughput);
171    }
172
173    #[test]
174    #[cfg(feature = "std")]
175    fn test_benchmark_sizes() {
176        let results = benchmark_sizes();
177        assert!(!results.is_empty(), "Should have benchmark results");
178
179        for (size, throughput) in results {
180            assert!(size > 0, "Data size should be positive");
181            assert!(throughput > 0.0, "Throughput should be positive");
182        }
183    }
184
185    #[test]
186    #[cfg(feature = "std")]
187    fn test_performance_stats() {
188        let stats = performance_stats();
189        assert!(!stats.is_empty(), "Performance stats should not be empty");
190        println!("{}", stats);
191    }
192
193    #[test]
194    fn test_estimate_memory_usage() {
195        let usage = estimate_memory_usage(1024);
196        assert!(
197            usage >= 1024,
198            "Memory usage should at least cover input data"
199        );
200
201        let usage_empty = estimate_memory_usage(0);
202        assert!(usage_empty > 0, "Even empty input should use some memory");
203    }
204
205    #[test]
206    #[cfg(feature = "std")]
207    fn test_cpu_features() {
208        let features = cpu_features();
209        assert!(
210            !features.is_empty(),
211            "CPU features string should not be empty"
212        );
213        println!("CPU features: {}", features);
214    }
215
216    #[test]
217    fn test_performance_verification() {
218        // Basic performance check - should work even without high performance
219        // In practice, this would be more stringent
220        #[cfg(feature = "std")]
221        {
222            let meets_target = verify_performance(1.0); // Very low target for testing
223            // This might fail on very slow systems, but should pass in normal conditions
224            if !meets_target {
225                println!("Warning: Performance verification failed (target: 1.0 MB/s)");
226                println!("This may be normal on slow systems or with debug builds");
227            }
228        }
229    }
230}