#[derive(Debug, Clone, PartialEq)]
pub struct ImpactEstimate {
pub savings_per_instance: usize,
pub cache_line_size: usize,
pub current_cache_lines: usize,
pub optimal_cache_lines: usize,
pub extra_bytes_1k: usize,
pub extra_bytes_1m: usize,
pub extra_cache_lines_1k: usize,
pub extra_cache_lines_1m: usize,
}
impl ImpactEstimate {
pub fn reduces_cache_line_crossings(&self) -> bool {
self.current_cache_lines > self.optimal_cache_lines
}
}
pub fn estimate_impact(
savings: usize,
current_size: usize,
optimal_size: usize,
cache_line: usize,
) -> ImpactEstimate {
let cl = cache_line.max(1);
let current_cache_lines = current_size.div_ceil(cl);
let optimal_cache_lines = optimal_size.div_ceil(cl);
ImpactEstimate {
savings_per_instance: savings,
cache_line_size: cl,
current_cache_lines,
optimal_cache_lines,
extra_bytes_1k: savings * 1_000,
extra_bytes_1m: savings * 1_000_000,
extra_cache_lines_1k: (savings * 1_000).div_ceil(cl),
extra_cache_lines_1m: (savings * 1_000_000).div_ceil(cl),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn connection_layout_impact() {
let est = estimate_impact(8, 24, 16, 64);
assert_eq!(est.savings_per_instance, 8);
assert_eq!(est.cache_line_size, 64);
assert_eq!(est.current_cache_lines, 1);
assert_eq!(est.optimal_cache_lines, 1);
assert!(!est.reduces_cache_line_crossings());
assert_eq!(est.extra_bytes_1k, 8_000);
assert_eq!(est.extra_bytes_1m, 8_000_000);
assert_eq!(est.extra_cache_lines_1k, 125);
assert_eq!(est.extra_cache_lines_1m, 125_000);
}
#[test]
fn large_struct_reduces_cache_line_crossings() {
let est = estimate_impact(64, 128, 64, 64);
assert_eq!(est.current_cache_lines, 2);
assert_eq!(est.optimal_cache_lines, 1);
assert!(est.reduces_cache_line_crossings());
assert_eq!(est.extra_bytes_1m, 64_000_000);
assert_eq!(est.extra_cache_lines_1m, 1_000_000);
}
#[test]
fn zero_savings_produces_zero_impact() {
let est = estimate_impact(0, 16, 16, 64);
assert_eq!(est.savings_per_instance, 0);
assert_eq!(est.extra_bytes_1k, 0);
assert_eq!(est.extra_bytes_1m, 0);
assert_eq!(est.extra_cache_lines_1k, 0);
assert_eq!(est.extra_cache_lines_1m, 0);
assert!(!est.reduces_cache_line_crossings());
}
#[test]
fn apple_silicon_128_byte_cache_line() {
let est = estimate_impact(8, 24, 16, 128);
assert_eq!(est.cache_line_size, 128);
assert_eq!(est.current_cache_lines, 1);
assert_eq!(est.optimal_cache_lines, 1);
assert_eq!(est.extra_cache_lines_1m, 62_500);
}
#[test]
fn struct_spanning_boundary_in_current_but_not_optimal() {
let est = estimate_impact(8, 72, 64, 64);
assert_eq!(est.current_cache_lines, 2); assert_eq!(est.optimal_cache_lines, 1); assert!(est.reduces_cache_line_crossings());
}
#[test]
fn small_savings_cache_lines_round_up() {
let est = estimate_impact(1, 8, 7, 64);
assert_eq!(est.extra_cache_lines_1k, 16); }
}