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 {
48            cache: Vec::with_capacity(32),
49        }
50    }
51
52    /// 从指令账户索引构建账户公钥向量
53    ///
54    /// # 参数
55    /// - `instruction_accounts`: 指令账户索引列表
56    /// - `all_accounts`: 所有账户公钥列表
57    ///
58    /// # 返回
59    /// 账户公钥切片引用
60    ///
61    /// # 性能优化
62    /// - 重用内部缓存,避免重新分配
63    /// - 仅在必要时扩容
64    #[inline]
65    pub fn build_account_pubkeys(
66        &mut self,
67        instruction_accounts: &[u8],
68        all_accounts: &[Pubkey],
69    ) -> &[Pubkey] {
70        self.cache.clear();
71
72        // 确保容量足够,避免动态扩容
73        if self.cache.capacity() < instruction_accounts.len() {
74            self.cache.reserve(instruction_accounts.len() - self.cache.capacity());
75        }
76
77        // 快速填充账户公钥(带边界检查)
78        for &idx in instruction_accounts.iter() {
79            if (idx as usize) < all_accounts.len() {
80                self.cache.push(all_accounts[idx as usize]);
81            }
82        }
83
84        &self.cache
85    }
86}
87
88impl Default for AccountPubkeyCache {
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94thread_local! {
95    static THREAD_LOCAL_ACCOUNT_CACHE: RefCell<AccountPubkeyCache> =
96        RefCell::new(AccountPubkeyCache::new());
97}
98
99/// 从线程局部缓存构建账户公钥列表
100///
101/// # 参数
102/// - `instruction_accounts`: 指令账户索引列表
103/// - `all_accounts`: 所有账户公钥列表
104///
105/// # 返回
106/// 账户公钥向量
107///
108/// # 线程安全
109/// 使用线程局部存储,每个线程独立缓存
110///
111/// # 性能
112/// - 首次调用:分配缓存(约 1μs)
113/// - 后续调用:重用缓存(约 100ns)
114///
115/// # 示例
116/// ```rust
117/// use sol_parser_sdk::core::cache::build_account_pubkeys_with_cache;
118/// use solana_sdk::pubkey::Pubkey;
119///
120/// let instruction_accounts = vec![0u8, 1, 2];
121/// let all_accounts = vec![Pubkey::default(); 10];
122///
123/// let account_pubkeys = build_account_pubkeys_with_cache(&instruction_accounts, &all_accounts);
124/// assert_eq!(account_pubkeys.len(), 3);
125/// ```
126#[inline]
127pub fn build_account_pubkeys_with_cache(
128    instruction_accounts: &[u8],
129    all_accounts: &[Pubkey],
130) -> Vec<Pubkey> {
131    THREAD_LOCAL_ACCOUNT_CACHE.with(|cache| {
132        let mut cache = cache.borrow_mut();
133        cache.build_account_pubkeys(instruction_accounts, all_accounts).to_vec()
134    })
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn test_account_pubkey_cache_basic() {
143        let mut cache = AccountPubkeyCache::new();
144        let all_accounts = vec![Pubkey::new_unique(), Pubkey::new_unique(), Pubkey::new_unique()];
145        let instruction_accounts = vec![0u8, 2];
146
147        let result = cache.build_account_pubkeys(&instruction_accounts, &all_accounts);
148        assert_eq!(result.len(), 2);
149        assert_eq!(result[0], all_accounts[0]);
150        assert_eq!(result[1], all_accounts[2]);
151    }
152
153    #[test]
154    fn test_account_pubkey_cache_reuse() {
155        let mut cache = AccountPubkeyCache::new();
156        let all_accounts = vec![Pubkey::new_unique(); 10];
157
158        // 第一次调用
159        let instruction_accounts = vec![0u8, 1, 2];
160        let result1 = cache.build_account_pubkeys(&instruction_accounts, &all_accounts);
161        assert_eq!(result1.len(), 3);
162
163        // 第二次调用 - 应该重用缓存
164        let instruction_accounts = vec![5u8, 6];
165        let result2 = cache.build_account_pubkeys(&instruction_accounts, &all_accounts);
166        assert_eq!(result2.len(), 2);
167        assert_eq!(result2[0], all_accounts[5]);
168        assert_eq!(result2[1], all_accounts[6]);
169    }
170
171    #[test]
172    fn test_account_pubkey_cache_out_of_bounds() {
173        let mut cache = AccountPubkeyCache::new();
174        let all_accounts = vec![Pubkey::new_unique(); 3];
175        let instruction_accounts = vec![0u8, 1, 10]; // 10 超出范围
176
177        let result = cache.build_account_pubkeys(&instruction_accounts, &all_accounts);
178        assert_eq!(result.len(), 2); // 只有前两个有效
179    }
180
181    #[test]
182    fn test_thread_local_cache() {
183        let all_accounts = vec![Pubkey::new_unique(); 5];
184        let instruction_accounts = vec![0u8, 1, 2];
185
186        let result = build_account_pubkeys_with_cache(&instruction_accounts, &all_accounts);
187        assert_eq!(result.len(), 3);
188        assert_eq!(result[0], all_accounts[0]);
189        assert_eq!(result[1], all_accounts[1]);
190        assert_eq!(result[2], all_accounts[2]);
191    }
192
193    #[test]
194    fn test_thread_local_cache_multiple_calls() {
195        let all_accounts = vec![Pubkey::new_unique(); 10];
196
197        // 第一次调用
198        let result1 = build_account_pubkeys_with_cache(&[0u8, 1], &all_accounts);
199        assert_eq!(result1.len(), 2);
200
201        // 第二次调用 - 应该重用线程局部缓存
202        let result2 = build_account_pubkeys_with_cache(&[5u8, 6, 7], &all_accounts);
203        assert_eq!(result2.len(), 3);
204    }
205}