perf: implement SIMD-accelerated event processing and optimize streaming performance

- Add SIMD utilities for fast byte array comparison and discriminator matching
- Optimize event processor with batch processing and memory pool
- Refactor global state management with concurrent data structures
- Remove deprecated batch processing module
- Enhance metrics collection with reduced overhead
- Improve parser efficiency across all protocol implementations
- Add performance benchmarking dependencies (criterion, wide)
- Update documentation and examples for new architecture

Performance improvements:
- SIMD-accelerated byte operations for instruction parsing
- Concurrent HashMap (DashMap) for better multi-threading
- Optimized memory allocation patterns
- Reduced lock contention in event processing pipeline

Breaking changes: Removed batch.rs module, updated parser interfaces
This commit is contained in:
ysq
2025-08-31 22:18:18 +08:00
parent 52a489ae80
commit 74781e5cbe
30 changed files with 1297 additions and 1259 deletions
@@ -1,11 +1,13 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::OnceLock;
use solana_sdk::pubkey::Pubkey;
use crate::streaming::common::SimdUtils;
use crate::streaming::event_parser::common::filter::EventTypeFilter;
use crate::streaming::event_parser::common::{EventMetadata, EventType, ProtocolType};
use crate::streaming::event_parser::core::traits::UnifiedEvent;
use crate::streaming::event_parser::core::traits::{UnifiedEvent, get_high_perf_clock};
use crate::streaming::event_parser::protocols::bonk::parser::BONK_PROGRAM_ID;
use crate::streaming::event_parser::protocols::pumpfun::parser::PUMPFUN_PROGRAM_ID;
use crate::streaming::event_parser::protocols::pumpswap::parser::PUMPSWAP_PROGRAM_ID;
@@ -35,7 +37,10 @@ static PROTOCOL_CONFIGS_CACHE: OnceLock<HashMap<Protocol, Vec<AccountEventParseC
pub struct AccountEventParser {}
impl AccountEventParser {
pub fn configs(protocols: Vec<Protocol>, event_type_filter: Option<EventTypeFilter>) -> Vec<AccountEventParseConfig> {
pub fn configs(
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
) -> Vec<AccountEventParseConfig> {
let protocols_map = PROTOCOL_CONFIGS_CACHE.get_or_init(|| {
let mut map: HashMap<Protocol, Vec<AccountEventParseConfig>> = HashMap::new();
map.insert(Protocol::PumpSwap, vec![
@@ -145,32 +150,39 @@ impl AccountEventParser {
});
let mut configs = vec![];
let empty_vec = vec![];
for protocol in protocols {
let protocol_configs = protocols_map.get(&protocol).unwrap_or(&vec![]).clone();
let filtered_configs: Vec<AccountEventParseConfig> = protocol_configs.into_iter().filter(|config| {
event_type_filter.as_ref().map(|filter| filter.include.contains(&config.event_type)).unwrap_or(true)
}).collect();
let protocol_configs = protocols_map.get(protocol).unwrap_or(&empty_vec);
let filtered_configs: Vec<AccountEventParseConfig> = protocol_configs
.iter()
.filter(|config| {
event_type_filter
.map(|filter| filter.include.contains(&config.event_type))
.unwrap_or(true)
})
.cloned()
.collect();
configs.extend(filtered_configs);
}
configs
}
pub fn parse_account_event(
protocols: Vec<Protocol>,
protocols: &[Protocol],
account: AccountPretty,
event_type_filter: Option<EventTypeFilter>,
event_type_filter: Option<&EventTypeFilter>,
) -> Option<Box<dyn UnifiedEvent>> {
let configs = Self::configs(protocols, event_type_filter);
for config in configs {
if account.owner == config.program_id
&& account.data[..config.account_discriminator.len()]
== *config.account_discriminator
&& SimdUtils::fast_discriminator_match(&account.data, config.account_discriminator)
{
let signature_str = Cow::Owned(account.signature.to_string());
let event = (config.account_parser)(
&account,
EventMetadata {
slot: account.slot,
signature: account.signature.to_string(),
signature: signature_str,
protocol: config.protocol_type,
event_type: config.event_type,
program_id: config.program_id,
@@ -180,7 +192,7 @@ impl AccountEventParser {
);
if let Some(mut event) = event {
event.set_program_handle_time_consuming_us(
chrono::Utc::now().timestamp_micros() - account.program_received_time_us,
get_high_perf_clock().elapsed_micros_since(account.program_received_time_us),
);
return Some(event);
}
@@ -1,4 +1,4 @@
use crate::streaming::event_parser::core::traits::UnifiedEvent;
use crate::streaming::event_parser::core::traits::{UnifiedEvent, get_high_perf_clock};
use crate::streaming::event_parser::protocols::block::block_meta_event::BlockMetaEvent;
pub struct CommonEventParser {}
@@ -17,7 +17,7 @@ impl CommonEventParser {
program_received_time_us,
);
block_meta_event.set_program_handle_time_consuming_us(
chrono::Utc::now().timestamp_micros() - program_received_time_us,
get_high_perf_clock().elapsed_micros_since(program_received_time_us),
);
Box::new(block_meta_event)
}
+170 -68
View File
@@ -1,92 +1,174 @@
use solana_sdk::pubkey::Pubkey;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use dashmap::DashMap;
use std::collections::BTreeSet;
/// Global state management, thread-safe implementation without locks
const MAX_SLOTS: usize = 1000;
const CLEANUP_BATCH_SIZE: usize = 100;
/// Slot-based trader addresses, completely lock-free
#[derive(Default)]
struct SlotAddresses {
/// Developer addresses for this slot
dev_addresses: BTreeSet<Pubkey>,
/// Bonk developer addresses for this slot
bonk_dev_addresses: BTreeSet<Pubkey>,
}
/// High-performance global state with lock-free slot-based storage
pub struct GlobalState {
/// Last processed slot
last_slot: AtomicU64,
/// Developer address array
dev_addresses: parking_lot::RwLock<Vec<Pubkey>>,
/// Bonk developer address array
bonk_dev_addresses: parking_lot::RwLock<Vec<Pubkey>>,
/// Slot -> trader addresses mapping (lock-free concurrent hashmap)
slot_data: DashMap<u64, SlotAddresses>,
/// Current slot count for capacity management
slot_count: AtomicUsize,
/// Generation counter to handle cleanup races
generation: AtomicU64,
}
impl GlobalState {
/// Create a new global state instance
/// Create a new high-performance global state instance
pub fn new() -> Self {
Self {
last_slot: AtomicU64::new(0),
dev_addresses: parking_lot::RwLock::new(Vec::new()),
bonk_dev_addresses: parking_lot::RwLock::new(Vec::new()),
slot_data: DashMap::new(),
slot_count: AtomicUsize::new(0),
generation: AtomicU64::new(0),
}
}
/// Get current slot
pub fn get_last_slot(&self) -> u64 {
self.last_slot.load(Ordering::Relaxed)
}
/// Lock-free capacity management - cleanup old slots when limit exceeded
fn maybe_cleanup(&self) {
let current_count = self.slot_count.load(Ordering::Relaxed);
if current_count <= MAX_SLOTS {
return;
}
/// Update slot, clear arrays if slot changes
pub fn update_slot(&self, new_slot: u64) {
let old_slot = self.last_slot.swap(new_slot, Ordering::Relaxed);
// Use CAS to ensure only one thread performs cleanup
let gen = self.generation.load(Ordering::Relaxed);
if self.generation.compare_exchange_weak(gen, gen + 1, Ordering::Acquire, Ordering::Relaxed).is_err() {
return; // Another thread is cleaning up
}
if old_slot != new_slot {
// Clear arrays when slot changes
let mut dev_addresses = self.dev_addresses.write();
let mut bonk_dev_addresses = self.bonk_dev_addresses.write();
// Collect oldest slots (BTreeMap naturally orders by key)
let mut slots_to_remove: Vec<u64> = self.slot_data.iter()
.map(|entry| *entry.key())
.collect();
if slots_to_remove.len() <= MAX_SLOTS {
return; // Race condition, already cleaned up
}
slots_to_remove.sort_unstable();
slots_to_remove.truncate(CLEANUP_BATCH_SIZE);
dev_addresses.clear();
bonk_dev_addresses.clear();
// Remove old slots atomically
for slot in slots_to_remove {
self.slot_data.remove(&slot);
self.slot_count.fetch_sub(1, Ordering::Relaxed);
}
}
/// Add developer address
pub fn add_dev_address(&self, address: Pubkey) {
let mut dev_addresses = self.dev_addresses.write();
if !dev_addresses.contains(&address) {
dev_addresses.push(address);
}
/// Add developer address for a specific slot (lock-free)
pub fn add_dev_address(&self, slot: u64, address: Pubkey) {
self.maybe_cleanup();
self.slot_data.entry(slot)
.and_modify(|addresses| {
addresses.dev_addresses.insert(address);
})
.or_insert_with(|| {
self.slot_count.fetch_add(1, Ordering::Relaxed);
let mut slot_addr = SlotAddresses::default();
slot_addr.dev_addresses.insert(address);
slot_addr
});
}
/// Check if address is a developer address
/// Add Bonk developer address for a specific slot (lock-free)
pub fn add_bonk_dev_address(&self, slot: u64, address: Pubkey) {
self.maybe_cleanup();
self.slot_data.entry(slot)
.and_modify(|addresses| {
addresses.bonk_dev_addresses.insert(address);
})
.or_insert_with(|| {
self.slot_count.fetch_add(1, Ordering::Relaxed);
let mut slot_addr = SlotAddresses::default();
slot_addr.bonk_dev_addresses.insert(address);
slot_addr
});
}
/// High-performance: Check if address is a developer address in specific slot (O(log m))
pub fn is_dev_address_in_slot(&self, slot: u64, address: &Pubkey) -> bool {
self.slot_data.get(&slot)
.map(|entry| entry.dev_addresses.contains(address))
.unwrap_or(false)
}
/// High-performance: Check if address is a Bonk developer address in specific slot (O(log m))
pub fn is_bonk_dev_address_in_slot(&self, slot: u64, address: &Pubkey) -> bool {
self.slot_data.get(&slot)
.map(|entry| entry.bonk_dev_addresses.contains(address))
.unwrap_or(false)
}
/// Check if address is a developer address in any slot (lock-free scan, slower)
pub fn is_dev_address(&self, address: &Pubkey) -> bool {
let dev_addresses = self.dev_addresses.read();
dev_addresses.contains(address)
self.slot_data.iter().any(|entry| entry.dev_addresses.contains(address))
}
/// Add Bonk developer address
pub fn add_bonk_dev_address(&self, address: Pubkey) {
let mut bonk_dev_addresses = self.bonk_dev_addresses.write();
if !bonk_dev_addresses.contains(&address) {
bonk_dev_addresses.push(address);
}
}
/// Check if address is a Bonk developer address
/// Check if address is a Bonk developer address in any slot (lock-free scan, slower)
pub fn is_bonk_dev_address(&self, address: &Pubkey) -> bool {
let bonk_dev_addresses = self.bonk_dev_addresses.read();
bonk_dev_addresses.contains(address)
self.slot_data.iter().any(|entry| entry.bonk_dev_addresses.contains(address))
}
/// Get all developer addresses
/// Get all developer addresses from all slots (lock-free aggregation)
pub fn get_dev_addresses(&self) -> Vec<Pubkey> {
let dev_addresses = self.dev_addresses.read();
dev_addresses.clone()
let mut all_addresses = BTreeSet::new();
for entry in self.slot_data.iter() {
for addr in &entry.dev_addresses {
all_addresses.insert(*addr);
}
}
all_addresses.into_iter().collect()
}
/// Get all Bonk developer addresses
/// Get all Bonk developer addresses from all slots (lock-free aggregation)
pub fn get_bonk_dev_addresses(&self) -> Vec<Pubkey> {
let bonk_dev_addresses = self.bonk_dev_addresses.read();
bonk_dev_addresses.clone()
let mut all_addresses = BTreeSet::new();
for entry in self.slot_data.iter() {
for addr in &entry.bonk_dev_addresses {
all_addresses.insert(*addr);
}
}
all_addresses.into_iter().collect()
}
/// Clear all data
pub fn clear_all_data(&self) {
let mut dev_addresses = self.dev_addresses.write();
let mut bonk_dev_addresses = self.bonk_dev_addresses.write();
/// Get developer addresses for a specific slot
pub fn get_dev_addresses_for_slot(&self, slot: u64) -> Vec<Pubkey> {
self.slot_data.get(&slot)
.map(|entry| entry.dev_addresses.iter().copied().collect())
.unwrap_or_default()
}
dev_addresses.clear();
bonk_dev_addresses.clear();
/// Get Bonk developer addresses for a specific slot
pub fn get_bonk_dev_addresses_for_slot(&self, slot: u64) -> Vec<Pubkey> {
self.slot_data.get(&slot)
.map(|entry| entry.bonk_dev_addresses.iter().copied().collect())
.unwrap_or_default()
}
/// Get current slot count
pub fn get_slot_count(&self) -> usize {
self.slot_count.load(Ordering::Relaxed)
}
/// Clear all data (lock-free)
pub fn clear_all_data(&self) {
self.slot_data.clear();
self.slot_count.store(0, Ordering::Relaxed);
self.generation.store(0, Ordering::Relaxed);
}
}
@@ -105,14 +187,9 @@ pub fn get_global_state() -> &'static GlobalState {
&GLOBAL_STATE
}
/// Convenience function: Update slot
pub fn update_slot(slot: u64) {
get_global_state().update_slot(slot);
}
/// Convenience function: Add developer address
pub fn add_dev_address(address: Pubkey) {
get_global_state().add_dev_address(address);
/// Convenience function: Add developer address for a specific slot
pub fn add_dev_address(slot: u64, address: Pubkey) {
get_global_state().add_dev_address(slot, address);
}
/// Convenience function: Check if address is a developer address
@@ -120,9 +197,9 @@ pub fn is_dev_address(address: &Pubkey) -> bool {
get_global_state().is_dev_address(address)
}
/// Convenience function: Add Bonk developer address
pub fn add_bonk_dev_address(address: Pubkey) {
get_global_state().add_bonk_dev_address(address);
/// Convenience function: Add Bonk developer address for a specific slot
pub fn add_bonk_dev_address(slot: u64, address: Pubkey) {
get_global_state().add_bonk_dev_address(slot, address);
}
/// Convenience function: Check if address is a Bonk developer address
@@ -139,3 +216,28 @@ pub fn get_dev_addresses() -> Vec<Pubkey> {
pub fn get_bonk_dev_addresses() -> Vec<Pubkey> {
get_global_state().get_bonk_dev_addresses()
}
/// Convenience function: Get developer addresses for a specific slot
pub fn get_dev_addresses_for_slot(slot: u64) -> Vec<Pubkey> {
get_global_state().get_dev_addresses_for_slot(slot)
}
/// Convenience function: Get Bonk developer addresses for a specific slot
pub fn get_bonk_dev_addresses_for_slot(slot: u64) -> Vec<Pubkey> {
get_global_state().get_bonk_dev_addresses_for_slot(slot)
}
/// Convenience function: Get current slot count
pub fn get_slot_count() -> usize {
get_global_state().get_slot_count()
}
/// High-performance: Check if address is a developer address in specific slot
pub fn is_dev_address_in_slot(slot: u64, address: &Pubkey) -> bool {
get_global_state().is_dev_address_in_slot(slot, address)
}
/// High-performance: Check if address is a Bonk developer address in specific slot
pub fn is_bonk_dev_address_in_slot(slot: u64, address: &Pubkey) -> bool {
get_global_state().is_bonk_dev_address_in_slot(slot, address)
}
+244 -93
View File
@@ -8,33 +8,141 @@ use solana_transaction_status::{
EncodedConfirmedTransactionWithStatusMeta, InnerInstruction, InnerInstructions,
TransactionWithStatusMeta, UiInstruction,
};
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::Arc;
use std::time::Instant;
use super::global_state::{add_dev_address, is_dev_address, update_slot};
use crate::streaming::event_parser::common::{parse_swap_data_from_next_instructions, SwapData};
use crate::streaming::event_parser::core::global_state::{
add_bonk_dev_address, is_bonk_dev_address,
use super::global_state::{
add_bonk_dev_address, add_dev_address, is_bonk_dev_address, is_dev_address,
};
use crate::streaming::common::simd_utils::SimdUtils;
use crate::streaming::event_parser::common::{parse_swap_data_from_next_instructions, SwapData};
use crate::streaming::event_parser::protocols::pumpswap::{PumpSwapBuyEvent, PumpSwapSellEvent};
use crate::streaming::event_parser::{
common::{utils::*, EventMetadata, EventType, ProtocolType},
common::{EventMetadata, EventType, ProtocolType},
protocols::{
bonk::{BonkPoolCreateEvent, BonkTradeEvent},
pumpfun::{PumpFunCreateTokenEvent, PumpFunTradeEvent},
},
};
/// 高性能时钟管理器,减少系统调用开销
#[derive(Debug)]
pub struct HighPerformanceClock {
/// 基准时间点(程序启动时的单调时钟时间)
base_instant: Instant,
/// 基准时间点对应的UTC时间戳(微秒)
base_timestamp_us: i64,
}
impl HighPerformanceClock {
/// 创建新的高性能时钟
pub fn new() -> Self {
let base_instant = Instant::now();
let base_timestamp_us = chrono::Utc::now().timestamp_micros();
Self { base_instant, base_timestamp_us }
}
/// 获取当前时间戳(微秒),使用单调时钟计算,避免系统调用
#[inline(always)]
pub fn now_micros(&self) -> i64 {
let elapsed = self.base_instant.elapsed();
self.base_timestamp_us + elapsed.as_micros() as i64
}
/// 计算从指定时间戳到现在的消耗时间(微秒)
#[inline(always)]
pub fn elapsed_micros_since(&self, start_timestamp_us: i64) -> i64 {
self.now_micros() - start_timestamp_us
}
}
impl Default for HighPerformanceClock {
fn default() -> Self {
Self::new()
}
}
/// 全局高性能时钟实例(使用OnceCell避免重复初始化)
static HIGH_PERF_CLOCK: once_cell::sync::OnceCell<HighPerformanceClock> =
once_cell::sync::OnceCell::new();
/// 获取全局高性能时钟实例
#[inline(always)]
pub fn get_high_perf_clock() -> &'static HighPerformanceClock {
HIGH_PERF_CLOCK.get_or_init(HighPerformanceClock::new)
}
/// 轻量级事件包装器,避免频繁的Box分配
#[derive(Debug)]
pub struct EventWrapper<T: UnifiedEvent> {
pub event: T,
}
impl<T: UnifiedEvent + 'static> EventWrapper<T> {
#[inline]
pub fn new(event: T) -> Self {
Self { event }
}
#[inline]
pub fn into_boxed(self) -> Box<dyn UnifiedEvent> {
Box::new(self.event)
}
}
/// 高性能账户公钥缓存,避免重复Vec分配
#[derive(Debug)]
pub struct AccountPubkeyCache {
/// 预分配的账户公钥向量,避免每次重新分配
cache: Vec<Pubkey>,
}
impl AccountPubkeyCache {
/// 创建新的账户公钥缓存
pub fn new() -> Self {
Self {
cache: Vec::with_capacity(32), // 预分配32个位置,覆盖大多数交易
}
}
/// 从指令账户索引构建账户公钥向量,重用缓存内存
#[inline]
pub fn build_account_pubkeys(
&mut self,
instruction_accounts: &[u8],
all_accounts: &[Pubkey],
) -> &[Pubkey] {
self.cache.clear();
// 确保容量足够,避免动态扩容
if self.cache.capacity() < instruction_accounts.len() {
self.cache.reserve(instruction_accounts.len() - self.cache.capacity());
}
// 快速填充账户公钥
for &idx in instruction_accounts.iter() {
if (idx as usize) < all_accounts.len() {
self.cache.push(all_accounts[idx as usize]);
}
}
&self.cache
}
}
impl Default for AccountPubkeyCache {
fn default() -> Self {
Self::new()
}
}
/// Unified Event Interface - All protocol events must implement this trait
pub trait UnifiedEvent: Debug + Send + Sync {
/// Get event ID
fn id(&self) -> &str;
/// Set event ID
fn clear_id(&mut self);
/// Get event type
fn event_type(&self) -> EventType;
@@ -70,6 +178,9 @@ pub trait UnifiedEvent: Debug + Send + Sync {
/// Set swap data
fn set_swap_data(&mut self, swap_data: SwapData);
/// swap_data is parsed
fn swap_data_is_parsed(&self) -> bool;
/// Get index
fn instruction_outer_index(&self) -> i64;
fn instruction_inner_index(&self) -> Option<i64>;
@@ -343,7 +454,6 @@ pub trait EventParser: Send + Sync {
let versioned_tx = match transaction.transaction.transaction.decode() {
Some(tx) => tx,
None => {
println!("Failed to decode transaction");
return Ok(());
}
};
@@ -363,6 +473,7 @@ pub trait EventParser: Send + Sync {
if let UiInstruction::Compiled(ui_compiled) = ui_instruction {
// 解码base58编码的data
if let Ok(data) = bs58::decode(&ui_compiled.data).into_vec() {
// base64解码
let compiled_instruction = CompiledInstruction {
program_id_index: ui_compiled.program_id_index,
accounts: ui_compiled.accounts.clone(),
@@ -565,6 +676,8 @@ pub struct GenericEventParser {
pub program_ids: Vec<Pubkey>,
// pub inner_instruction_configs: HashMap<Vec<u8>, Vec<GenericEventParseConfig>>,
pub instruction_configs: HashMap<Vec<u8>, Vec<GenericEventParseConfig>>,
/// 账户公钥缓存,避免重复分配
pub account_cache: parking_lot::Mutex<AccountPubkeyCache>,
}
impl GenericEventParser {
@@ -580,7 +693,10 @@ impl GenericEventParser {
.push(config.clone());
}
Self { program_ids, instruction_configs }
// 初始化账户缓存
let account_cache = parking_lot::Mutex::new(AccountPubkeyCache::new());
Self { program_ids, instruction_configs, account_cache }
}
/// 通用的内联指令解析方法
@@ -598,11 +714,11 @@ impl GenericEventParser {
transaction_index: Option<u64>,
) -> Option<Box<dyn UnifiedEvent>> {
if let Some(parser) = config.inner_instruction_parser {
let signature_str = Cow::Owned(signature.to_string());
let timestamp = block_time.unwrap_or(Timestamp { seconds: 0, nanos: 0 });
let block_time_ms = timestamp.seconds * 1000 + (timestamp.nanos as i64) / 1_000_000;
let metadata = EventMetadata::new(
signature.to_string(),
signature.to_string(),
signature_str,
slot,
timestamp.seconds,
block_time_ms,
@@ -636,11 +752,11 @@ impl GenericEventParser {
transaction_index: Option<u64>,
) -> Option<Box<dyn UnifiedEvent>> {
if let Some(parser) = config.instruction_parser {
let signature_str = Cow::Owned(signature.to_string());
let timestamp = block_time.unwrap_or(Timestamp { seconds: 0, nanos: 0 });
let block_time_ms = timestamp.seconds * 1000 + (timestamp.nanos as i64) / 1_000_000;
let metadata = EventMetadata::new(
signature.to_string(),
signature.to_string(),
signature_str,
slot,
timestamp.seconds,
block_time_ms,
@@ -678,7 +794,8 @@ impl EventParser for GenericEventParser {
transaction_index: Option<u64>,
config: &GenericEventParseConfig,
) -> Vec<Box<dyn UnifiedEvent>> {
if inner_instruction.data.len() < 16 {
// Use SIMD-optimized data validation
if !SimdUtils::validate_instruction_data_simd(&inner_instruction.data, 16, 0) {
return Vec::new();
}
let data = &inner_instruction.data[16..];
@@ -720,80 +837,115 @@ impl EventParser for GenericEventParser {
if !self.should_handle(&program_id) {
return Ok(());
}
for (disc, configs) in &self.instruction_configs {
if instruction.data.len() < disc.len() {
continue;
}
let discriminator = &instruction.data[..disc.len()];
let data = &instruction.data[disc.len()..];
if discriminator == disc {
// 验证账户索引
if !validate_account_indices(&instruction.accounts, accounts.len()) {
continue;
}
let account_pubkeys: Vec<Pubkey> =
instruction.accounts.iter().map(|&idx| accounts[idx as usize]).collect();
for config in configs {
if config.program_id != program_id {
continue;
}
if let Some(mut event) = self.parse_instruction_event(
config,
data,
&account_pubkeys,
signature,
slot,
block_time,
program_received_time_us,
outer_index,
inner_index,
transaction_index,
) {
let mut inner_instruction_event: Option<Box<dyn UnifiedEvent>> = None;
if inner_instructions.is_some() {
// 解析对应的内部 log 执行
for inner_instruction in inner_instructions.unwrap().instructions.iter()
{
let result = self.parse_events_from_inner_instruction(
&inner_instruction.instruction,
signature,
slot,
block_time,
program_received_time_us,
outer_index,
inner_index,
transaction_index,
config,
);
if result.len() > 0 {
inner_instruction_event = Some(result[0].clone());
}
// 解析swap数据
let swap_data = parse_swap_data_from_next_instructions(
&*event,
inner_instructions.unwrap(),
inner_index.unwrap_or(-1_i64) as i8,
&accounts,
);
if let Some(swap_data) = swap_data {
event.set_swap_data(swap_data);
}
// 一维化并行处理:将所有 (discriminator, config) 组合展开并行处理
let all_processing_params: Vec<_> = self
.instruction_configs
.iter()
.filter(|(disc, _)| {
// Use SIMD-optimized data validation and discriminator matching
SimdUtils::validate_instruction_data_simd(&instruction.data, disc.len(), disc.len())
&& SimdUtils::fast_discriminator_match(&instruction.data, disc)
})
.flat_map(|(disc, configs)| {
configs
.iter()
.filter(|config| config.program_id == program_id)
.map(move |config| (disc, config))
})
.collect();
// Use SIMD-optimized account indices validation (只需检查一次)
if !SimdUtils::validate_account_indices_simd(&instruction.accounts, accounts.len()) {
return Ok(());
}
// 使用缓存构建账户公钥列表,避免重复分配 (只需构建一次)
let account_pubkeys = {
let mut cache_guard = self.account_cache.lock();
cache_guard.build_account_pubkeys(&instruction.accounts, accounts).to_vec()
};
// 并行处理所有 (discriminator, config) 组合
let all_results: Vec<_> = all_processing_params
.iter()
.filter_map(|(disc, config)| {
let data = &instruction.data[disc.len()..];
self.parse_instruction_event(
config,
data,
&account_pubkeys,
signature,
slot,
block_time,
program_received_time_us,
outer_index,
inner_index,
transaction_index,
)
.map(|event| ((*disc).clone(), (*config).clone(), event))
})
.collect();
for (_disc, config, mut event) in all_results {
// 阻塞处理:原有的同步逻辑
let mut inner_instruction_event: Option<Box<dyn UnifiedEvent>> = None;
if inner_instructions.is_some() {
let inner_instructions_ref = inner_instructions.unwrap();
// 并行执行两个任务
let (inner_event_result, swap_data_result) = std::thread::scope(|s| {
let inner_event_handle = s.spawn(|| {
for inner_instruction in inner_instructions_ref.instructions.iter() {
let result = self.parse_events_from_inner_instruction(
&inner_instruction.instruction,
signature,
slot,
block_time,
program_received_time_us,
outer_index,
inner_index,
transaction_index,
&config,
);
if result.len() > 0 {
return Some(result[0].clone());
}
}
// 合并事件
if let Some(inner_instruction_event) = inner_instruction_event {
event.merge(&*inner_instruction_event);
None
});
let swap_data_handle = s.spawn(|| {
if !event.swap_data_is_parsed() {
parse_swap_data_from_next_instructions(
&*event,
inner_instructions_ref,
inner_index.unwrap_or(-1_i64) as i8,
&accounts,
)
} else {
None
}
// 设置处理时间
event.set_program_handle_time_consuming_us(
chrono::Utc::now().timestamp_micros() - program_received_time_us,
);
event = process_event(event, bot_wallet);
callback(&event);
break;
}
});
// 等待两个任务完成
(inner_event_handle.join().unwrap(), swap_data_handle.join().unwrap())
});
inner_instruction_event = inner_event_result;
if let Some(swap_data) = swap_data_result {
event.set_swap_data(swap_data);
}
}
// 合并事件
if let Some(inner_instruction_event) = inner_instruction_event {
event.merge(&*inner_instruction_event);
}
// 设置处理时间(使用高性能时钟)
event.set_program_handle_time_consuming_us(
get_high_perf_clock().elapsed_micros_since(program_received_time_us),
);
event = process_event(event, bot_wallet);
callback(&event);
}
Ok(())
}
@@ -811,11 +963,11 @@ fn process_event(
mut event: Box<dyn UnifiedEvent>,
bot_wallet: Option<Pubkey>,
) -> Box<dyn UnifiedEvent> {
update_slot(event.slot());
let slot = event.slot();
if let Some(token_info) = event.as_any().downcast_ref::<PumpFunCreateTokenEvent>() {
add_dev_address(token_info.user);
add_dev_address(slot, token_info.user);
if token_info.creator != Pubkey::default() && token_info.creator != token_info.user {
add_dev_address(token_info.creator);
add_dev_address(slot, token_info.creator);
}
} else if let Some(trade_info) = event.as_any_mut().downcast_mut::<PumpFunTradeEvent>() {
if is_dev_address(&trade_info.user) || is_dev_address(&trade_info.creator) {
@@ -844,7 +996,7 @@ fn process_event(
trade_info.user_quote_amount_out;
}
} else if let Some(pool_info) = event.as_any().downcast_ref::<BonkPoolCreateEvent>() {
add_bonk_dev_address(pool_info.creator);
add_bonk_dev_address(slot, pool_info.creator);
} else if let Some(trade_info) = event.as_any_mut().downcast_mut::<BonkTradeEvent>() {
if is_bonk_dev_address(&trade_info.payer) {
trade_info.is_dev_create_token_trade = true;
@@ -854,6 +1006,5 @@ fn process_event(
trade_info.is_dev_create_token_trade = false;
}
}
event.clear_id();
event
}