Improve performance of stats revlog entries with memory state (#3866)

* improve performace of stats_revlog_entries_with_memory_state

* format

* move Vec<RevlogEntry> into FsrsItemForMemoryState
This commit is contained in:
Jarrett Ye 2025-03-20 15:02:40 +08:00 committed by GitHub
parent d8c83ac075
commit 5d7f6b25c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 28 additions and 19 deletions

View file

@ -242,6 +242,7 @@ pub(crate) struct FsrsItemForMemoryState {
/// When revlogs have been truncated, this stores the initial state at first
/// review
pub starting_state: Option<MemoryState>,
pub filtered_revlogs: Vec<RevlogEntry>,
}
/// Like [fsrs_item_for_memory_state], but for updating multiple cards at once.
@ -330,6 +331,7 @@ pub(crate) fn fsrs_item_for_memory_state(
Ok(Some(FsrsItemForMemoryState {
item,
starting_state: None,
filtered_revlogs: output.filtered_revlogs,
}))
} else if let Some(first_user_grade) = output.filtered_revlogs.first() {
// the revlog has been truncated, but not fully
@ -356,6 +358,7 @@ pub(crate) fn fsrs_item_for_memory_state(
Ok(Some(FsrsItemForMemoryState {
item,
starting_state: Some(starting_state),
filtered_revlogs: output.filtered_revlogs,
}))
} else {
// only manual and rescheduled revlogs; treat like empty

View file

@ -4,6 +4,7 @@
use fsrs::FSRS;
use crate::card::CardType;
use crate::card::FsrsMemoryState;
use crate::prelude::*;
use crate::revlog::RevlogEntry;
use crate::scheduler::fsrs::memory_state::fsrs_item_for_memory_state;
@ -140,26 +141,31 @@ impl Collection {
let ignore_before = ignore_revlogs_before_ms_from_config(&config)?;
let mut result = Vec::new();
let mut accumulated_revlog = Vec::new();
for entry in revlog {
accumulated_revlog.push(entry.clone());
let item = fsrs_item_for_memory_state(
&fsrs,
accumulated_revlog.clone(),
next_day_at,
historical_retention,
ignore_before,
)?;
let mut card_clone = card.clone();
card_clone.set_memory_state(&fsrs, item, historical_retention)?;
let mut stats_entry = stats_revlog_entry(&entry);
stats_entry.memory_state = card_clone.memory_state.map(Into::into);
result.push(stats_entry);
if let Some(item) = fsrs_item_for_memory_state(
&fsrs,
revlog.clone(),
next_day_at,
historical_retention,
ignore_before,
)? {
let memory_states = fsrs.historical_memory_states(item.item, item.starting_state)?;
let mut revlog_index = 0;
for entry in revlog {
let mut stats_entry = stats_revlog_entry(&entry);
let memory_state: FsrsMemoryState =
if entry.id == item.filtered_revlogs[revlog_index].id {
revlog_index += 1;
memory_states[revlog_index - 1].into()
} else {
memory_states[revlog_index].into()
};
stats_entry.memory_state = Some(memory_state.into());
result.push(stats_entry);
}
Ok(result.into_iter().rev().collect())
} else {
Ok(revlog.iter().map(stats_revlog_entry).collect())
}
Ok(result.into_iter().rev().collect())
}
}