Release v3.5.0: performance, constants, bilingual docs
- Bump version to 3.5.0 - Performance: hot-path timing only when log_enabled/simulate; execute_parallel takes &[Arc<SwqosClient>]; shared HTTP client constants for SWQoS - Code quality: validate_protocol_params extracted for buy/sell; BYTES_PER_ACCOUNT, MAX_INSTRUCTIONS_WARN, HTTP timeout constants; prefetch/syscall bypass comments - Documentation: bilingual (EN + 中文) doc comments in execution, executor, perf, swqos; README/README_CN version and What's new in 3.5.0 - Add release_notes_v3.5.0.md Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
//! High-performance clock (same design as sol-parser-sdk for consistent grpc_recv_us vs "now").
|
||||
//!
|
||||
//! Uses monotonic clock + base UTC timestamp to avoid frequent syscalls; aligned with sol-parser-sdk
|
||||
//! so event-side grpc_recv_us and SDK-side now_micros() share the same time scale.
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
/// High-performance clock: monotonic + base UTC microsecond timestamp.
|
||||
#[derive(Debug)]
|
||||
pub struct HighPerformanceClock {
|
||||
base_instant: Instant,
|
||||
base_timestamp_us: i64,
|
||||
last_calibration: Instant,
|
||||
calibration_interval_secs: u64,
|
||||
}
|
||||
|
||||
impl HighPerformanceClock {
|
||||
/// Calibrate every 5 minutes by default.
|
||||
pub fn new() -> Self {
|
||||
Self::new_with_calibration_interval(300)
|
||||
}
|
||||
|
||||
/// Sample multiple times and use the lowest-latency baseline to reduce init error.
|
||||
pub fn new_with_calibration_interval(calibration_interval_secs: u64) -> Self {
|
||||
let mut best_offset = i64::MAX;
|
||||
let mut best_instant = Instant::now();
|
||||
let mut best_timestamp = chrono::Utc::now().timestamp_micros();
|
||||
|
||||
for _ in 0..3 {
|
||||
let instant_before = Instant::now();
|
||||
let timestamp = chrono::Utc::now().timestamp_micros();
|
||||
let instant_after = Instant::now();
|
||||
let sample_latency = instant_after.duration_since(instant_before).as_nanos() as i64;
|
||||
if sample_latency < best_offset {
|
||||
best_offset = sample_latency;
|
||||
best_instant = instant_before;
|
||||
best_timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
base_instant: best_instant,
|
||||
base_timestamp_us: best_timestamp,
|
||||
last_calibration: best_instant,
|
||||
calibration_interval_secs,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn now_micros(&self) -> i64 {
|
||||
let elapsed = self.base_instant.elapsed();
|
||||
self.base_timestamp_us + elapsed.as_micros() as i64
|
||||
}
|
||||
|
||||
/// Recalibrate when needed to prevent drift.
|
||||
pub fn now_micros_with_calibration(&mut self) -> i64 {
|
||||
if self.last_calibration.elapsed().as_secs() >= self.calibration_interval_secs {
|
||||
self.recalibrate();
|
||||
}
|
||||
self.now_micros()
|
||||
}
|
||||
|
||||
fn recalibrate(&mut self) {
|
||||
let current_monotonic = Instant::now();
|
||||
let current_utc = chrono::Utc::now().timestamp_micros();
|
||||
let expected_utc = self.base_timestamp_us
|
||||
+ current_monotonic.duration_since(self.base_instant).as_micros() as i64;
|
||||
let drift_us = current_utc - expected_utc;
|
||||
if drift_us.abs() > 1000 {
|
||||
self.base_instant = current_monotonic;
|
||||
self.base_timestamp_us = current_utc;
|
||||
}
|
||||
self.last_calibration = current_monotonic;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HighPerformanceClock {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
static HIGH_PERF_CLOCK: once_cell::sync::OnceCell<HighPerformanceClock> =
|
||||
once_cell::sync::OnceCell::new();
|
||||
|
||||
/// Current time in microseconds (UTC scale); same as sol-parser-sdk clock::now_micros for comparable grpc_recv_us.
|
||||
#[inline(always)]
|
||||
pub fn now_micros() -> i64 {
|
||||
let clock = HIGH_PERF_CLOCK.get_or_init(HighPerformanceClock::new);
|
||||
clock.now_micros()
|
||||
}
|
||||
|
||||
/// Elapsed microseconds from start_timestamp_us to now.
|
||||
#[inline(always)]
|
||||
pub fn elapsed_micros_since(start_timestamp_us: i64) -> i64 {
|
||||
now_micros() - start_timestamp_us
|
||||
}
|
||||
@@ -174,7 +174,9 @@ mod tests {
|
||||
let total_elapsed = start.elapsed();
|
||||
let avg_per_call = total_elapsed.as_nanos() / iterations;
|
||||
|
||||
println!("Average fast_now_nanos() call: {}ns", avg_per_call);
|
||||
if crate::common::sdk_log::sdk_log_enabled() {
|
||||
println!("Average fast_now_nanos() call: {}ns", avg_per_call);
|
||||
}
|
||||
|
||||
// 快速时间戳应该非常快(< 100ns per call)
|
||||
assert!(avg_per_call < 100);
|
||||
@@ -193,6 +195,8 @@ mod tests {
|
||||
let total_elapsed = start.elapsed();
|
||||
let avg_per_call = total_elapsed.as_nanos() / iterations;
|
||||
|
||||
println!("Average Instant::now() call: {}ns", avg_per_call);
|
||||
if crate::common::sdk_log::sdk_log_enabled() {
|
||||
println!("Average Instant::now() call: {}ns", avg_per_call);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +354,9 @@ impl GasFeeStrategy {
|
||||
/// 打印所有策略。
|
||||
/// Print all strategies
|
||||
pub fn print_all_strategies(&self) {
|
||||
if !crate::common::sdk_log::sdk_log_enabled() {
|
||||
return;
|
||||
}
|
||||
for strategy in self.get_strategies(TradeType::Buy) {
|
||||
println!("[buy] - {:?}", strategy);
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,5 +1,8 @@
|
||||
pub mod address_lookup;
|
||||
pub mod bonding_curve;
|
||||
pub mod clock;
|
||||
pub mod fast_fn;
|
||||
pub mod sdk_log;
|
||||
pub mod fast_timing;
|
||||
pub mod gas_fee_strategy;
|
||||
pub mod global;
|
||||
@@ -10,7 +13,6 @@ pub mod spl_token;
|
||||
pub mod spl_token_2022;
|
||||
pub mod subscription_handle;
|
||||
pub mod types;
|
||||
pub mod address_lookup;
|
||||
|
||||
pub use gas_fee_strategy::*;
|
||||
pub use types::*;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//! sol-trade-sdk global log switch
|
||||
//!
|
||||
//! Controlled by `TradeConfig::log_enabled`, set in `TradingClient::new`.
|
||||
//! All SDK logs (timing, SWQOS submit/confirm, WSOL, blacklist, etc.) should check this before output.
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
static SDK_LOG_ENABLED: AtomicBool = AtomicBool::new(true);
|
||||
|
||||
/// Whether SDK logging is enabled (set from TradeConfig.log_enabled in TradingClient::new).
|
||||
#[inline(always)]
|
||||
pub fn sdk_log_enabled() -> bool {
|
||||
SDK_LOG_ENABLED.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Set the SDK global log switch (only called from TradingClient::new).
|
||||
pub fn set_sdk_log_enabled(enabled: bool) {
|
||||
SDK_LOG_ENABLED.store(enabled, Ordering::Relaxed);
|
||||
}
|
||||
+12
-4
@@ -72,6 +72,10 @@ pub struct TradeConfig {
|
||||
pub create_wsol_ata_on_startup: bool,
|
||||
/// Whether to use seed optimization for all ATA operations (default: true)
|
||||
pub use_seed_optimize: bool,
|
||||
/// Whether to pin parallel submit tasks to CPU cores (can reduce latency; set false in containers). Default true.
|
||||
pub use_core_affinity: bool,
|
||||
/// Whether to output all SDK logs (timing, SWQOS submit/confirm, WSOL, blacklist, etc.). Default true.
|
||||
pub log_enabled: bool,
|
||||
}
|
||||
|
||||
impl TradeConfig {
|
||||
@@ -80,14 +84,18 @@ impl TradeConfig {
|
||||
swqos_configs: Vec<SwqosConfig>,
|
||||
commitment: CommitmentConfig,
|
||||
) -> Self {
|
||||
println!("🔧 TradeConfig create_wsol_ata_on_startup default value: true");
|
||||
println!("🔧 TradeConfig use_seed_optimize default value: true");
|
||||
if crate::common::sdk_log::sdk_log_enabled() {
|
||||
println!("🔧 TradeConfig create_wsol_ata_on_startup default: true");
|
||||
println!("🔧 TradeConfig use_seed_optimize default: true");
|
||||
}
|
||||
Self {
|
||||
rpc_url,
|
||||
swqos_configs,
|
||||
commitment,
|
||||
create_wsol_ata_on_startup: true, // 默认:启动时检查并创建
|
||||
use_seed_optimize: true, // 默认:使用seed优化
|
||||
create_wsol_ata_on_startup: true, // default: check and create on startup
|
||||
use_seed_optimize: true, // default: use seed optimization
|
||||
use_core_affinity: true, // default: pin parallel submit tasks to cores
|
||||
log_enabled: true, // default: enable all SDK logs
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user