Remove overall item batching (still keeping FSRS batching)

This commit is contained in:
Daniel Pechersky 2025-09-16 16:17:06 +07:00
parent f2d6c30036
commit 1514b99785

View file

@ -290,51 +290,48 @@ impl Collection {
fn update_memory_state_for_cards_with_items( fn update_memory_state_for_cards_with_items(
&mut self, &mut self,
mut items: Vec<(CardId, FsrsItemForMemoryState)>, items: Vec<(CardId, FsrsItemForMemoryState)>,
fsrs: &FSRS, fsrs: &FSRS,
mut set_decay_and_desired_retention: impl FnMut(&mut Card), mut set_decay_and_desired_retention: impl FnMut(&mut Card),
mut maybe_reschedule_card: impl FnMut(&mut Card, &mut Self, &FSRS) -> Result<()>, mut maybe_reschedule_card: impl FnMut(&mut Card, &mut Self, &FSRS) -> Result<()>,
usn: Usn, usn: Usn,
mut on_updated_card: impl FnMut() -> Result<()>, mut on_updated_card: impl FnMut() -> Result<()>,
) -> Result<()> { ) -> Result<()> {
const ITEM_CHUNK_SIZE: usize = 100_000; const FSRS_BATCH_SIZE: usize = 1000;
const FSRS_CHUNK_SIZE: usize = 1000;
let mut to_update = Vec::new(); let mut to_update = Vec::new();
let mut fsrs_items = Vec::new(); let mut fsrs_items = Vec::new();
let mut starting_states = Vec::new(); let mut starting_states = Vec::new();
for items in items.chunk_into_vecs(ITEM_CHUNK_SIZE) { for (card_id, item) in items.into_iter() {
for (card_id, item) in items.into_iter() { let card = self.storage.get_card(card_id)?.or_not_found(card_id)?;
let card = self.storage.get_card(card_id)?.or_not_found(card_id)?;
to_update.push(card); to_update.push(card);
fsrs_items.push(item.item); fsrs_items.push(item.item);
starting_states.push(item.starting_state); starting_states.push(item.starting_state);
} }
// fsrs.memory_state_batch is O(nm) where n is the number of cards and m is the max review count between all items. // fsrs.memory_state_batch is O(nm) where n is the number of cards and m is the max review count between all items.
// Therefore we want to pass batches to fsrs.memory_state_batch where the review count is relatively even. // Therefore we want to pass batches to fsrs.memory_state_batch where the review count is relatively even.
let mut p = permutation::sort_unstable_by_key(&fsrs_items, |item| item.reviews.len()); let mut p = permutation::sort_unstable_by_key(&fsrs_items, |item| item.reviews.len());
p.apply_slice_in_place(&mut to_update); p.apply_slice_in_place(&mut to_update);
p.apply_slice_in_place(&mut fsrs_items); p.apply_slice_in_place(&mut fsrs_items);
p.apply_slice_in_place(&mut starting_states); p.apply_slice_in_place(&mut starting_states);
for ((to_update, fsrs_items), starting_states) in to_update for ((to_update, fsrs_items), starting_states) in to_update
.chunk_into_vecs(FSRS_CHUNK_SIZE) .chunk_into_vecs(FSRS_BATCH_SIZE)
.zip_eq(fsrs_items.chunk_into_vecs(FSRS_CHUNK_SIZE)) .zip_eq(fsrs_items.chunk_into_vecs(FSRS_BATCH_SIZE))
.zip_eq(starting_states.chunk_into_vecs(FSRS_CHUNK_SIZE)) .zip_eq(starting_states.chunk_into_vecs(FSRS_BATCH_SIZE))
{ {
let memory_states = fsrs.memory_state_batch(fsrs_items, starting_states)?; let memory_states = fsrs.memory_state_batch(fsrs_items, starting_states)?;
for (mut card, memory_state) in to_update.into_iter().zip_eq(memory_states) { for (mut card, memory_state) in to_update.into_iter().zip_eq(memory_states) {
let original = card.clone(); let original = card.clone();
set_decay_and_desired_retention(&mut card); set_decay_and_desired_retention(&mut card);
card.memory_state = Some(memory_state.into()); card.memory_state = Some(memory_state.into());
maybe_reschedule_card(&mut card, self, fsrs)?; maybe_reschedule_card(&mut card, self, fsrs)?;
self.update_card_inner(&mut card, original, usn)?; self.update_card_inner(&mut card, original, usn)?;
on_updated_card()?; on_updated_card()?;
}
} }
} }
Ok(()) Ok(())