perf: reuse stock-state calendar index
This commit is contained in:
@@ -1997,7 +1997,24 @@ impl DataSet {
|
||||
symbol_id: u32,
|
||||
include_now: bool,
|
||||
) -> Option<usize> {
|
||||
let calendar_index = self.calendar.index_of(date)?;
|
||||
let calendar_index = self.calendar_index(date)?;
|
||||
self.market_series_end_index_by_symbol_id_at_calendar_index(
|
||||
calendar_index,
|
||||
symbol_id,
|
||||
include_now,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn calendar_index(&self, date: NaiveDate) -> Option<usize> {
|
||||
self.calendar.index_of(date)
|
||||
}
|
||||
|
||||
fn market_series_end_index_by_symbol_id_at_calendar_index(
|
||||
&self,
|
||||
calendar_index: usize,
|
||||
symbol_id: u32,
|
||||
include_now: bool,
|
||||
) -> Option<usize> {
|
||||
let positions = self
|
||||
.market_series_end_positions_by_symbol_id
|
||||
.as_ref()
|
||||
@@ -2020,6 +2037,14 @@ impl DataSet {
|
||||
self.market_series_end_index_by_symbol_id(date, symbol_id, true)
|
||||
}
|
||||
|
||||
pub(crate) fn market_current_series_end_index_by_symbol_id_at_calendar_index(
|
||||
&self,
|
||||
calendar_index: usize,
|
||||
symbol_id: u32,
|
||||
) -> Option<usize> {
|
||||
self.market_series_end_index_by_symbol_id_at_calendar_index(calendar_index, symbol_id, true)
|
||||
}
|
||||
|
||||
pub fn factor(&self, date: NaiveDate, symbol: &str) -> Option<&DailyFactorSnapshot> {
|
||||
let symbol_id = self.symbol_id(symbol)?;
|
||||
self.factor_by_symbol_id(date, symbol_id)
|
||||
@@ -2061,9 +2086,10 @@ impl DataSet {
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn market_standard_rolling_means_by_symbol_id(
|
||||
pub(crate) fn market_standard_rolling_means_by_symbol_id_with_calendar_index(
|
||||
&self,
|
||||
date: NaiveDate,
|
||||
calendar_index: Option<usize>,
|
||||
symbol_id: u32,
|
||||
close_lookbacks: &[usize; 7],
|
||||
volume_lookbacks: &[usize; 5],
|
||||
@@ -2079,7 +2105,13 @@ impl DataSet {
|
||||
}
|
||||
// Both series are built from the same market-date sequence. Reuse the
|
||||
// indexed boundary lookup instead of repeating it for close and volume.
|
||||
let series_end = self.market_series_end_index_by_symbol_id(date, symbol_id, include_now);
|
||||
let series_end = calendar_index.and_then(|calendar_index| {
|
||||
self.market_series_end_index_by_symbol_id_at_calendar_index(
|
||||
calendar_index,
|
||||
symbol_id,
|
||||
include_now,
|
||||
)
|
||||
});
|
||||
let close = if close_required {
|
||||
self.adjusted_close_series_by_symbol_id(symbol_id)
|
||||
.map(|series| {
|
||||
@@ -5227,6 +5259,85 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "manual release-mode calendar index reuse benchmark"]
|
||||
fn benchmark_series_boundary_reuses_calendar_index() {
|
||||
use std::hint::black_box;
|
||||
use std::time::Instant;
|
||||
|
||||
let data = volume_contract_data(Some([1.0, 1.0, 1.0]));
|
||||
let symbol_id = data.symbol_id("000001.SZ").expect("symbol id");
|
||||
let date = *data.calendar().days().last().expect("calendar date");
|
||||
let calendar_index = data.calendar_index(date).expect("calendar index");
|
||||
let iterations = 5_000_000usize;
|
||||
let mut date_lookup_nanos = 0u128;
|
||||
let mut reused_nanos = 0u128;
|
||||
let mut date_lookup_checksum = 0usize;
|
||||
let mut reused_checksum = 0usize;
|
||||
|
||||
for sample in 0..6 {
|
||||
let measure_date_lookup = || {
|
||||
let started = Instant::now();
|
||||
let mut checksum = 0usize;
|
||||
for _ in 0..iterations {
|
||||
checksum += black_box(
|
||||
data.market_series_end_index_by_symbol_id(
|
||||
black_box(date),
|
||||
black_box(symbol_id),
|
||||
true,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
(started.elapsed().as_nanos(), checksum)
|
||||
};
|
||||
let measure_reused = || {
|
||||
let started = Instant::now();
|
||||
let mut checksum = 0usize;
|
||||
for _ in 0..iterations {
|
||||
checksum += black_box(
|
||||
data.market_series_end_index_by_symbol_id_at_calendar_index(
|
||||
black_box(calendar_index),
|
||||
black_box(symbol_id),
|
||||
true,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
(started.elapsed().as_nanos(), checksum)
|
||||
};
|
||||
let (first_nanos, first_checksum, second_nanos, second_checksum) = if sample % 2 == 0 {
|
||||
let (date_nanos, date_checksum) = measure_date_lookup();
|
||||
let (reused_nanos, reused_checksum) = measure_reused();
|
||||
(date_nanos, date_checksum, reused_nanos, reused_checksum)
|
||||
} else {
|
||||
let (reused_nanos, reused_checksum) = measure_reused();
|
||||
let (date_nanos, date_checksum) = measure_date_lookup();
|
||||
(date_nanos, date_checksum, reused_nanos, reused_checksum)
|
||||
};
|
||||
date_lookup_nanos += first_nanos;
|
||||
date_lookup_checksum += first_checksum;
|
||||
reused_nanos += second_nanos;
|
||||
reused_checksum += second_checksum;
|
||||
}
|
||||
|
||||
assert_eq!(date_lookup_checksum, reused_checksum);
|
||||
let date_lookup_seconds = date_lookup_nanos as f64 / 1_000_000_000.0;
|
||||
let reused_seconds = reused_nanos as f64 / 1_000_000_000.0;
|
||||
eprintln!(
|
||||
"{}",
|
||||
serde_json::json!({
|
||||
"schemaVersion": "fidc-series-boundary-calendar-index-benchmark/v1",
|
||||
"samples": 6,
|
||||
"iterationsPerSample": iterations,
|
||||
"dateLookupSeconds": date_lookup_seconds,
|
||||
"reusedCalendarIndexSeconds": reused_seconds,
|
||||
"speedup": date_lookup_seconds / reused_seconds,
|
||||
"checksum": date_lookup_checksum,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "manual component benchmark"]
|
||||
fn benchmark_daily_snapshot_view_lookup() {
|
||||
@@ -5743,8 +5854,10 @@ mod tests {
|
||||
let symbol_id = data.symbol_id("000001.SZ").unwrap();
|
||||
let close_lookbacks = [1, 2, 3, 1, 2, 3, 0];
|
||||
let volume_lookbacks = [1, 2, 3, 0, 2];
|
||||
let batched = data.market_standard_rolling_means_by_symbol_id(
|
||||
let calendar_index = data.calendar_index(date);
|
||||
let batched = data.market_standard_rolling_means_by_symbol_id_with_calendar_index(
|
||||
date,
|
||||
calendar_index,
|
||||
symbol_id,
|
||||
&close_lookbacks,
|
||||
&volume_lookbacks,
|
||||
@@ -5774,8 +5887,9 @@ mod tests {
|
||||
)
|
||||
);
|
||||
}
|
||||
let current = data.market_standard_rolling_means_by_symbol_id(
|
||||
let current = data.market_standard_rolling_means_by_symbol_id_with_calendar_index(
|
||||
date,
|
||||
calendar_index,
|
||||
symbol_id,
|
||||
&close_lookbacks,
|
||||
&volume_lookbacks,
|
||||
@@ -5830,6 +5944,22 @@ mod tests {
|
||||
data.market_series_end_index_by_symbol_id(dates[2], symbol_id, true),
|
||||
Some(3)
|
||||
);
|
||||
let calendar_index = data.calendar_index(dates[2]).expect("calendar index");
|
||||
assert_eq!(
|
||||
data.market_series_end_index_by_symbol_id_at_calendar_index(
|
||||
calendar_index,
|
||||
symbol_id,
|
||||
false,
|
||||
),
|
||||
Some(2)
|
||||
);
|
||||
assert_eq!(
|
||||
data.market_current_series_end_index_by_symbol_id_at_calendar_index(
|
||||
calendar_index,
|
||||
symbol_id,
|
||||
),
|
||||
Some(3)
|
||||
);
|
||||
|
||||
let extended = data
|
||||
.clone()
|
||||
|
||||
Reference in New Issue
Block a user