Skip to main content

crossfire_utils/
cache_padded.rs

1//! Fork from crossbeam-utils crate commit 5a154def002304814d50f3c7658bd30eb46b2fad
2//!
3//! The MIT License (MIT)
4//!
5//! Copyright (c) 2019 The Crossbeam Project Developers
6//!
7//! Permission is hereby granted, free of charge, to any
8//! person obtaining a copy of this software and associated
9//! documentation files (the "Software"), to deal in the
10//! Software without restriction, including without
11//! limitation the rights to use, copy, modify, merge,
12//! publish, distribute, sublicense, and/or sell copies of
13//! the Software, and to permit persons to whom the Software
14//! is furnished to do so, subject to the following
15//! conditions:
16//!
17//! The above copyright notice and this permission notice
18//! shall be included in all copies or substantial portions
19//! of the Software.
20//!
21//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
22//! ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
23//! TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
24//! PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
25//! SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
26//! CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27//! OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
28//! IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29//! DEALINGS IN THE SOFTWARE.
30
31use core::{
32    fmt,
33    ops::{Deref, DerefMut},
34};
35
36/// Pads and aligns a value to the length of a cache line.
37///
38/// In concurrent programming, sometimes it is desirable to make sure commonly accessed pieces of
39/// data are not placed into the same cache line. Updating an atomic value invalidates the whole
40/// cache line it belongs to, which makes the next access to the same cache line slower for other
41/// CPU cores. Use `CachePadded` to ensure updating one piece of data doesn't invalidate other
42/// cached data.
43///
44/// # Size and alignment
45///
46/// Cache lines are assumed to be N bytes long, depending on the architecture:
47///
48/// * On x86-64, aarch64, and powerpc64, N = 128.
49/// * On arm, mips, mips64, sparc, and hexagon, N = 32.
50/// * On m68k, N = 16.
51/// * On s390x, N = 256.
52/// * On all others, N = 64.
53///
54/// Note that N is just a reasonable guess and is not guaranteed to match the actual cache line
55/// length of the machine the program is running on. On modern Intel architectures, spatial
56/// prefetcher is pulling pairs of 64-byte cache lines at a time, so we pessimistically assume that
57/// cache lines are 128 bytes long.
58///
59/// The size of `CachePadded<T>` is the smallest multiple of N bytes large enough to accommodate
60/// a value of type `T`.
61///
62/// The alignment of `CachePadded<T>` is the maximum of N bytes and the alignment of `T`.
63///
64/// # Layout
65///
66/// Since crossbeam-utils 0.8.22, this type is `#[repr(C)]` and is guaranteed that the pointer to
67/// `CachePadded<T>` has the same address as the pointer to the underlying `T`.
68///
69/// # Examples
70///
71/// Alignment and padding:
72///
73/// ```
74/// use crossfire_utils::CachePadded;
75///
76/// let array = [CachePadded::new(1i8), CachePadded::new(2i8)];
77/// let addr1 = &*array[0] as *const i8 as usize;
78/// let addr2 = &*array[1] as *const i8 as usize;
79///
80/// assert!(addr2 - addr1 >= 16);
81/// assert_eq!(addr1 % 16, 0);
82/// assert_eq!(addr2 % 16, 0);
83/// ```
84///
85/// When building a concurrent queue with a head and a tail index, it is wise to place them in
86/// different cache lines so that concurrent threads pushing and popping elements don't invalidate
87/// each other's cache lines:
88///
89/// ```
90/// use crossfire_utils::CachePadded;
91/// use core::sync::atomic::AtomicUsize;
92///
93/// struct Queue<T> {
94///     head: CachePadded<AtomicUsize>,
95///     tail: CachePadded<AtomicUsize>,
96///     buffer: *mut T,
97/// }
98/// ```
99#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
100// Starting from Intel's Sandy Bridge, spatial prefetcher is now pulling pairs of 64-byte cache
101// lines at a time, so we have to align to 128 bytes rather than 64.
102//
103// Sources:
104// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
105// - https://github.com/facebook/folly/blob/1b5288e6eea6df074758f877c849b6e73bbb9fbb/folly/lang/Align.h#L107
106//
107// aarch64/arm64ec's big.LITTLE architecture has asymmetric cores and "big" cores have 128-byte cache line size.
108//
109// Sources:
110// - https://www.mono-project.com/news/2016/09/12/arm64-icache/
111//
112// powerpc64 has 128-byte cache line size.
113//
114// Sources:
115// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_ppc64x.go#L9
116// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/powerpc/include/asm/cache.h#L26
117#[cfg_attr(
118    any(
119        target_arch = "x86_64",
120        target_arch = "aarch64",
121        target_arch = "arm64ec",
122        target_arch = "powerpc64",
123    ),
124    repr(align(128))
125)]
126// arm, mips, mips64, sparc, and hexagon have 32-byte cache line size.
127//
128// Sources:
129// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_arm.go#L7
130// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips.go#L7
131// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mipsle.go#L7
132// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips64x.go#L9
133// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/sparc/include/asm/cache.h#L17
134// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/hexagon/include/asm/cache.h#L12
135#[cfg_attr(
136    any(
137        target_arch = "arm",
138        target_arch = "mips",
139        target_arch = "mips32r6",
140        target_arch = "mips64",
141        target_arch = "mips64r6",
142        target_arch = "sparc",
143        target_arch = "hexagon",
144    ),
145    repr(align(32))
146)]
147// m68k has 16-byte cache line size.
148//
149// Sources:
150// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/m68k/include/asm/cache.h#L9
151#[cfg_attr(target_arch = "m68k", repr(align(16)))]
152// s390x has 256-byte cache line size.
153//
154// Sources:
155// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_s390x.go#L7
156// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/s390/include/asm/cache.h#L13
157#[cfg_attr(target_arch = "s390x", repr(align(256)))]
158// x86, wasm, riscv, and sparc64 have 64-byte cache line size.
159//
160// Sources:
161// - https://github.com/golang/go/blob/dda2991c2ea0c5914714469c4defc2562a907230/src/internal/cpu/cpu_x86.go#L9
162// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_wasm.go#L7
163// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/riscv/include/asm/cache.h#L10
164// - https://github.com/torvalds/linux/blob/3516bd729358a2a9b090c1905bd2a3fa926e24c6/arch/sparc/include/asm/cache.h#L19
165//
166// All others are assumed to have 64-byte cache line size.
167#[cfg_attr(
168    not(any(
169        target_arch = "x86_64",
170        target_arch = "aarch64",
171        target_arch = "arm64ec",
172        target_arch = "powerpc64",
173        target_arch = "arm",
174        target_arch = "mips",
175        target_arch = "mips32r6",
176        target_arch = "mips64",
177        target_arch = "mips64r6",
178        target_arch = "sparc",
179        target_arch = "hexagon",
180        target_arch = "m68k",
181        target_arch = "s390x",
182    )),
183    repr(align(64))
184)]
185#[repr(C)]
186pub struct CachePadded<T> {
187    value: T,
188}
189
190unsafe impl<T: Send> Send for CachePadded<T> {}
191unsafe impl<T: Sync> Sync for CachePadded<T> {}
192
193impl<T> CachePadded<T> {
194    /// Pads and aligns a value to the length of a cache line.
195    ///
196    /// # Examples
197    ///
198    /// ```
199    /// use crossfire_utils::CachePadded;
200    ///
201    /// let padded_value = CachePadded::new(1);
202    /// ```
203    pub const fn new(t: T) -> Self {
204        Self { value: t }
205    }
206
207    /// Returns the inner value.
208    ///
209    /// # Examples
210    ///
211    /// ```
212    /// use crossfire_utils::CachePadded;
213    ///
214    /// let padded_value = CachePadded::new(7);
215    /// let value = padded_value.into_inner();
216    /// assert_eq!(value, 7);
217    /// ```
218    pub fn into_inner(self) -> T {
219        self.value
220    }
221}
222
223impl<T> Deref for CachePadded<T> {
224    type Target = T;
225
226    fn deref(&self) -> &T {
227        &self.value
228    }
229}
230
231impl<T> DerefMut for CachePadded<T> {
232    fn deref_mut(&mut self) -> &mut T {
233        &mut self.value
234    }
235}
236
237impl<T: fmt::Debug> fmt::Debug for CachePadded<T> {
238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239        f.debug_struct("CachePadded").field("value", &self.value).finish()
240    }
241}
242
243impl<T> From<T> for CachePadded<T> {
244    fn from(t: T) -> Self {
245        Self::new(t)
246    }
247}
248
249impl<T: fmt::Display> fmt::Display for CachePadded<T> {
250    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251        fmt::Display::fmt(&self.value, f)
252    }
253}