pub const CACHE_LINE_SIZE: usize = if cfg!(any(
target_arch = "arm",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "hexagon",
)) {
32
} else if cfg!(target_arch = "m68k") {
16
} else if cfg!(target_arch = "s390x") {
256
} else if cfg!(target_arch = "powerpc64") {
128
} else {
64
};
pub const DESTRUCTIVE_INTERFERENCE_SIZE: usize = if cfg!(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "powerpc64",
)) {
128
} else if cfg!(any(
target_arch = "arm",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "hexagon",
)) {
32
} else if cfg!(target_arch = "m68k") {
16
} else if cfg!(target_arch = "s390x") {
256
} else {
64
};
const _: () = assert!(core::mem::align_of::<CacheAligned<u8>>() == DESTRUCTIVE_INTERFERENCE_SIZE);
const _: () = assert!(CACHE_LINE_SIZE <= DESTRUCTIVE_INTERFERENCE_SIZE);
const _: () = assert!(CACHE_LINE_SIZE.is_power_of_two());
const _: () = assert!(DESTRUCTIVE_INTERFERENCE_SIZE.is_power_of_two());
#[must_use]
pub const fn align_to_cache_line(size: usize) -> usize {
(size + CACHE_LINE_SIZE - 1) & !(CACHE_LINE_SIZE - 1)
}
#[cfg_attr(
any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "powerpc64",
),
repr(align(128))
)]
#[cfg_attr(
any(
target_arch = "arm",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "hexagon",
),
repr(align(32))
)]
#[cfg_attr(target_arch = "m68k", repr(align(16)))]
#[cfg_attr(target_arch = "s390x", repr(align(256)))]
#[cfg_attr(
not(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "powerpc64",
target_arch = "arm",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "hexagon",
target_arch = "m68k",
target_arch = "s390x",
)),
repr(align(64))
)]
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CacheAligned<T>(pub T);
pub type CachePad = CacheAligned<()>;
const _: () = assert!(core::mem::size_of::<CachePad>() == 0);
impl<T> CacheAligned<T> {
pub const fn new(value: T) -> Self {
Self(value)
}
pub const fn get(&self) -> &T {
&self.0
}
pub fn get_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T> core::ops::Deref for CacheAligned<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> core::ops::DerefMut for CacheAligned<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T: core::fmt::Debug> core::fmt::Debug for CacheAligned<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("CacheAligned")
.field("value", &self.0)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn align_to_cache_line_rounds_up_to_the_transfer_granularity() {
assert_eq!(align_to_cache_line(0), 0);
assert_eq!(align_to_cache_line(1), CACHE_LINE_SIZE);
assert_eq!(align_to_cache_line(CACHE_LINE_SIZE), CACHE_LINE_SIZE);
assert_eq!(
align_to_cache_line(CACHE_LINE_SIZE + 1),
CACHE_LINE_SIZE * 2
);
}
#[test]
fn cache_aligned_wrapper_forwards_to_the_inner_value() {
let aligned = CacheAligned::new(42);
assert_eq!(*aligned, 42);
assert_eq!(aligned.get(), &42);
}
#[test]
fn cache_aligned_separates_neighbours_by_the_interference_size() {
let pair = [CacheAligned::new(0_u8), CacheAligned::new(0_u8)];
let first = std::ptr::addr_of!(pair[0].0) as usize;
let second = std::ptr::addr_of!(pair[1].0) as usize;
assert_eq!(second - first, DESTRUCTIVE_INTERFERENCE_SIZE);
}
#[test]
fn cache_pad_is_free_but_raises_alignment() {
struct Host {
_pad: CachePad,
value: u8,
}
assert_eq!(core::mem::size_of::<CachePad>(), 0);
assert_eq!(core::mem::align_of::<Host>(), DESTRUCTIVE_INTERFERENCE_SIZE);
let host = Host {
_pad: CacheAligned::new(()),
value: 7,
};
assert_eq!(host.value, 7);
}
#[test]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
fn line_and_interference_sizes_differ_on_this_target() {
assert_eq!(CACHE_LINE_SIZE, 64);
assert_eq!(DESTRUCTIVE_INTERFERENCE_SIZE, 128);
assert_eq!(
core::mem::align_of::<CacheAligned<u8>>(),
DESTRUCTIVE_INTERFERENCE_SIZE
);
}
}