Release v3.6.4: bump version, update README (EN/CN), ensure all examples build

- Bump version to 3.6.4 in Cargo.toml
- Update version references in README.md and README_CN.md
- Add RELEASE_v3.6.4.md with English release notes
- All workspace examples verified to compile (cargo build --workspace)
- Remove RELEASE_v3.6.3.md and docs/ASYNC_EXECUTOR_REVIEW.md
- Minor updates in types, lib, transaction_builder, async_executor, executor, params

Made-with: Cursor
This commit is contained in:
Wood
2026-03-17 04:52:27 +08:00
parent 667d6d2c5b
commit 7d2ecd57e9
12 changed files with 188 additions and 139 deletions
+69 -13
View File
@@ -90,6 +90,10 @@ pub struct TradingInfrastructure {
pub swqos_clients: Arc<Vec<Arc<SwqosClient>>>,
/// Configuration used to create this infrastructure
pub config: InfrastructureConfig,
/// Precomputed at init: min(swqos_clients.len(), 2/3 * num_cores). Not computed on trade hot path.
pub max_sender_concurrency: usize,
/// Precomputed at init: first max_sender_concurrency CoreIds for job affinity. Empty if no cores. Not computed on trade hot path.
pub effective_core_ids: Arc<Vec<core_affinity::CoreId>>,
}
impl TradingInfrastructure {
@@ -175,10 +179,23 @@ impl TradingInfrastructure {
}
}
let swqos_count = swqos_clients.len();
let (max_sender_concurrency, effective_core_ids) = {
let num_cores = core_affinity::get_core_ids().map(|c| c.len()).unwrap_or(0);
let max_by_cores = (num_cores * 2 / 3).max(1);
let cap = swqos_count.min(max_by_cores).max(1);
let ids = core_affinity::get_core_ids()
.map(|all| all.into_iter().take(cap).collect::<Vec<_>>())
.unwrap_or_default();
(cap, Arc::new(ids))
};
Self {
rpc,
swqos_clients: Arc::new(swqos_clients),
config,
max_sender_concurrency,
effective_core_ids,
}
}
}
@@ -199,12 +216,14 @@ pub struct TradingClient {
/// Whether to use seed optimization for all ATA operations (default: true)
/// Applies to all token account creations across buy and sell operations
pub use_seed_optimize: bool,
/// Whether to pin parallel submit tasks to CPU cores (from TradeConfig.use_core_affinity). Default true.
pub use_core_affinity: bool,
/// Use dedicated sender threads (from TradeConfig.use_dedicated_sender_threads). Default false.
/// Internal: use dedicated sender threads (default false). Set via with_dedicated_sender_threads() for advanced use.
pub use_dedicated_sender_threads: bool,
/// Core indices for dedicated sender threads (from TradeConfig.sender_thread_cores). Arc avoids cloning the Vec when building SwapParams.
/// Internal: core indices for dedicated sender threads. Trimmed to ≤ max_sender_concurrency at set.
pub sender_thread_cores: Option<Arc<Vec<usize>>>,
/// Internal: precomputed at infra init (min(swqos_count, 2/3*cores)). Not user-configurable.
pub max_sender_concurrency: usize,
/// Internal: precomputed at infra init for job affinity. Not user-configurable.
pub effective_core_ids: Arc<Vec<core_affinity::CoreId>>,
/// Whether to output all SDK logs (from TradeConfig.log_enabled).
pub log_enabled: bool,
/// Whether to check minimum tip per SWQOS (from TradeConfig.check_min_tip). Default false for lower latency.
@@ -223,9 +242,10 @@ impl Clone for TradingClient {
infrastructure: self.infrastructure.clone(),
middleware_manager: self.middleware_manager.clone(),
use_seed_optimize: self.use_seed_optimize,
use_core_affinity: self.use_core_affinity,
use_dedicated_sender_threads: self.use_dedicated_sender_threads,
sender_thread_cores: self.sender_thread_cores.clone(),
max_sender_concurrency: self.max_sender_concurrency,
effective_core_ids: self.effective_core_ids.clone(),
log_enabled: self.log_enabled,
check_min_tip: self.check_min_tip,
}
@@ -348,15 +368,18 @@ impl TradingClient {
) -> Self {
// Initialize wallet-specific caches (fast, synchronous)
crate::common::fast_fn::fast_init(&payer.pubkey());
let max_sender_concurrency = infrastructure.max_sender_concurrency;
let effective_core_ids = infrastructure.effective_core_ids.clone();
Self {
payer,
infrastructure,
middleware_manager: None,
use_seed_optimize,
use_core_affinity: true,
use_dedicated_sender_threads: false,
sender_thread_cores: None,
max_sender_concurrency,
effective_core_ids,
log_enabled: true,
check_min_tip: false,
}
@@ -391,14 +414,18 @@ impl TradingClient {
}
}
let max_sender_concurrency = infrastructure.max_sender_concurrency;
let effective_core_ids = infrastructure.effective_core_ids.clone();
Self {
payer,
infrastructure,
middleware_manager: None,
use_seed_optimize,
use_core_affinity: true,
use_dedicated_sender_threads: false,
sender_thread_cores: None,
max_sender_concurrency,
effective_core_ids,
log_enabled: true,
check_min_tip: false,
}
@@ -568,14 +595,16 @@ impl TradingClient {
}
}
// 并发/核心相关由 infrastructure 预计算,用户无需配置
let instance = Self {
payer,
infrastructure,
infrastructure: infrastructure.clone(),
middleware_manager: None,
use_seed_optimize: trade_config.use_seed_optimize,
use_core_affinity: trade_config.use_core_affinity,
use_dedicated_sender_threads: trade_config.use_dedicated_sender_threads,
sender_thread_cores: trade_config.sender_thread_cores.clone(),
use_dedicated_sender_threads: false,
sender_thread_cores: None,
max_sender_concurrency: infrastructure.max_sender_concurrency,
effective_core_ids: infrastructure.effective_core_ids.clone(),
log_enabled: trade_config.log_enabled,
check_min_tip: trade_config.check_min_tip,
};
@@ -601,6 +630,31 @@ impl TradingClient {
self
}
/// **Advanced.** Use dedicated OS threads for sender pool (and optionally pin to cores).
/// By default the SDK uses a shared tokio pool; this can reduce scheduling contention when sending many txs.
/// Concurrency and core count are capped internally (≤ swqos count, ≤ 2/3 of CPU cores).
/// - `None`: keep default (shared tokio pool).
/// - `Some(vec![])`: dedicated threads with default count, no core pinning.
/// - `Some(indices)`: dedicated threads pinned to those core indices (trimmed to cap).
pub fn with_dedicated_sender_threads(mut self, core_indices: Option<Vec<usize>>) -> Self {
match core_indices {
None => {
self.use_dedicated_sender_threads = false;
self.sender_thread_cores = None;
}
Some(v) if v.is_empty() => {
self.use_dedicated_sender_threads = true;
self.sender_thread_cores = None;
}
Some(v) => {
self.use_dedicated_sender_threads = true;
let cap = v.len().min(self.max_sender_concurrency);
self.sender_thread_cores = Some(Arc::new(if cap < v.len() { v[..cap].to_vec() } else { v }));
}
}
self
}
/// Gets the RPC client instance for direct Solana blockchain interactions
///
/// This provides access to the underlying Solana RPC client that can be used
@@ -721,9 +775,10 @@ impl TradingClient {
gas_fee_strategy: params.gas_fee_strategy,
simulate: params.simulate,
log_enabled: self.log_enabled,
use_core_affinity: self.use_core_affinity,
use_dedicated_sender_threads: self.use_dedicated_sender_threads,
sender_thread_cores: self.sender_thread_cores.clone(),
max_sender_concurrency: self.max_sender_concurrency,
effective_core_ids: self.effective_core_ids.clone(),
check_min_tip: self.check_min_tip,
grpc_recv_us: params.grpc_recv_us,
use_exact_sol_amount: params.use_exact_sol_amount,
@@ -827,9 +882,10 @@ impl TradingClient {
gas_fee_strategy: params.gas_fee_strategy,
simulate: params.simulate,
log_enabled: self.log_enabled,
use_core_affinity: self.use_core_affinity,
use_dedicated_sender_threads: self.use_dedicated_sender_threads,
sender_thread_cores: self.sender_thread_cores.clone(),
max_sender_concurrency: self.max_sender_concurrency,
effective_core_ids: self.effective_core_ids.clone(),
check_min_tip: self.check_min_tip,
grpc_recv_us: params.grpc_recv_us,
use_exact_sol_amount: None,