diff --git a/src/common/fast_timing.rs b/src/common/fast_timing.rs new file mode 100644 index 0000000..8bb9c5a --- /dev/null +++ b/src/common/fast_timing.rs @@ -0,0 +1,198 @@ +//! 🚀 快速计时模块 - 减少 Instant::now() 系统调用开销 +//! +//! 使用 syscall_bypass 提供的快速时间戳避免频繁的系统调用 + +use std::time::{Duration, Instant}; +use once_cell::sync::Lazy; +use crate::perf::syscall_bypass::SystemCallBypassManager; + +/// 全局快速时间提供器 +static FAST_TIMER: Lazy = Lazy::new(|| FastTimer::new()); + +/// 快速计时器 - 减少系统调用开销 +pub struct FastTimer { + bypass_manager: SystemCallBypassManager, + _base_instant: Instant, + _base_nanos: u64, +} + +impl FastTimer { + fn new() -> Self { + use crate::perf::syscall_bypass::SyscallBypassConfig; + + let bypass_manager = SystemCallBypassManager::new(SyscallBypassConfig::default()) + .expect("Failed to create SystemCallBypassManager"); + + let base_instant = Instant::now(); + let base_nanos = bypass_manager.fast_timestamp_nanos(); + + Self { + bypass_manager, + _base_instant: base_instant, + _base_nanos: base_nanos, + } + } + + /// 🚀 获取当前时间戳(纳秒) - 使用快速系统调用绕过 + #[inline(always)] + pub fn now_nanos(&self) -> u64 { + self.bypass_manager.fast_timestamp_nanos() + } + + /// 🚀 获取当前时间戳(微秒) + #[inline(always)] + pub fn now_micros(&self) -> u64 { + self.now_nanos() / 1_000 + } + + /// 🚀 获取当前时间戳(毫秒) + #[inline(always)] + pub fn now_millis(&self) -> u64 { + self.now_nanos() / 1_000_000 + } + + /// 🚀 计算从开始到现在的耗时(纳秒) + #[inline(always)] + pub fn elapsed_nanos(&self, start_nanos: u64) -> u64 { + self.now_nanos().saturating_sub(start_nanos) + } + + /// 🚀 计算从开始到现在的耗时(Duration) + #[inline(always)] + pub fn elapsed_duration(&self, start_nanos: u64) -> Duration { + Duration::from_nanos(self.elapsed_nanos(start_nanos)) + } +} + +/// 🚀 快速获取当前时间戳(纳秒)- 全局函数 +/// +/// 使用 syscall_bypass 避免频繁的 clock_gettime 系统调用 +#[inline(always)] +pub fn fast_now_nanos() -> u64 { + FAST_TIMER.now_nanos() +} + +/// 🚀 快速获取当前时间戳(微秒) +#[inline(always)] +pub fn fast_now_micros() -> u64 { + FAST_TIMER.now_micros() +} + +/// 🚀 快速获取当前时间戳(毫秒) +#[inline(always)] +pub fn fast_now_millis() -> u64 { + FAST_TIMER.now_millis() +} + +/// 🚀 计算耗时(纳秒) +#[inline(always)] +pub fn fast_elapsed_nanos(start_nanos: u64) -> u64 { + FAST_TIMER.elapsed_nanos(start_nanos) +} + +/// 🚀 计算耗时(Duration) +#[inline(always)] +pub fn fast_elapsed(start_nanos: u64) -> Duration { + FAST_TIMER.elapsed_duration(start_nanos) +} + +/// 快速计时器句柄 - 用于测量代码块耗时 +pub struct FastStopwatch { + start_nanos: u64, + #[allow(dead_code)] + label: &'static str, +} + +impl FastStopwatch { + /// 创建并启动计时器 + #[inline(always)] + pub fn start(label: &'static str) -> Self { + Self { + start_nanos: fast_now_nanos(), + label, + } + } + + /// 获取已耗时(纳秒) + #[inline(always)] + pub fn elapsed_nanos(&self) -> u64 { + fast_elapsed_nanos(self.start_nanos) + } + + /// 获取已耗时(Duration) + #[inline(always)] + pub fn elapsed(&self) -> Duration { + fast_elapsed(self.start_nanos) + } + + /// 获取已耗时(微秒) + #[inline(always)] + pub fn elapsed_micros(&self) -> u64 { + self.elapsed_nanos() / 1_000 + } + + /// 获取已耗时(毫秒) + #[inline(always)] + pub fn elapsed_millis(&self) -> u64 { + self.elapsed_nanos() / 1_000_000 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_fast_timing() { + let start = fast_now_nanos(); + std::thread::sleep(Duration::from_millis(10)); + let elapsed = fast_elapsed_nanos(start); + + // 应该大约是 10ms = 10,000,000 纳秒 + assert!(elapsed >= 9_000_000 && elapsed <= 12_000_000); + } + + #[test] + fn test_stopwatch() { + let sw = FastStopwatch::start("test"); + std::thread::sleep(Duration::from_millis(10)); + let elapsed_ms = sw.elapsed_millis(); + + assert!(elapsed_ms >= 9 && elapsed_ms <= 12); + } + + #[test] + fn test_fast_now_overhead() { + // 测试调用开销 + let iterations = 10_000; + let start = Instant::now(); + + for _ in 0..iterations { + let _ = fast_now_nanos(); + } + + let total_elapsed = start.elapsed(); + let avg_per_call = total_elapsed.as_nanos() / iterations; + + println!("Average fast_now_nanos() call: {}ns", avg_per_call); + + // 快速时间戳应该非常快(< 100ns per call) + assert!(avg_per_call < 100); + } + + #[test] + fn test_instant_now_overhead() { + // 对比标准 Instant::now() 的开销 + let iterations = 10_000; + let start = Instant::now(); + + for _ in 0..iterations { + let _ = Instant::now(); + } + + let total_elapsed = start.elapsed(); + let avg_per_call = total_elapsed.as_nanos() / iterations; + + println!("Average Instant::now() call: {}ns", avg_per_call); + } +} diff --git a/src/common/mod.rs b/src/common/mod.rs index 2918d23..a6e9ca6 100755 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,6 +1,7 @@ pub mod address_lookup_cache; pub mod bonding_curve; pub mod fast_fn; +pub mod fast_timing; pub mod gas_fee_strategy; pub mod global; pub mod nonce_cache; diff --git a/src/trading/core/parallel.rs b/src/trading/core/parallel.rs index 4e2e8c6..a6d3012 100755 --- a/src/trading/core/parallel.rs +++ b/src/trading/core/parallel.rs @@ -88,7 +88,9 @@ async fn parallel_execute( { return Err(anyhow!("No Rpc Default Swqos configured.")); } + // 🚀 获取 CPU 核心并优化亲和性分配 let cores = core_affinity::get_core_ids().unwrap(); + let _num_cores = cores.len(); let mut handles: Vec)>>> = Vec::with_capacity(swqos_clients.len()); diff --git a/src/trading/core/transaction_pool.rs b/src/trading/core/transaction_pool.rs index 520f1c2..b2b841d 100644 --- a/src/trading/core/transaction_pool.rs +++ b/src/trading/core/transaction_pool.rs @@ -39,6 +39,30 @@ impl PreallocatedTxBuilder { } /// 🚀 零分配构建交易 + /// + /// # 交易版本自动选择 + /// + /// - **有地址查找表** (`lookup_table = Some`): 使用 `VersionedMessage::V0` + /// - 支持地址查找表压缩 + /// - 减少交易大小 + /// - 需要 RPC 支持 V0 + /// + /// - **无地址查找表** (`lookup_table = None`): 使用 `VersionedMessage::Legacy` + /// - 兼容所有 RPC 节点 + /// - 无需地址查找表支持 + /// - 适用于简单交易 + /// + /// # 示例 + /// + /// ```rust,ignore + /// // 无查找表 -> Legacy 消息 + /// let msg = builder.build_zero_alloc(&payer, &ixs, None, blockhash); + /// assert!(matches!(msg, VersionedMessage::Legacy(_))); + /// + /// // 有查找表 -> V0 消息 + /// let msg = builder.build_zero_alloc(&payer, &ixs, Some(table_key), blockhash); + /// assert!(matches!(msg, VersionedMessage::V0(_))); + /// ``` #[inline(always)] pub fn build_zero_alloc( &mut self, @@ -51,7 +75,7 @@ impl PreallocatedTxBuilder { self.reset(); self.instructions.extend_from_slice(instructions); - // 如果有查找表,使用 V0 消息 + // ✅ 如果有查找表,使用 V0 消息 if let Some(table_key) = lookup_table { self.lookup_tables.push(v0::MessageAddressTableLookup { account_key: table_key, @@ -73,7 +97,7 @@ impl PreallocatedTxBuilder { VersionedMessage::V0(message) } else { - // 没有查找表,使用 legacy 消息 + // ✅ 没有查找表,使用 Legacy 消息(兼容所有 RPC) let message = Message::new_with_blockhash( &self.instructions, Some(payer), @@ -170,4 +194,47 @@ mod tests { let final_count = get_pool_stats().0; assert_eq!(final_count, initial_count); } + + #[test] + fn test_message_version_selection() { + use solana_sdk::signature::Keypair; + use solana_sdk::system_instruction; + + let payer = Keypair::new(); + let recipient = Keypair::new(); + let blockhash = Hash::default(); + + let instructions = vec![ + system_instruction::transfer(&payer.pubkey(), &recipient.pubkey(), 1000) + ]; + + let mut builder = PreallocatedTxBuilder::new(); + + // 测试1: 无查找表 -> 应该返回 Legacy 消息 + let msg_no_lookup = builder.build_zero_alloc( + &payer.pubkey(), + &instructions, + None, // ← 无查找表 + blockhash, + ); + + assert!( + matches!(msg_no_lookup, VersionedMessage::Legacy(_)), + "Without lookup table, should use Legacy message" + ); + + // 测试2: 有查找表 -> 应该返回 V0 消息 + let lookup_table_key = Pubkey::new_unique(); + let msg_with_lookup = builder.build_zero_alloc( + &payer.pubkey(), + &instructions, + Some(lookup_table_key), // ← 有查找表 + blockhash, + ); + + assert!( + matches!(msg_with_lookup, VersionedMessage::V0(_)), + "With lookup table, should use V0 message" + ); + } } diff --git a/src/utils/calc/common.rs b/src/utils/calc/common.rs index 88fedb5..32b65a0 100644 --- a/src/utils/calc/common.rs +++ b/src/utils/calc/common.rs @@ -9,7 +9,8 @@ /// * fee_basis_points = 10 -> 0.1% fee /// * fee_basis_points = 25 -> 0.25% fee (common exchange rate) /// * fee_basis_points = 100 -> 1% fee -pub fn compute_fee(amount: u128, fee_basis_points: u128) -> u128 { +#[inline(always)] +pub const fn compute_fee(amount: u128, fee_basis_points: u128) -> u128 { ceil_div(amount * fee_basis_points, 10_000) } @@ -22,7 +23,8 @@ pub fn compute_fee(amount: u128, fee_basis_points: u128) -> u128 { /// /// # Returns /// Returns the ceiling result of a/b -pub fn ceil_div(a: u128, b: u128) -> u128 { +#[inline(always)] +pub const fn ceil_div(a: u128, b: u128) -> u128 { (a + b - 1) / b } @@ -35,10 +37,11 @@ pub fn ceil_div(a: u128, b: u128) -> u128 { /// /// # Examples /// * basis_points = 1 -> 0.01% slippage -/// * basis_points = 10 -> 0.1% slippage +/// * basis_points = 10 -> 0.1% slippage /// * basis_points = 100 -> 1% slippage /// * basis_points = 500 -> 5% slippage -pub fn calculate_with_slippage_buy(amount: u64, basis_points: u64) -> u64 { +#[inline(always)] +pub const fn calculate_with_slippage_buy(amount: u64, basis_points: u64) -> u64 { amount + (amount * basis_points / 10000) } @@ -51,10 +54,11 @@ pub fn calculate_with_slippage_buy(amount: u64, basis_points: u64) -> u64 { /// /// # Examples /// * basis_points = 1 -> 0.01% slippage -/// * basis_points = 10 -> 0.1% slippage +/// * basis_points = 10 -> 0.1% slippage /// * basis_points = 100 -> 1% slippage /// * basis_points = 500 -> 5% slippage -pub fn calculate_with_slippage_sell(amount: u64, basis_points: u64) -> u64 { +#[inline(always)] +pub const fn calculate_with_slippage_sell(amount: u64, basis_points: u64) -> u64 { if amount <= basis_points / 10000 { 1 } else { diff --git a/src/utils/calc/pumpfun.rs b/src/utils/calc/pumpfun.rs index a15f94d..986017e 100644 --- a/src/utils/calc/pumpfun.rs +++ b/src/utils/calc/pumpfun.rs @@ -17,6 +17,7 @@ use crate::{ /// /// # Returns /// The amount of tokens that will be received (in token's smallest unit) +#[inline] pub fn get_buy_token_amount_from_sol_amount( virtual_token_reserves: u128, virtual_sol_reserves: u128, @@ -74,6 +75,7 @@ pub fn get_buy_token_amount_from_sol_amount( /// /// # Returns /// The amount of SOL that will be received after fees (in lamports) +#[inline] pub fn get_sell_sol_amount_from_token_amount( virtual_token_reserves: u128, virtual_sol_reserves: u128, diff --git a/src/utils/calc/raydium_cpmm.rs b/src/utils/calc/raydium_cpmm.rs index 924a876..ea06ba9 100644 --- a/src/utils/calc/raydium_cpmm.rs +++ b/src/utils/calc/raydium_cpmm.rs @@ -10,6 +10,7 @@ use crate::instruction::utils::raydium_cpmm::accounts::{ /// /// # Returns /// The calculated trading fee +#[inline(always)] fn compute_trading_fee(amount: u64, fee_rate: u64) -> u64 { let numerator = (amount as u128) * (fee_rate as u128); ((numerator + FEE_RATE_DENOMINATOR_VALUE - 1) / FEE_RATE_DENOMINATOR_VALUE) as u64 @@ -23,6 +24,7 @@ fn compute_trading_fee(amount: u64, fee_rate: u64) -> u64 { /// /// # Returns /// The calculated protocol or fund fee +#[inline(always)] fn compute_protocol_fund_fee(amount: u64, fee_rate: u64) -> u64 { let numerator = (amount as u128) * (fee_rate as u128); (numerator / FEE_RATE_DENOMINATOR_VALUE) as u64 @@ -36,6 +38,7 @@ fn compute_protocol_fund_fee(amount: u64, fee_rate: u64) -> u64 { /// /// # Returns /// The calculated creator fee +#[inline(always)] fn compute_creator_fee_new(amount: u64, fee_rate: u64) -> u64 { let numerator = (amount as u128) * (fee_rate as u128); ((numerator + FEE_RATE_DENOMINATOR_VALUE - 1) / FEE_RATE_DENOMINATOR_VALUE) as u64 @@ -93,6 +96,7 @@ pub struct SwapResult { /// /// # Returns /// A `SwapResult` containing all swap calculations and fees +#[inline] fn swap_base_input( input_amount: u64, input_vault_amount: u64, @@ -155,6 +159,7 @@ fn swap_base_input( /// /// # Returns /// A `ComputeSwapParams` struct containing all computed swap parameters +#[inline] pub fn compute_swap_amount( base_reserve: u64, quote_reserve: u64,