Skip to content

Commit d93364c

Browse files
committed
Merge branch 'main' into enable-vpclmulqdq-on-rust-1.89
2 parents f88ca8f + 7dd338d commit d93364c

35 files changed

+6291
-314
lines changed

.github/workflows/tests.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
- name: Check
2727
run: cargo check
2828
- name: Architecture check
29-
run: cargo run arch-check
29+
run: cargo run --bin arch-check
3030
- if: ${{ matrix.rust-toolchain != 'nightly' }}
3131
name: Format
3232
run: cargo fmt -- --check
@@ -56,6 +56,8 @@ jobs:
5656
run: cargo install cross --locked --version 0.2.5
5757
- name: Check
5858
run: cross check --target ${{ matrix.target }}
59+
- name: Architecture check
60+
run: cross run --bin arch-check --target ${{ matrix.target }}
5961
- name: Test
6062
run: cross test --target ${{ matrix.target }}
6163

@@ -79,5 +81,7 @@ jobs:
7981
run: cargo install cross --locked --version 0.2.5
8082
- name: Check
8183
run: cross check --target ${{ matrix.target }}
84+
- name: Architecture check
85+
run: cross run --bin arch-check --target ${{ matrix.target }}
8286
- name: Test
8387
run: cross test --target ${{ matrix.target }}
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
# Design Document
2+
3+
## Overview
4+
5+
The CRC parameters caching system will add a thread-safe, memory-efficient cache to the `CrcParams::new()` method. The cache will store pre-computed folding keys indexed by the input parameters, eliminating redundant key generation for identical parameter sets. The design prioritizes performance, thread safety, and minimal memory overhead while maintaining complete API compatibility.
6+
7+
## Architecture
8+
9+
### Cache Structure
10+
11+
The caching system will use a global, thread-safe cache implemented with:
12+
13+
- **Cache Storage**: `std::collections::HashMap<CrcParamsCacheKey, [u64; 23]>`
14+
- **Thread Safety**: `std::sync::RwLock` for concurrent read access with exclusive write access
15+
- **Cache Key**: Custom struct containing all parameters that affect key generation
16+
- **Lazy Initialization**: `std::sync::OnceLock` to initialize the cache on first use
17+
18+
### Cache Key Design
19+
20+
```rust
21+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
22+
struct CrcParamsCacheKey {
23+
width: u8,
24+
poly: u64,
25+
reflected: bool,
26+
}
27+
```
28+
29+
The cache key includes only the parameters that directly affect key generation (`width`, `poly`, `reflected`), excluding parameters like `name`, `init`, `xorout`, and `check` which don't influence the mathematical key computation.
30+
31+
### Cache Access Pattern
32+
33+
1. **Cache Hit Path**: Read lock → HashMap lookup → Return cached keys
34+
2. **Cache Miss Path**: Read lock → Cache miss → Generate keys → Write lock → Store in cache → Return keys
35+
3. **Concurrent Access**: Multiple readers can access simultaneously; writers get exclusive access
36+
37+
## Components and Interfaces
38+
39+
### Core Components
40+
41+
#### 1. Cache Module (`src/cache.rs`)
42+
43+
```rust
44+
use std::collections::HashMap;
45+
use std::sync::{OnceLock, RwLock};
46+
47+
static CACHE: OnceLock<RwLock<HashMap<CrcParamsCacheKey, [u64; 23]>>> = OnceLock::new();
48+
49+
pub fn get_or_generate_keys(width: u8, poly: u64, reflected: bool) -> [u64; 23]
50+
pub fn clear_cache() // For testing and memory management
51+
```
52+
53+
#### 2. Cache Key Structure
54+
55+
```rust
56+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
57+
struct CrcParamsCacheKey {
58+
width: u8,
59+
poly: u64,
60+
reflected: bool,
61+
}
62+
```
63+
64+
#### 3. Modified CrcParams Implementation
65+
66+
The existing `CrcParams::new()` method will be updated to use the cache:
67+
68+
```rust
69+
impl CrcParams {
70+
pub fn new(
71+
name: &'static str,
72+
width: u8,
73+
poly: u64,
74+
init: u64,
75+
reflected: bool,
76+
xorout: u64,
77+
check: u64,
78+
) -> Self {
79+
let keys = cache::get_or_generate_keys(width, poly, reflected);
80+
81+
let algorithm = match width {
82+
32 => CrcAlgorithm::Crc32Custom,
83+
64 => CrcAlgorithm::Crc64Custom,
84+
_ => panic!("Unsupported width: {}", width),
85+
};
86+
87+
Self {
88+
algorithm,
89+
name,
90+
width,
91+
poly,
92+
init,
93+
refin: reflected,
94+
refout: reflected,
95+
xorout,
96+
check,
97+
keys,
98+
}
99+
}
100+
}
101+
```
102+
103+
### Interface Design
104+
105+
#### Public Interface
106+
- No changes to existing public APIs
107+
- `CrcParams::new()` maintains identical signature and behavior
108+
- Cache operations are completely internal
109+
110+
#### Internal Interface
111+
- `cache::get_or_generate_keys()` - Primary cache interface
112+
- `cache::clear_cache()` - For testing and memory management
113+
- Cache key creation and hashing handled internally
114+
115+
## Data Models
116+
117+
### Cache Key Model
118+
```rust
119+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
120+
struct CrcParamsCacheKey {
121+
width: u8, // CRC width (32 or 64)
122+
poly: u64, // Polynomial value
123+
reflected: bool, // Reflection mode
124+
}
125+
```
126+
127+
### Cache Storage Model
128+
```rust
129+
type CacheStorage = HashMap<CrcParamsCacheKey, [u64; 23]>;
130+
type ThreadSafeCache = RwLock<CacheStorage>;
131+
```
132+
133+
### Memory Layout Considerations
134+
- Cache keys: ~17 bytes per entry (8 + 8 + 1 bytes + HashMap overhead)
135+
- Cache values: 184 bytes per entry (23 × 8 bytes)
136+
- Total per entry: ~201 bytes + HashMap overhead
137+
- Expected usage: 1-10 unique parameter sets in typical applications (single parameter set most common)
138+
139+
## Error Handling
140+
141+
### Cache Access Errors
142+
- **RwLock Poisoning**: If a thread panics while holding the write lock, subsequent accesses will fall back to direct key generation
143+
- **Memory Allocation**: HashMap growth failures will be handled by Rust's standard allocation error handling
144+
145+
### Fallback Strategy
146+
```rust
147+
fn get_or_generate_keys(width: u8, poly: u64, reflected: bool) -> [u64; 23] {
148+
let cache_key = CrcParamsCacheKey { width, poly, reflected };
149+
150+
// Try cache read first
151+
if let Ok(cache) = get_cache().read() {
152+
if let Some(keys) = cache.get(&cache_key) {
153+
return *keys;
154+
}
155+
}
156+
157+
// Generate keys outside of write lock to minimize lock hold time
158+
let keys = generate::keys(width, poly, reflected);
159+
160+
// Try to cache the result (best effort)
161+
if let Ok(mut cache) = get_cache().write() {
162+
cache.insert(cache_key, keys);
163+
}
164+
165+
keys
166+
}
167+
```
168+
169+
### Error Recovery
170+
- Lock poisoning: Continue with direct key generation
171+
- Memory pressure: Cache operations become no-ops, functionality preserved
172+
- Hash collisions: Handled by HashMap implementation
173+
174+
## Testing Strategy
175+
176+
### Unit Tests
177+
1. **Cache Functionality**
178+
- Verify cache hits return identical keys
179+
- Verify cache misses generate and store keys
180+
- Test cache key equality and hashing
181+
182+
2. **Thread Safety**
183+
- Concurrent read access tests
184+
- Read-write contention tests
185+
- Cache consistency under concurrent access
186+
187+
3. **Performance Tests**
188+
- Benchmark cache hit vs. miss performance
189+
- Memory usage validation
190+
- Comparison with uncached implementation
191+
192+
4. **Edge Cases**
193+
- Empty cache behavior
194+
- Cache with single entry
195+
- Maximum realistic cache size
196+
- Lock poisoning recovery
197+
198+
### Integration Tests
199+
1. **API Compatibility**
200+
- Existing CrcParams::new() behavior unchanged
201+
- All existing tests continue to pass
202+
- Identical results for cached vs. uncached keys
203+
204+
2. **Real-world Usage Patterns**
205+
- Multiple CrcParams instances with same parameters
206+
- Mixed usage with different parameters
207+
- Long-running application simulation
208+
209+
### Performance Benchmarks
210+
1. **Cache Hit Performance**: Measure lookup time vs. key generation time
211+
2. **Cache Miss Performance**: Measure overhead of cache check + generation
212+
3. **Memory Usage**: Track cache memory consumption over time
213+
4. **Concurrent Access**: Measure performance under thread contention
214+
215+
## Implementation Phases
216+
217+
### Phase 1: Core Cache Implementation
218+
- Create cache module with basic HashMap storage
219+
- Implement thread-safe access with RwLock
220+
- Add cache key structure and hashing
221+
222+
### Phase 2: Integration
223+
- Modify CrcParams::new() to use cache
224+
- Add fallback error handling
225+
- Ensure API compatibility
226+
227+
### Phase 3: Testing and Optimization
228+
- Comprehensive test suite
229+
- Performance benchmarking
230+
- Memory usage optimization
231+
- Documentation updates
232+
233+
## Performance Considerations
234+
235+
### Cache Hit Performance
236+
- Expected improvement: 50-100x faster than key generation
237+
- RwLock read access: ~10-20ns overhead
238+
- HashMap lookup: O(1) average case, ~50-100ns
239+
240+
### Cache Miss Performance
241+
- Additional overhead: ~100-200ns for cache check
242+
- Write lock acquisition: ~50-100ns
243+
- HashMap insertion: O(1) average case
244+
245+
### Memory Efficiency
246+
- Cache overhead per entry: ~201 bytes
247+
- Expected cache size: 200 bytes - 2KB for typical applications
248+
- Memory growth: Linear with unique parameter combinations
249+
250+
### Thread Contention
251+
- Read-heavy workload: Excellent scalability
252+
- Write contention: Minimal impact (writes are rare after warmup)
253+
- Lock-free reads: Multiple threads can read simultaneously
254+
255+
## Security Considerations
256+
257+
### Memory Safety
258+
- All cache operations use safe Rust constructs
259+
- No unsafe code in cache implementation
260+
- HashMap provides memory safety guarantees
261+
262+
### Thread Safety
263+
- RwLock prevents data races
264+
- Cache key immutability prevents modification after creation
265+
- Atomic operations for cache initialization
266+
267+
### Resource Management
268+
- Cache growth is bounded by unique parameter combinations
269+
- No automatic eviction policy (acceptable for typical usage)
270+
- Manual cache clearing available for memory management
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Requirements Document
2+
3+
## Introduction
4+
5+
This feature adds a caching layer to the `CrcParams::new()` method to optimize performance when the same CRC parameters are used multiple times during program execution. Currently, each call to `CrcParams::new()` regenerates the folding keys through expensive mathematical operations, even when identical parameters have been used before. The caching system will store generated keys in memory and reuse them for subsequent requests with matching parameters, significantly improving performance for applications that create multiple CRC instances with the same configuration.
6+
7+
## Requirements
8+
9+
### Requirement 1
10+
11+
**User Story:** As a developer using the crc-fast library, I want CrcParams::new() to cache generated keys so that repeated calls with identical parameters don't regenerate keys unnecessarily.
12+
13+
#### Acceptance Criteria
14+
15+
1. WHEN CrcParams::new() is called with parameters that have been used before THEN the system SHALL return cached keys instead of regenerating them
16+
2. WHEN CrcParams::new() is called with new parameters for the first time THEN the system SHALL generate the keys and cache them for future use
17+
3. WHEN multiple threads call CrcParams::new() concurrently with the same parameters THEN the system SHALL handle thread safety correctly without data races
18+
19+
### Requirement 2
20+
21+
**User Story:** As a performance-conscious developer, I want the caching mechanism to have minimal overhead so that it doesn't negatively impact single-use scenarios.
22+
23+
#### Acceptance Criteria
24+
25+
1. WHEN CrcParams::new() is called for the first time with any parameters THEN the performance overhead SHALL be minimal compared to the current implementation
26+
2. WHEN CrcParams::new() is called with cached parameters THEN the lookup SHALL be significantly faster than key generation
27+
3. WHEN the cache is accessed THEN the lookup mechanism SHALL use efficient data structures optimized for the expected access patterns
28+
29+
### Requirement 3
30+
31+
**User Story:** As a developer working with custom CRC parameters, I want the cache to correctly identify identical parameter sets so that functionally equivalent calls are properly cached.
32+
33+
#### Acceptance Criteria
34+
35+
1. WHEN two CrcParams::new() calls use identical values for all parameters (name, width, poly, init, reflected, xorout, check) THEN the system SHALL treat them as cache hits
36+
2. WHEN two CrcParams::new() calls differ in any parameter value THEN the system SHALL treat them as separate cache entries
37+
3. WHEN parameter comparison is performed THEN the system SHALL use all relevant fields to determine cache key uniqueness
38+
39+
### Requirement 4
40+
41+
**User Story:** As a developer concerned about memory usage, I want the cache to have reasonable memory management so that it doesn't grow unbounded in long-running applications.
42+
43+
#### Acceptance Criteria
44+
45+
1. WHEN the cache stores parameter sets THEN it SHALL use memory-efficient storage for the cache keys and values
46+
2. WHEN the application runs for extended periods THEN the cache SHALL not consume excessive memory for typical usage patterns
47+
3. IF the cache grows large THEN the system SHALL provide a way to clear or manage cache size (though automatic eviction is not required for this initial implementation)
48+
49+
### Requirement 5
50+
51+
**User Story:** As a developer integrating this library, I want the caching to be transparent so that existing code continues to work without modifications.
52+
53+
#### Acceptance Criteria
54+
55+
1. WHEN existing code calls CrcParams::new() THEN it SHALL work exactly as before with no API changes required
56+
2. WHEN CrcParams instances are created THEN they SHALL have identical behavior regardless of whether keys came from cache or generation
57+
3. WHEN the caching system is active THEN it SHALL not affect the public interface or return values of CrcParams::new()

0 commit comments

Comments
 (0)