Skip to main content

god_graph/utils/
cache.rs

1//! Cache 优化和预取工具
2//!
3//! 提供 CPU 缓存优化功能,包括:
4//! - 64 字节对齐的填充类型,避免多线程访问时的 false sharing
5//! - 软件预取指令,提前将数据加载到 CPU 缓存
6//!
7//! ## 使用示例
8//!
9//! ```rust
10//! use god_gragh::utils::cache::{Padded, prefetch_read};
11//!
12//! // 创建对齐的填充值
13//! let padded = Padded::new(42);
14//! assert_eq!(*padded, 42);
15//!
16//! // 预取数据到缓存
17//! let data = vec![1, 2, 3, 4, 5];
18//! prefetch_read(&data[0]);
19//! ```
20
21/// 64 字节对齐的填充类型,避免 false sharing
22///
23/// 在多线程环境中,当多个线程访问相邻内存时,
24/// 即使访问不同的变量,也可能因为缓存行共享而导致性能下降。
25/// `Padded<T>` 通过将数据填充到 64 字节(缓存行大小)来避免这个问题。
26///
27/// ## 类型参数
28///
29/// - `T`: 要包装的数据类型
30///
31/// ## 示例
32///
33/// ```rust
34/// use god_gragh::utils::cache::Padded;
35///
36/// let padded = Padded::new(42);
37/// assert_eq!(*padded, 42);
38/// assert!(Padded::<u8>::size() >= 64); // 至少 64 字节
39/// ```
40#[repr(align(64))]
41#[derive(Debug, Clone, Copy)]
42pub struct Padded<T> {
43    /// 包装的数据
44    pub data: T,
45    /// 填充字节,确保 64 字节对齐
46    _padding: [u8; 64],
47}
48
49impl<T> Padded<T> {
50    /// 创建新的 Padded 包装
51    ///
52    /// ## 参数
53    ///
54    /// - `data`: 要包装的数据
55    ///
56    /// ## 示例
57    ///
58    /// ```rust
59    /// use god_gragh::utils::cache::Padded;
60    ///
61    /// let padded = Padded::new(42);
62    /// assert_eq!(padded.data, 42);
63    /// ```
64    pub fn new(data: T) -> Self {
65        Self {
66            data,
67            _padding: [0; 64],
68        }
69    }
70
71    /// 获取 Padded 结构的大小(字节)
72    ///
73    /// 返回值总是 >= 64,因为缓存行对齐
74    pub fn size() -> usize {
75        core::mem::size_of::<Self>()
76    }
77
78    /// 验证指针是否 64 字节对齐
79    ///
80    /// ## 参数
81    ///
82    /// - `ptr`: 要验证的指针
83    ///
84    /// ## 返回
85    ///
86    /// - `true`: 对齐正确
87    /// - `false`: 对齐错误
88    ///
89    /// ## 示例
90    ///
91    /// ```rust
92    /// use god_gragh::utils::cache::Padded;
93    ///
94    /// let padded = Padded::new(42);
95    /// assert!(Padded::is_aligned(&padded));
96    /// ```
97    pub fn is_aligned(ptr: &T) -> bool {
98        (ptr as *const T as usize) % 64 == 0
99    }
100
101    /// 验证 Vec 中 T 的指针是否 64 字节对齐
102    ///
103    /// ## 参数
104    ///
105    /// - `vec`: 要验证的 Vec
106    ///
107    /// ## 返回
108    ///
109    /// - `true`: 对齐正确
110    /// - `false`: 对齐错误
111    ///
112    /// ## 示例
113    ///
114    /// ```rust
115    /// use god_gragh::utils::cache::Padded;
116    ///
117    /// let vec: Vec<Padded<u8>> = vec![Padded::new(1), Padded::new(2)];
118    /// assert!(Padded::is_vec_aligned(&vec));
119    /// ```
120    pub fn is_vec_aligned(vec: &[T]) -> bool {
121        if vec.is_empty() {
122            return true;
123        }
124        (vec.as_ptr() as usize) % 64 == 0
125    }
126}
127
128impl<T> core::ops::Deref for Padded<T> {
129    type Target = T;
130
131    fn deref(&self) -> &Self::Target {
132        &self.data
133    }
134}
135
136impl<T> core::ops::DerefMut for Padded<T> {
137    fn deref_mut(&mut self) -> &mut Self::Target {
138        &mut self.data
139    }
140}
141
142impl<T: Default> Default for Padded<T> {
143    fn default() -> Self {
144        Self::new(T::default())
145    }
146}
147
148/// 预取数据到 CPU 缓存用于读取
149///
150/// 使用 CPU 的 prefetch 指令提前将数据加载到缓存中,
151/// 可以减少后续访问的延迟。这是一个性能优化提示,
152/// 在某些架构上可能被忽略。
153///
154/// ## 参数
155///
156/// - `data`: 要预取的数据
157///
158/// ## 平台支持
159///
160/// - x86_64: 使用 `_mm_prefetch` 指令
161/// - 其他架构:使用 `std::hint::prefetch_read_data`(需要 std)
162///
163/// ## 示例
164///
165/// ```rust
166/// use god_gragh::utils::cache::prefetch_read;
167///
168/// let data = vec![1, 2, 3, 4, 5];
169/// prefetch_read(&data[0]);
170/// // 现在访问 data[0] 可能更快
171/// ```
172#[inline]
173pub fn prefetch_read<T>(data: &T) {
174    #[cfg(target_arch = "x86_64")]
175    // SAFETY: `_mm_prefetch` 是 CPU 内置指令,仅读取缓存不修改内存,
176    // 指针由 Rust 引用转换而来,保证有效且对齐
177    unsafe {
178        core::arch::x86_64::_mm_prefetch(
179            data as *const T as *const i8,
180            core::arch::x86_64::_MM_HINT_T0,
181        );
182    }
183
184    #[cfg(not(target_arch = "x86_64"))]
185    {
186        #[cfg(feature = "std")]
187        // SAFETY: `prefetch_read_data` 仅预取数据到缓存,不修改内存,
188        // 指针有效且大小正确
189        unsafe {
190            std::hint::prefetch_read_data(data as *const T as *const _, core::mem::size_of::<T>());
191        }
192    }
193}
194
195/// 预取数据到 CPU 缓存用于写入
196///
197/// 使用 CPU 的 prefetch 指令提前将数据加载到缓存中,
198/// 为后续的写入操作做准备。
199///
200/// ## 参数
201///
202/// - `data`: 要预取的数据
203///
204/// ## 平台支持
205///
206/// - x86_64: 使用 `_mm_prefetch` 指令
207/// - 其他架构:无操作(空实现)
208///
209/// ## 示例
210///
211/// ```rust
212/// use god_gragh::utils::cache::prefetch_write;
213///
214/// let mut data = vec![1, 2, 3, 4, 5];
215/// prefetch_write(&mut data[0]);
216/// // 现在写入 data[0] 可能更快
217/// ```
218#[inline]
219pub fn prefetch_write<T>(data: &mut T) {
220    #[cfg(target_arch = "x86_64")]
221    // SAFETY: `_mm_prefetch` 是 CPU 内置指令,预取到缓存用于后续写入,
222    // 可变引用保证指针有效且独占访问
223    unsafe {
224        core::arch::x86_64::_mm_prefetch(
225            data as *mut T as *const i8,
226            core::arch::x86_64::_MM_HINT_T0,
227        );
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234
235    #[test]
236    fn test_padded_size() {
237        assert!(Padded::<u8>::size() >= 64);
238    }
239
240    #[test]
241    fn test_padded_deref() {
242        let padded = Padded::new(42);
243        assert_eq!(*padded, 42);
244
245        let mut padded = Padded::new(42);
246        *padded = 43;
247        assert_eq!(padded.data, 43);
248    }
249}