Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Avoid sending settings from worker threads #827

Merged
merged 3 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions src/state/optimizer/optimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { defaultJsThreads } from '../slices/controlsSlice';
import type { ExtrasType } from '../slices/extras';
import type { RootState } from '../store';
import { iteratePartitionCount } from './combinatorics';
import type { Character, OptimizerCoreSettings } from './optimizerCore';
import type {
Character,
OptimizerCoreMinimalSettings,
OptimizerCoreSettings,
} from './optimizerCore';
import { characterLT, UPDATE_MS } from './optimizerCore';
import type { ExtrasCombinationEntry } from './optimizerSetup';
import { setupCombinations } from './optimizerSetup';
Expand All @@ -15,6 +19,7 @@ export interface Combination extends ExtrasCombinationEntry {
index: number;

settings: OptimizerCoreSettings;
minimalSettings: OptimizerCoreMinimalSettings;
done: boolean;
list: Character[];
calculationRuns: number;
Expand Down Expand Up @@ -97,6 +102,18 @@ export async function* calculate(
({ extrasCombinationEntry, settings }, index): Combination => ({
...extrasCombinationEntry,
settings,
minimalSettings: {
cachedFormState: settings.cachedFormState,
profession: settings.profession,
specialization: settings.specialization,
weaponType: settings.weaponType,
appliedModifiers: settings.appliedModifiers,
rankby: settings.rankby,
shouldDisplayExtras: settings.shouldDisplayExtras,
extrasCombination: settings.extrasCombination,
modifiers: settings.modifiers,
gameMode: settings.gameMode,
},
done: false,
list: [],
calculationRuns: 0,
Expand Down Expand Up @@ -151,7 +168,12 @@ export async function* calculate(
} else {
const { value } = result;
if (value.newList) {
combination.list = value.newList;
const newList: Character[] = value.newList.map((character) => ({
...character,
settings: combination.minimalSettings,
}));

combination.list = newList;
}
if (value.isChanged) {
isChanged = true;
Expand Down Expand Up @@ -241,6 +263,18 @@ export async function* calculateHeuristic(
({ extrasCombinationEntry, settings }, index) => ({
...extrasCombinationEntry,
settings,
minimalSettings: {
cachedFormState: settings.cachedFormState,
profession: settings.profession,
specialization: settings.specialization,
weaponType: settings.weaponType,
appliedModifiers: settings.appliedModifiers,
rankby: settings.rankby,
shouldDisplayExtras: settings.shouldDisplayExtras,
extrasCombination: settings.extrasCombination,
modifiers: settings.modifiers,
gameMode: settings.gameMode,
},
done: false,
list: [],
calculationRuns: 0,
Expand Down Expand Up @@ -307,8 +341,12 @@ export async function* calculateHeuristic(
} else {
const { value } = result;
if (value.newList) {
const newList: Character[] = value.newList.map((character) => ({
...character,
settings: combination.minimalSettings,
}));
// eslint-disable-next-line prefer-destructuring
combination.heuristicBestResult = value.newList[0];
combination.heuristicBestResult = newList[0];
}
if (value.isChanged) {
isChanged = true;
Expand Down
51 changes: 19 additions & 32 deletions src/state/optimizer/optimizerCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ export type OptimizerCoreSettings = OptimizerCoreSettingsPerCalculation &
extrasCombination: ExtrasCombination;
};

// only supply character with settings it uses to render
export type OptimizerCoreMinimalSettings = Pick<
OptimizerCoreSettings,
| 'cachedFormState'
Expand Down Expand Up @@ -227,7 +228,6 @@ interface Results {
}
export interface CharacterUnprocessed {
id?: string;
settings: OptimizerCoreMinimalSettings;
attributes?: Attributes;
gear: Gear;
gearStats: GearStats;
Expand All @@ -237,20 +237,22 @@ export interface CharacterUnprocessed {
infusions: Partial<Record<InfusionName, number>>;
results?: Results;
}
export interface Character extends CharacterUnprocessed {
export interface CharacterProcessed extends CharacterUnprocessed {
// note: this is not actually accurate
// (we convince typescript every attribute is defined via a type predicate in calcStats)
// TODO: improve this
attributes: Required<Attributes>;
}
export interface Character extends CharacterProcessed {
settings: OptimizerCoreMinimalSettings;
}

export type CalculateGenerator = ReturnType<OptimizerCore['calculate']>;

export const UPDATE_MS = 90;

export class OptimizerCore {
settings: OptimizerCoreSettings;
minimalSettings: OptimizerCoreMinimalSettings;
applyInfusionsFunction: (
this: OptimizerCore,
gear: Gear,
Expand All @@ -260,27 +262,14 @@ export class OptimizerCore {

condiResultCache = new Map<number, number>();
worstScore: number = 0;
list: Character[] = [];
list: CharacterProcessed[] = [];
isChanged = true;
uniqueIDCounter = 0;
randomId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
conditionData: typeof conditionData;

constructor(settings: OptimizerCoreSettings) {
this.settings = settings;
// only supply character with settings it uses to render
this.minimalSettings = {
cachedFormState: settings.cachedFormState,
profession: settings.profession,
specialization: settings.specialization,
weaponType: settings.weaponType,
appliedModifiers: settings.appliedModifiers,
rankby: settings.rankby,
shouldDisplayExtras: settings.shouldDisplayExtras,
extrasCombination: settings.extrasCombination,
modifiers: settings.modifiers,
gameMode: settings.gameMode,
};
this.conditionData = settings.gameMode === 'wvw' ? conditionDataWvW : conditionData;

let applyInfusionsFunction: OptimizerCore['applyInfusionsFunction'];
Expand Down Expand Up @@ -508,7 +497,6 @@ export class OptimizerCore {
const character: CharacterUnprocessed = {
gear, // passed by reference
infusions, // passed by reference
settings: this.minimalSettings, // passed by reference
gearStats, // passed by reference
attributes: undefined,
valid: true,
Expand Down Expand Up @@ -636,7 +624,7 @@ export class OptimizerCore {
return temp.attributes[settings.rankby] > this.worstScore;
}

insertCharacter(character: Character) {
insertCharacter(character: CharacterProcessed) {
const { settings } = this;

if (
Expand Down Expand Up @@ -687,7 +675,7 @@ export class OptimizerCore {
updateAttributes(
character: CharacterUnprocessed,
noRounding = false,
): asserts character is Character {
): asserts character is CharacterProcessed {
const { modifiers } = this.settings;
const { damageMultiplier } = modifiers;

Expand Down Expand Up @@ -715,7 +703,7 @@ export class OptimizerCore {
updateAttributesFast(
character: CharacterUnprocessed,
skipValidation = false,
): asserts character is Character {
): asserts character is CharacterProcessed {
const { settings } = this;
const { modifiers } = this.settings;
const { damageMultiplier } = modifiers;
Expand Down Expand Up @@ -762,7 +750,7 @@ export class OptimizerCore {
character: CharacterUnprocessed,
modifiers: Modifiers,
noRounding = false,
): asserts character is Character {
): asserts character is CharacterProcessed {
const { settings } = this;

const round = noRounding ? (val: number) => val : roundEven;
Expand Down Expand Up @@ -839,7 +827,7 @@ export class OptimizerCore {
}
}

checkInvalid(character: Character) {
checkInvalid(character: CharacterProcessed) {
const { settings } = this;
const { attributes } = character;

Expand All @@ -865,7 +853,7 @@ export class OptimizerCore {
return invalid;
}

checkInvalidIndicators(character: Character) {
checkInvalidIndicators(character: CharacterProcessed) {
const { settings } = this;
const { attributes } = character;

Expand All @@ -881,7 +869,7 @@ export class OptimizerCore {
return invalid;
}

calcPower(character: Character, damageMultiplier: DamageMultiplier) {
calcPower(character: CharacterProcessed, damageMultiplier: DamageMultiplier) {
const { settings } = this;
const { attributes } = character;

Expand Down Expand Up @@ -961,7 +949,7 @@ export class OptimizerCore {
(this.conditionData[condition].factor * cdmg + this.conditionData[condition].baseDamage) * mult;

calcCondi(
character: Character,
character: CharacterProcessed,
damageMultiplier: DamageMultiplier,
relevantConditions: readonly DamagingConditionName[],
) {
Expand Down Expand Up @@ -1007,7 +995,7 @@ export class OptimizerCore {
return condiDamageScore;
}

calcSurvivability(character: Character, damageMultiplier: DamageMultiplier) {
calcSurvivability(character: CharacterProcessed, damageMultiplier: DamageMultiplier) {
const { attributes } = character;

attributes['Effective Health'] =
Expand All @@ -1016,7 +1004,7 @@ export class OptimizerCore {
attributes['Survivability'] = attributes['Effective Health'] / 1967;
}

calcHealing(character: Character) {
calcHealing(character: CharacterProcessed) {
const { attributes } = character;

// reasonably representative skill: druid celestial avatar 4 pulse
Expand All @@ -1027,7 +1015,7 @@ export class OptimizerCore {
attributes['Healing'] = attributes['Effective Healing'];
}

calcResults(character: Character) {
calcResults(character: CharacterProcessed) {
if (character.results) return;

const { settings } = this;
Expand Down Expand Up @@ -1149,7 +1137,6 @@ export class OptimizerCore {
*/
clone(character: CharacterUnprocessed): CharacterUnprocessed {
return {
settings: character.settings, // passed by reference
attributes: character.attributes, // passed by reference
gear: character.gear, // passed by reference
gearStats: character.gearStats, // passed by reference
Expand All @@ -1163,8 +1150,8 @@ export class OptimizerCore {

// returns a positive value if B is better than A
export function characterLT(
a: Character | undefined,
b: Character | undefined,
a: CharacterProcessed | undefined,
b: CharacterProcessed | undefined,
rankby: IndicatorName,
): number {
if (!a && !b) return 0;
Expand Down
Loading