From 29faf7932ed7838d0a2178a34b3fe6a259bd9052 Mon Sep 17 00:00:00 2001 From: boris Date: Sat, 5 Sep 2026 04:59:50 +0800 Subject: [PATCH] perf: avoid caching transient selection states --- .../fidc-core/src/platform_expr_strategy.rs | 99 ++++++++++++++----- 1 file changed, 77 insertions(+), 22 deletions(-) diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index 0fe438f..282fe76 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -4008,7 +4008,7 @@ impl PlatformExprStrategy { } #[allow(clippy::too_many_arguments)] - fn selection_stock_state_with_factor_date_from_views_by_symbol_id<'a>( + fn uncached_selection_stock_state_from_views_by_symbol_id<'a>( &self, ctx: &StrategyContext<'a>, date: NaiveDate, @@ -4017,14 +4017,15 @@ impl PlatformExprStrategy { symbol: &str, execution_day: &DailySnapshotView<'a>, factor_day: &DailySnapshotView<'a>, - ) -> Result, BacktestError> { + ) -> Result { let source = ViewStockStateSnapshotSource { execution: execution_day, factor: factor_day, same_date: factor_date == date, }; let use_intraday_quote = self.selection_quote_usage != StockFilterQuoteUsage::DailyOnly; - self.stock_state_with_factor_date_and_time_from_source_by_symbol_id( + let calendar_index = self.prepare_stock_state_cache_date(ctx, date); + self.build_stock_state_with_factor_date_and_time_from_source_by_symbol_id( ctx, date, factor_date, @@ -4032,6 +4033,7 @@ impl PlatformExprStrategy { symbol, None, use_intraday_quote, + calendar_index, &source, ) } @@ -4202,6 +4204,61 @@ impl PlatformExprStrategy { use_intraday_quote: bool, source: &S, ) -> Result, BacktestError> + where + S: StockStateSnapshotSource<'a>, + { + let calendar_index = self.prepare_stock_state_cache_date(ctx, date); + let cache_key = (factor_date, symbol_id, execution_time, use_intraday_quote); + if let Some(state) = self.stock_state_cache.borrow().get(&cache_key) { + return Ok(Arc::clone(state)); + } + + let state = Arc::new( + self.build_stock_state_with_factor_date_and_time_from_source_by_symbol_id( + ctx, + date, + factor_date, + symbol_id, + symbol, + execution_time, + use_intraday_quote, + calendar_index, + source, + )?, + ); + self.stock_state_cache + .borrow_mut() + .insert(cache_key, Arc::clone(&state)); + Ok(state) + } + + fn prepare_stock_state_cache_date( + &self, + ctx: &StrategyContext<'_>, + date: NaiveDate, + ) -> Option { + let mut cache_date = self.stock_state_cache_date.borrow_mut(); + if *cache_date != Some(date) { + self.stock_state_cache.borrow_mut().clear(); + *cache_date = Some(date); + *self.stock_state_cache_calendar_index.borrow_mut() = ctx.data.calendar_index(date); + } + *self.stock_state_cache_calendar_index.borrow() + } + + #[allow(clippy::too_many_arguments)] + fn build_stock_state_with_factor_date_and_time_from_source_by_symbol_id<'a, S>( + &self, + ctx: &StrategyContext<'a>, + date: NaiveDate, + factor_date: NaiveDate, + symbol_id: u32, + symbol: &str, + execution_time: Option, + use_intraday_quote: bool, + calendar_index: Option, + source: &S, + ) -> Result where S: StockStateSnapshotSource<'a>, { @@ -4212,19 +4269,6 @@ impl PlatformExprStrategy { symbol: symbol.to_string(), }) })?; - let calendar_index = { - let mut cache_date = self.stock_state_cache_date.borrow_mut(); - if *cache_date != Some(date) { - self.stock_state_cache.borrow_mut().clear(); - *cache_date = Some(date); - *self.stock_state_cache_calendar_index.borrow_mut() = ctx.data.calendar_index(date); - } - *self.stock_state_cache_calendar_index.borrow() - }; - let cache_key = (factor_date, symbol_id, execution_time, use_intraday_quote); - if let Some(state) = self.stock_state_cache.borrow().get(&cache_key) { - return Ok(Arc::clone(state)); - } let market = source.execution_market(symbol_id).ok_or_else(|| { BacktestError::Data(crate::data::DataSetError::MissingSnapshot { @@ -4406,7 +4450,7 @@ impl PlatformExprStrategy { BTreeMap::new() }; - let state = Arc::new(StockExpressionState { + let state = StockExpressionState { symbol: shared_symbol, symbol_id, market_cap, @@ -4483,10 +4527,7 @@ impl PlatformExprStrategy { } else { BTreeMap::new() }, - }); - self.stock_state_cache - .borrow_mut() - .insert(cache_key, Arc::clone(&state)); + }; Ok(state) } @@ -10115,7 +10156,7 @@ impl PlatformExprStrategy { .factor(symbol_id) .expect("market-cap order references missing factor row"); let symbol = factor.symbol.as_str(); - let stock = self.selection_stock_state_with_factor_date_from_views_by_symbol_id( + let stock = self.uncached_selection_stock_state_from_views_by_symbol_id( ctx, date, stock_factor_date, @@ -13101,6 +13142,20 @@ mod tests { }; let strategy = PlatformExprStrategy::new(PlatformExprStrategyConfig::microcap_rotation()); let first_context = context(dates[0], 0); + let first_day = data.daily_snapshot_view(dates[0]); + let uncached = strategy + .uncached_selection_stock_state_from_views_by_symbol_id( + &first_context, + dates[0], + dates[0], + data.symbol_id(symbol).unwrap(), + symbol, + &first_day, + &first_day, + ) + .expect("uncached first state"); + assert_eq!(uncached.close, 10.0); + assert!(strategy.stock_state_cache.borrow().is_empty()); let first = strategy .stock_state(&first_context, dates[0], symbol) .expect("first state");