performance optimization
This commit is contained in:
@@ -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<FastTimer> = 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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<JoinHandle<Result<(bool, Signature, Option<anyhow::Error>)>>> =
|
||||
Vec::with_capacity(swqos_clients.len());
|
||||
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user