Feat/Use cached workload values

This commit is contained in:
Luc Mcgrady 2025-07-15 14:12:37 +01:00
parent 5a19027185
commit 82a9fa3331
No known key found for this signature in database
GPG key ID: 4F3D7A0B17CC3D9C
3 changed files with 44 additions and 46 deletions

View file

@ -40,12 +40,10 @@ message DeckConfigId {
message GetRetentionWorkloadRequest { message GetRetentionWorkloadRequest {
repeated float w = 1; repeated float w = 1;
string search = 2; string search = 2;
float before = 3;
float after = 4;
} }
message GetRetentionWorkloadResponse { message GetRetentionWorkloadResponse {
float factor = 1; map<uint32, float> costs = 1;
} }
message GetIgnoredBeforeCountRequest { message GetIgnoredBeforeCountRequest {

View file

@ -1,5 +1,8 @@
// Copyright: Ankitects Pty Ltd and contributors // Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use std::collections::HashMap;
use anki_proto::generic; use anki_proto::generic;
use crate::collection::Collection; use crate::collection::Collection;
@ -139,30 +142,25 @@ impl crate::services::DeckConfigService for Collection {
costs.pass_count, costs.pass_count,
); );
let before = fsrs::expected_workload( let costs = (70u32..=99u32)
.map(|dr| {
Ok((
dr,
fsrs::expected_workload(
&input.w, &input.w,
input.before, dr as f32 / 100.,
LEARN_SPAN, LEARN_SPAN,
cost_success, cost_success,
cost_failure, cost_failure,
cost_learn, cost_learn,
initial_pass_rate, initial_pass_rate,
TERMINATION_PROB, TERMINATION_PROB,
)?; )?,
let after = fsrs::expected_workload( ))
&input.w,
input.after,
LEARN_SPAN,
cost_success,
cost_failure,
cost_learn,
initial_pass_rate,
TERMINATION_PROB,
)?;
Ok(anki_proto::deck_config::GetRetentionWorkloadResponse {
factor: after / before,
}) })
.collect::<Result<HashMap<_, _>>>()?;
Ok(anki_proto::deck_config::GetRetentionWorkloadResponse { costs })
} }
} }

View file

@ -29,6 +29,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import SimulatorModal from "./SimulatorModal.svelte"; import SimulatorModal from "./SimulatorModal.svelte";
import { import {
GetRetentionWorkloadRequest, GetRetentionWorkloadRequest,
type GetRetentionWorkloadResponse,
UpdateDeckConfigsMode, UpdateDeckConfigsMode,
} from "@generated/anki/deck_config_pb"; } from "@generated/anki/deck_config_pb";
import type Modal from "bootstrap/js/dist/modal"; import type Modal from "bootstrap/js/dist/modal";
@ -66,19 +67,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
$: roundedRetention = Number($config.desiredRetention.toFixed(2)); $: roundedRetention = Number($config.desiredRetention.toFixed(2));
$: desiredRetentionWarning = getRetentionLongShortWarning(roundedRetention); $: desiredRetentionWarning = getRetentionLongShortWarning(roundedRetention);
let timeoutId: ReturnType<typeof setTimeout> | undefined = undefined;
const WORKLOAD_UPDATE_DELAY_MS = 100;
let desiredRetentionChangeInfo = ""; let desiredRetentionChangeInfo = "";
$: { $: {
clearTimeout(timeoutId); showDesiredRetentionTooltip
if (showDesiredRetentionTooltip) { ? getRetentionChangeInfo(roundedRetention, fsrsParams($config))
timeoutId = setTimeout(() => { : "";
getRetentionChangeInfo(roundedRetention, fsrsParams($config));
}, WORKLOAD_UPDATE_DELAY_MS);
} else {
desiredRetentionChangeInfo = "";
}
} }
$: retentionWarningClass = getRetentionWarningClass(roundedRetention); $: retentionWarningClass = getRetentionWarningClass(roundedRetention);
@ -111,21 +104,30 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
} }
let retentionWorloadInfo: undefined | Promise<GetRetentionWorkloadResponse> =
undefined;
async function getRetentionChangeInfo(retention: number, params: number[]) { async function getRetentionChangeInfo(retention: number, params: number[]) {
if (!retentionWorloadInfo) {
const request = new GetRetentionWorkloadRequest({
w: params,
search: defaultparamSearch,
});
retentionWorloadInfo = getRetentionWorkload(request);
}
if (+startingDesiredRetention == roundedRetention) { if (+startingDesiredRetention == roundedRetention) {
desiredRetentionChangeInfo = tr.deckConfigWorkloadFactorUnchanged(); desiredRetentionChangeInfo = tr.deckConfigWorkloadFactorUnchanged();
return; return;
} }
const request = new GetRetentionWorkloadRequest({
w: params, const previous = +startingDesiredRetention * 100;
search: defaultparamSearch, const after = retention * 100;
before: +startingDesiredRetention, const resp = await retentionWorloadInfo;
after: retention, const factor = resp.costs[after] / resp.costs[previous];
});
const resp = await getRetentionWorkload(request);
desiredRetentionChangeInfo = tr.deckConfigWorkloadFactorChange({ desiredRetentionChangeInfo = tr.deckConfigWorkloadFactorChange({
factor: resp.factor.toFixed(2), factor: factor.toFixed(2),
previousDr: (+startingDesiredRetention * 100).toString(), previousDr: previous.toString(),
}); });
} }