Skip to content

Commit

Permalink
Use caching to improve performance of large profiles
Browse files Browse the repository at this point in the history
When the local mod list is browsed vue seems to render the whole list
again on some user actions. This causes three methods of the component
to get called repeatedly:

- getMissingDependencies, which uses modifiableModList property, which
  is based on the mod list stored in VueX store. modifiableModList is
  used by other parts of the component as well, so I thought better not
  touch it at this time since figuring out unintended side effects
  would be a lot of work
- getThunderstoreModFromMod, which used the Thunderstore mod list
  stored in VueX, but now uses the list stored in the
  ThunderstorePackages. As far as I can tell both are updated in the
  Splash view and by the scheduled background process in UtilityMixin.
  Ergo this shouldn't break things, but this is the most significant
  functional change in this commit, and therefore most likely culprit
  should problems arise
- isLatestVersion, which did and still does use ThunderstorePackages
  for its shenanigans

So while this commit doesn't reduce the incessant function calls, it
caches the results to a simple object to reduce required calculations.
Effects were tested with a profile containing a mod pack with 109 mods.
Completing the following tasks were timed (roughly and manually), with
the accompanying results (original vs. cached):

- Initial rendering of the local mod list when moving from profile
  selection view: 7.0s vs. 5.4s
- Opening modal to disable a mod with two dependants: 4.4s vs. 1,2s
- Closing the modal without disabling the mod: 4.4s vs. 1.2s
- Opening modal to uninstall the same mod: 4.5s vs. 1.0s
- Uninstalling the mod: 15.8s vs 6.0s

(There might be further changes for optimizing the uninstall process,
since it seems some stuff is done after each dependant is uninstalled,
while it MIGHT be enough to do it just once in the end.)

For a small profile with 3 mods there's no noticeable difference
between the performance of the old and new implementation.
  • Loading branch information
anttimaki committed Jan 10, 2024
1 parent cee0f0f commit a3c9d70
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 12 deletions.
6 changes: 3 additions & 3 deletions src/components/views/LocalModList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ import SearchUtils from '../../utils/SearchUtils';
}
getThunderstoreModFromMod(mod: ManifestV2) {
return ModBridge.getThunderstoreModFromMod(mod, this.thunderstorePackages);
return ModBridge.getCachedThunderstoreModFromMod(mod);
}
async moveUp(vueMod: any) {
Expand Down Expand Up @@ -360,8 +360,8 @@ import SearchUtils from '../../utils/SearchUtils';
this.filterModList();
}
isLatest(vueMod: any): boolean {
return ModBridge.isLatestVersion(vueMod);
isLatest(mod: ManifestV2): boolean {
return ModBridge.isCachedLatestVersion(mod);
}
getMissingDependencies(vueMod: any): string[] {
Expand Down
4 changes: 4 additions & 0 deletions src/model/VersionNumber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,8 @@ export default class VersionNumber implements ReactiveObjectConverterInterface {
const patchCompare = Math.sign(this.patch - version.patch);
return (majorCompare === 0 && minorCompare === 0 && patchCompare === 0);
}

public isEqualOrNewerThan(version: VersionNumber): boolean {
return this.isEqualTo(version) || this.isNewerThan(version);
}
}
54 changes: 45 additions & 9 deletions src/r2mm/mods/ModBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@ import ThunderstoreVersion from '../../model/ThunderstoreVersion';
import ManifestV2 from '../../model/ManifestV2';
import ThunderstorePackages from '../data/ThunderstorePackages';

interface CachedMod {
tsMod: ThunderstoreMod | undefined;
isLatest: boolean;
}

interface ModCache {
[key: string]: CachedMod;
}

export default class ModBridge {
private static CACHE: ModCache = {}

public static getLatestVersion(mod: ManifestV2, modList: ThunderstoreMod[]): ThunderstoreVersion | void {
public static getLatestVersion(mod: ManifestV2, modList: ThunderstoreMod[]): ThunderstoreVersion | undefined {
const matchingMod: ThunderstoreMod | undefined = modList.find((tsMod: ThunderstoreMod) => tsMod.getFullName() === mod.getName());
if (matchingMod === undefined) {
return;
}
// Compare version numbers and reduce.
return matchingMod.getVersions().reduce((v1: ThunderstoreVersion, v2: ThunderstoreVersion) => {
if (v1.getVersionNumber().isNewerThan(v2.getVersionNumber())) {
return v1;
}
return v2;
});
return matchingMod.getVersions().reduce(reduceToNewestVersion);
}

public static getThunderstoreModFromMod(mod: ManifestV2, modList: ThunderstoreMod[]): ThunderstoreMod | undefined {
Expand All @@ -27,10 +32,41 @@ export default class ModBridge {
const mod: ManifestV2 = new ManifestV2().fromReactive(vueMod);
const latestVersion: ThunderstoreVersion | void = ModBridge.getLatestVersion(mod, ThunderstorePackages.PACKAGES);
if (latestVersion instanceof ThunderstoreVersion) {
return mod.getVersionNumber()
.isEqualTo(latestVersion.getVersionNumber()) || mod.getVersionNumber().isNewerThan(latestVersion.getVersionNumber());
return mod.getVersionNumber().isEqualOrNewerThan(latestVersion.getVersionNumber());
}
return true;
}

private static getCached(mod: ManifestV2): CachedMod {
const cacheKey = `${mod.getName()}-${mod.getVersionNumber()}`;

if (ModBridge.CACHE[cacheKey] === undefined) {
const tsMod = ThunderstorePackages.PACKAGES.find((tsMod) => tsMod.getFullName() === mod.getName());

if (tsMod === undefined) {
ModBridge.CACHE[cacheKey] = { tsMod: undefined, isLatest: true };
} else {
const latestVersion = tsMod.getVersions().reduce(reduceToNewestVersion);
const isLatest = mod.getVersionNumber().isEqualOrNewerThan(latestVersion.getVersionNumber());
ModBridge.CACHE[cacheKey] = { tsMod, isLatest };
}
}

return ModBridge.CACHE[cacheKey];
}

public static getCachedThunderstoreModFromMod(mod: ManifestV2): ThunderstoreMod | undefined {
return ModBridge.getCached(mod).tsMod;
}

public static isCachedLatestVersion(mod: ManifestV2): boolean {
return ModBridge.getCached(mod).isLatest;
}
}

const reduceToNewestVersion = (v1: ThunderstoreVersion, v2: ThunderstoreVersion) => {
if (v1.getVersionNumber().isNewerThan(v2.getVersionNumber())) {
return v1;
}
return v2;
};

0 comments on commit a3c9d70

Please sign in to comment.