|
| 1 | +# Design Document |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +This design extends the existing `bin/checksum.rs` tool with benchmark functionality through a new `-b` flag. The benchmark mode will measure CRC performance using either user-provided data (files/strings) or randomly generated data, reporting throughput in GiB/s along with the acceleration target used. |
| 6 | + |
| 7 | +The design maintains backward compatibility while adding a clean benchmark interface that leverages existing patterns from the `benches/benchmark.rs` implementation. |
| 8 | + |
| 9 | +## Architecture |
| 10 | + |
| 11 | +### Command Line Interface |
| 12 | + |
| 13 | +The tool will extend the existing argument parsing to support: |
| 14 | +- `-b`: Enable benchmark mode |
| 15 | +- `--size <bytes>`: Specify data size for random generation (when no file/string provided) |
| 16 | +- `--duration <seconds>`: Benchmark duration as floating-point seconds (default: 10.0) |
| 17 | +- Existing `-a <algorithm>`: CRC algorithm (required in benchmark mode) |
| 18 | +- Existing `-f <file>` or `-s <string>`: Optional data source for benchmarking |
| 19 | + |
| 20 | +### Data Flow |
| 21 | + |
| 22 | +``` |
| 23 | +User Input → Argument Parsing → Mode Detection → Benchmark Execution → Results Display |
| 24 | + ↓ |
| 25 | + [Normal Checksum Mode] |
| 26 | + ↓ |
| 27 | + [Existing Functionality] |
| 28 | +``` |
| 29 | + |
| 30 | +In benchmark mode: |
| 31 | +1. Parse and validate benchmark parameters |
| 32 | +2. Determine data source (file, string, or generated) |
| 33 | +3. For string/generated data: Load/generate test data once; For file data: use file path directly |
| 34 | +4. Run benchmark loop for specified duration using appropriate checksum function |
| 35 | +5. Calculate and display results |
| 36 | + |
| 37 | +## Components and Interfaces |
| 38 | + |
| 39 | +### Enhanced Config Structure |
| 40 | + |
| 41 | +```rust |
| 42 | +#[derive(Debug)] |
| 43 | +struct Config { |
| 44 | + algorithm: String, |
| 45 | + file: Option<String>, |
| 46 | + string: Option<String>, |
| 47 | + format: OutputFormat, |
| 48 | + benchmark: Option<BenchmarkConfig>, |
| 49 | +} |
| 50 | + |
| 51 | +#[derive(Debug)] |
| 52 | +struct BenchmarkConfig { |
| 53 | + size: Option<usize>, |
| 54 | + duration: f64, |
| 55 | +} |
| 56 | +``` |
| 57 | + |
| 58 | +### Benchmark Execution Module |
| 59 | + |
| 60 | +```rust |
| 61 | +enum BenchmarkData { |
| 62 | + InMemory(Vec<u8>), |
| 63 | + File(String), |
| 64 | +} |
| 65 | + |
| 66 | +struct BenchmarkRunner { |
| 67 | + algorithm: CrcAlgorithm, |
| 68 | + data: BenchmarkData, |
| 69 | + duration: f64, |
| 70 | +} |
| 71 | + |
| 72 | +impl BenchmarkRunner { |
| 73 | + fn new(algorithm: CrcAlgorithm, data: BenchmarkData, duration: f64) -> Self |
| 74 | + fn run(&self) -> BenchmarkResult |
| 75 | +} |
| 76 | + |
| 77 | +struct BenchmarkResult { |
| 78 | + iterations: u64, |
| 79 | + elapsed_seconds: f64, |
| 80 | + throughput_gibs: f64, |
| 81 | + time_per_iteration_nanos: f64, |
| 82 | + acceleration_target: String, |
| 83 | + data_size: u64, |
| 84 | +} |
| 85 | +``` |
| 86 | + |
| 87 | +### Data Generation |
| 88 | + |
| 89 | +The benchmark will reuse the random data generation pattern from `benches/benchmark.rs`: |
| 90 | + |
| 91 | +```rust |
| 92 | +fn generate_random_data(size: usize) -> Vec<u8> { |
| 93 | + let mut rng = rand::rng(); |
| 94 | + let mut buf = vec![0u8; size]; |
| 95 | + rng.fill_bytes(&mut buf); |
| 96 | + buf |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +## Data Models |
| 101 | + |
| 102 | +### Input Data Sources |
| 103 | + |
| 104 | +1. **File Input**: Use `checksum_file()` function to benchmark the entire file I/O and checksum stack |
| 105 | +2. **String Input**: Use string bytes directly with in-memory `checksum()` function |
| 106 | +3. **Generated Data**: Create random data of specified size using `rand::RngCore::fill_bytes()` and use in-memory `checksum()` function |
| 107 | + |
| 108 | +### Benchmark Metrics |
| 109 | + |
| 110 | +- **Iterations**: Number of checksum calculations performed |
| 111 | +- **Elapsed Time**: Actual benchmark duration in seconds |
| 112 | +- **Throughput**: Calculated as `(data_size * iterations) / elapsed_time / (1024^3)` GiB/s |
| 113 | +- **Acceleration Target**: Result from `crc_fast::get_calculator_target(algorithm)` |
| 114 | + |
| 115 | +## Error Handling |
| 116 | + |
| 117 | +### Validation Errors |
| 118 | + |
| 119 | +- Invalid algorithm names (reuse existing validation) |
| 120 | +- Invalid size parameters (non-positive values) |
| 121 | +- Invalid duration parameters (non-positive values) |
| 122 | +- File read errors (reuse existing error handling) |
| 123 | + |
| 124 | +### Runtime Errors |
| 125 | + |
| 126 | +- Memory allocation failures for large data sizes |
| 127 | +- Timer precision issues (fallback to alternative timing methods) |
| 128 | + |
| 129 | +### Error Messages |
| 130 | + |
| 131 | +All errors will follow the existing pattern of displaying the error message followed by usage information. |
| 132 | + |
| 133 | +## Testing Strategy |
| 134 | + |
| 135 | +### Unit Tests |
| 136 | + |
| 137 | +- Argument parsing validation for benchmark flags |
| 138 | +- BenchmarkConfig creation and validation |
| 139 | +- Data generation with various sizes |
| 140 | +- Throughput calculation accuracy |
| 141 | + |
| 142 | +### Integration Tests |
| 143 | + |
| 144 | +- End-to-end benchmark execution with different algorithms |
| 145 | +- File and string input handling in benchmark mode |
| 146 | +- Error handling for invalid parameters |
| 147 | +- Backward compatibility verification |
| 148 | + |
| 149 | +### Performance Validation |
| 150 | + |
| 151 | +- Verify benchmark results are reasonable (within expected ranges) |
| 152 | +- Compare with existing `benches/benchmark.rs` results for consistency |
| 153 | +- Test with various data sizes to ensure linear scaling |
| 154 | + |
| 155 | +## Implementation Notes |
| 156 | + |
| 157 | +### Timing Mechanism |
| 158 | + |
| 159 | +Use `std::time::Instant` for high-precision timing, with different approaches for different data sources: |
| 160 | + |
| 161 | +```rust |
| 162 | +let start = std::time::Instant::now(); |
| 163 | +let mut iterations = 0u64; |
| 164 | + |
| 165 | +while start.elapsed().as_secs_f64() < duration { |
| 166 | + match &self.data { |
| 167 | + BenchmarkData::InMemory(data) => { |
| 168 | + std::hint::black_box(checksum(algorithm, data)); |
| 169 | + } |
| 170 | + BenchmarkData::File(filename) => { |
| 171 | + std::hint::black_box(checksum_file(algorithm, filename, None).unwrap()); |
| 172 | + } |
| 173 | + } |
| 174 | + iterations += 1; |
| 175 | +} |
| 176 | + |
| 177 | +let elapsed = start.elapsed().as_secs_f64(); |
| 178 | +``` |
| 179 | + |
| 180 | +### Memory Considerations |
| 181 | + |
| 182 | +- Pre-allocate test data once before benchmark loop |
| 183 | +- Use `std::hint::black_box()` to prevent compiler optimizations |
| 184 | +- Consider memory alignment for optimal performance (optional enhancement) |
| 185 | + |
| 186 | +### Output Format |
| 187 | + |
| 188 | +``` |
| 189 | +Algorithm: CRC-32/ISCSI |
| 190 | +Acceleration Target: aarch64-neon-sha3 |
| 191 | +Data Size: 1,048,576 bytes (1.0 MiB) |
| 192 | +Duration: 10.00 seconds |
| 193 | +Iterations: 12,345 |
| 194 | +Throughput: 45.67 GiB/s |
| 195 | +Time per iteration: 810.2 μs |
| 196 | +``` |
| 197 | + |
| 198 | +### Default Values |
| 199 | + |
| 200 | +- **Size**: 1,048,576 bytes (1 MiB) |
| 201 | +- **Duration**: 10.0 seconds |
| 202 | +- **Algorithm**: Must be specified via `-a` flag (no default) |
0 commit comments