Skip to main content

sol_parser_sdk/core/
cache.rs

1//! # 解析器缓存模块
2//!
3//! 提供高性能缓存机制,减少内存分配和重复计算:
4//! - 程序ID缓存:避免重复查找和分配
5//! - 账户公钥缓存:线程局部缓存,零锁竞争
6//!
7//! ## 性能优势
8//!
9//! - **减少 30-50% 内存分配**:通过缓存重用避免重复分配
10//! - **零锁竞争**:线程局部存储,每个线程独立缓存
11//! - **快速查找**:读写锁优化,读操作无阻塞
12//!
13//! ## 使用示例
14//!
15//! ```rust
16//! use sol_parser_sdk::core::cache::build_account_pubkeys_with_cache;
17//! use solana_sdk::pubkey::Pubkey;
18//!
19//! let instruction_accounts = vec![0u8, 1, 2];
20//! let all_accounts = vec![Pubkey::default(); 10];
21//!
22//! // 使用线程局部缓存,避免重复分配
23//! let account_pubkeys = build_account_pubkeys_with_cache(&instruction_accounts, &all_accounts);
24//! ```
25
26use solana_sdk::pubkey::Pubkey;
27use std::cell::RefCell;
28
29// ============================================================================
30// 账户公钥缓存工具(Account Pubkey Cache)
31// ============================================================================
32
33/// 高性能账户公钥缓存
34///
35/// 通过重用内存避免重复Vec分配,提升性能
36#[derive(Debug)]
37pub struct AccountPubkeyCache {
38    /// 预分配的账户公钥向量
39    cache: Vec<Pubkey>,
40}
41
42impl AccountPubkeyCache {
43    /// 创建新的账户公钥缓存
44    ///
45    /// 预分配32个位置,覆盖大多数交易场景
46    pub fn new() -> Self {
47        Self { cache: Vec::with_capacity(32) }
48    }
49
50    /// 从指令账户索引构建账户公钥向量
51    ///
52    /// # 参数
53    /// - `instruction_accounts`: 指令账户索引列表
54    /// - `all_accounts`: 所有账户公钥列表
55    ///
56    /// # 返回
57    /// 账户公钥切片引用
58    ///
59    /// # 性能优化
60    /// - 重用内部缓存,避免重新分配
61    /// - 仅在必要时扩容
62    #[inline]
63    pub fn build_account_pubkeys(
64        &mut self,
65        instruction_accounts: &[u8],
66        all_accounts: &[Pubkey],
67    ) -> &[Pubkey] {
68        self.cache.clear();
69
70        // 确保容量足够,避免动态扩容
71        if self.cache.capacity() < instruction_accounts.len() {
72            self.cache.reserve(instruction_accounts.len() - self.cache.capacity());
73        }
74
75        // 快速填充账户公钥(带边界检查)
76        for &idx in instruction_accounts.iter() {
77            if (idx as usize) < all_accounts.len() {
78                self.cache.push(all_accounts[idx as usize]);
79            }
80        }
81
82        &self.cache
83    }
84}
85
86impl Default for AccountPubkeyCache {
87    fn default() -> Self {
88        Self::new()
89    }
90}
91
92thread_local! {
93    static THREAD_LOCAL_ACCOUNT_CACHE: RefCell<AccountPubkeyCache> =
94        RefCell::new(AccountPubkeyCache::new());
95}
96
97/// 从线程局部缓存构建账户公钥列表
98///
99/// # 参数
100/// - `instruction_accounts`: 指令账户索引列表
101/// - `all_accounts`: 所有账户公钥列表
102///
103/// # 返回
104/// 账户公钥向量
105///
106/// # 线程安全
107/// 使用线程局部存储,每个线程独立缓存
108///
109/// # 性能
110/// - 首次调用:分配缓存(约 1μs)
111/// - 后续调用:重用缓存(约 100ns)
112///
113/// # 示例
114/// ```rust
115/// use sol_parser_sdk::core::cache::build_account_pubkeys_with_cache;
116/// use solana_sdk::pubkey::Pubkey;
117///
118/// let instruction_accounts = vec![0u8, 1, 2];
119/// let all_accounts = vec![Pubkey::default(); 10];
120///
121/// let account_pubkeys = build_account_pubkeys_with_cache(&instruction_accounts, &all_accounts);
122/// assert_eq!(account_pubkeys.len(), 3);
123/// ```
124#[inline]
125pub fn build_account_pubkeys_with_cache(
126    instruction_accounts: &[u8],
127    all_accounts: &[Pubkey],
128) -> Vec<Pubkey> {
129    THREAD_LOCAL_ACCOUNT_CACHE.with(|cache| {
130        let mut cache = cache.borrow_mut();
131        cache.build_account_pubkeys(instruction_accounts, all_accounts).to_vec()
132    })
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn test_account_pubkey_cache_basic() {
141        let mut cache = AccountPubkeyCache::new();
142        let all_accounts = vec![Pubkey::new_unique(), Pubkey::new_unique(), Pubkey::new_unique()];
143        let instruction_accounts = vec![0u8, 2];
144
145        let result = cache.build_account_pubkeys(&instruction_accounts, &all_accounts);
146        assert_eq!(result.len(), 2);
147        assert_eq!(result[0], all_accounts[0]);
148        assert_eq!(result[1], all_accounts[2]);
149    }
150
151    #[test]
152    fn test_account_pubkey_cache_reuse() {
153        let mut cache = AccountPubkeyCache::new();
154        let all_accounts = vec![Pubkey::new_unique(); 10];
155
156        // 第一次调用
157        let instruction_accounts = vec![0u8, 1, 2];
158        let result1 = cache.build_account_pubkeys(&instruction_accounts, &all_accounts);
159        assert_eq!(result1.len(), 3);
160
161        // 第二次调用 - 应该重用缓存
162        let instruction_accounts = vec![5u8, 6];
163        let result2 = cache.build_account_pubkeys(&instruction_accounts, &all_accounts);
164        assert_eq!(result2.len(), 2);
165        assert_eq!(result2[0], all_accounts[5]);
166        assert_eq!(result2[1], all_accounts[6]);
167    }
168
169    #[test]
170    fn test_account_pubkey_cache_out_of_bounds() {
171        let mut cache = AccountPubkeyCache::new();
172        let all_accounts = vec![Pubkey::new_unique(); 3];
173        let instruction_accounts = vec![0u8, 1, 10]; // 10 超出范围
174
175        let result = cache.build_account_pubkeys(&instruction_accounts, &all_accounts);
176        assert_eq!(result.len(), 2); // 只有前两个有效
177    }
178
179    #[test]
180    fn test_thread_local_cache() {
181        let all_accounts = vec![Pubkey::new_unique(); 5];
182        let instruction_accounts = vec![0u8, 1, 2];
183
184        let result = build_account_pubkeys_with_cache(&instruction_accounts, &all_accounts);
185        assert_eq!(result.len(), 3);
186        assert_eq!(result[0], all_accounts[0]);
187        assert_eq!(result[1], all_accounts[1]);
188        assert_eq!(result[2], all_accounts[2]);
189    }
190
191    #[test]
192    fn test_thread_local_cache_multiple_calls() {
193        let all_accounts = vec![Pubkey::new_unique(); 10];
194
195        // 第一次调用
196        let result1 = build_account_pubkeys_with_cache(&[0u8, 1], &all_accounts);
197        assert_eq!(result1.len(), 2);
198
199        // 第二次调用 - 应该重用线程局部缓存
200        let result2 = build_account_pubkeys_with_cache(&[5u8, 6, 7], &all_accounts);
201        assert_eq!(result2.len(), 3);
202    }
203}