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

Parallelize for performance, relationship resolver improvements #3917

Merged
merged 22 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cc91979
Trivial formatting changes
HebaruSan Sep 28, 2023
1e768f2
Move identifier=version syntax from Core to Cmdline
HebaruSan Sep 27, 2023
bea0831
Use standard Message property for InconsistentKraken
HebaruSan Sep 27, 2023
ec3ec5e
Performance monitoring tools
HebaruSan Sep 26, 2023
db2e75d
Make tag index thread-safe
HebaruSan Sep 28, 2023
2b3b980
Parse large JSON module dictionaries in parallel
HebaruSan Sep 27, 2023
4276e20
Parallelize loading of Versions tab
HebaruSan Sep 27, 2023
0a12805
Parallelize loading of CompatibilitySorter
HebaruSan Sep 27, 2023
89fe1d3
Fix RelationshipResolver reporting conflicts with null
HebaruSan Sep 27, 2023
0d7725a
Make RelationshipResolver.ReasonsFor return empty instead of throwing…
HebaruSan Sep 28, 2023
2873ca8
Parallelize creation of GUIMod objects
HebaruSan Sep 28, 2023
0f69666
Parallelize creation of grid rows
HebaruSan Sep 28, 2023
a6f6e10
Parallelize filtering of rows
HebaruSan Sep 28, 2023
9e704f2
Use collection params instead of converting to collection
HebaruSan Sep 28, 2023
dd19068
Parallelize changeset sorting
HebaruSan Sep 28, 2023
97ba9b0
Parallelize JSON parsing of downloaded repo data
HebaruSan Oct 6, 2023
d497119
Indicate which user-selected mods are affected by a conflict
HebaruSan Sep 28, 2023
ff1fdec
Indicate when a removal is because a dependency was removed
HebaruSan Sep 28, 2023
693f954
Fix relationships tab stop sign in recommendations
HebaruSan Sep 26, 2023
07a545e
Obey suppress_recommendations for CmdLine installs
HebaruSan Sep 28, 2023
ff7ec54
Improve recommendations performance
HebaruSan Oct 3, 2023
4c10b68
Remove redundant pre-install ZIP validation
HebaruSan Oct 7, 2023
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
13 changes: 6 additions & 7 deletions Cmdline/Action/Import.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,17 @@ public int RunCommand(CKAN.GameInstance instance, object options)
else
{
log.InfoFormat("Importing {0} files", toImport.Count);
var toInstall = new List<string>();
var toInstall = new List<CkanModule>();
var installer = new ModuleInstaller(instance, manager.Cache, user);
var regMgr = RegistryManager.Instance(instance, repoData);
installer.ImportFiles(toImport, user, mod => toInstall.Add(mod.identifier), regMgr.registry, !opts.Headless);
installer.ImportFiles(toImport, user, mod => toInstall.Add(mod), regMgr.registry, !opts.Headless);
HashSet<string> possibleConfigOnlyDirs = null;
if (toInstall.Count > 0)
{
installer.InstallList(
toInstall,
new RelationshipResolverOptions(),
regMgr,
ref possibleConfigOnlyDirs);
installer.InstallList(toInstall,
new RelationshipResolverOptions(),
regMgr,
ref possibleConfigOnlyDirs);
}
return Exit.OK;
}
Expand Down
13 changes: 10 additions & 3 deletions Cmdline/Action/Install.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,15 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
{
HashSet<string> possibleConfigOnlyDirs = null;
var installer = new ModuleInstaller(instance, manager.Cache, user);
installer.InstallList(modules, install_ops, regMgr, ref possibleConfigOnlyDirs);
installer.InstallList(modules.Select(arg => CkanModule.FromIDandVersion(
regMgr.registry, arg,
options.allow_incompatible
? null
: instance.VersionCriteria()))
.ToList(),
install_ops,
regMgr,
ref possibleConfigOnlyDirs);
user.RaiseMessage("");
done = true;
}
Expand Down Expand Up @@ -213,8 +221,7 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
}
catch (InconsistentKraken ex)
{
// The prettiest Kraken formats itself for us.
user.RaiseError("{0}", ex.InconsistenciesPretty);
user.RaiseError("{0}", ex.Message);
user.RaiseMessage(Properties.Resources.InstallCancelled);
return Exit.ERROR;
}
Expand Down
5 changes: 4 additions & 1 deletion Cmdline/Action/List.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Collections.Generic;

using log4net;
Expand Down Expand Up @@ -39,7 +40,9 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
if (!(options.porcelain) && exportFileType == null)
{
user.RaiseMessage("");
user.RaiseMessage(Properties.Resources.ListGameFound, instance.game.ShortName, instance.GameDir());
user.RaiseMessage(Properties.Resources.ListGameFound,
instance.game.ShortName,
instance.GameDir().Replace('/', Path.DirectorySeparatorChar));
user.RaiseMessage("");
user.RaiseMessage(Properties.Resources.ListGameVersion, instance.game.ShortName, instance.Version());
user.RaiseMessage("");
Expand Down
17 changes: 12 additions & 5 deletions Cmdline/Action/Upgrade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
}
catch (ModuleNotFoundKraken kraken)
{
user.RaiseMessage(Properties.Resources.UpgradeNotFound, kraken.module);
user.RaiseMessage(Properties.Resources.UpgradeNotFound, $"{kraken.module} {kraken.version}");
return Exit.ERROR;
}
catch (InconsistentKraken kraken)
Expand Down Expand Up @@ -166,7 +166,7 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
/// <param name="user">IUser object for output</param>
/// <param name="instance">Game instance to use</param>
/// <param name="modules">List of modules to upgrade</param>
public void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameInstance instance, bool ConfirmPrompt, List<CkanModule> modules)
private void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameInstance instance, bool ConfirmPrompt, List<CkanModule> modules)
{
UpgradeModules(manager, user, instance, repoData,
(ModuleInstaller installer, NetAsyncModulesDownloader downloader, RegistryManager regMgr, ref HashSet<string> possibleConfigOnlyDirs) =>
Expand All @@ -183,12 +183,19 @@ public void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameIns
/// <param name="user">IUser object for output</param>
/// <param name="instance">Game instance to use</param>
/// <param name="identsAndVersions">List of identifier[=version] to upgrade</param>
public void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameInstance instance, List<string> identsAndVersions)
private void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameInstance instance, List<string> identsAndVersions)
{
UpgradeModules(manager, user, instance, repoData,
(ModuleInstaller installer, NetAsyncModulesDownloader downloader, RegistryManager regMgr, ref HashSet<string> possibleConfigOnlyDirs) =>
installer.Upgrade(identsAndVersions, downloader,
ref possibleConfigOnlyDirs, regMgr, true),
installer.Upgrade(
identsAndVersions.Select(arg => CkanModule.FromIDandVersion(
regMgr.registry, arg,
instance.VersionCriteria()))
.ToList(),
downloader,
ref possibleConfigOnlyDirs,
regMgr,
true),
m => identsAndVersions.Add(m.identifier)
);
}
Expand Down
2 changes: 1 addition & 1 deletion Cmdline/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ private static int Scan(CKAN.GameInstance inst, IUser user, string next_command

if (next_command == null)
{
user.RaiseError("{0}", kraken.InconsistenciesPretty);
user.RaiseError("{0}", kraken.Message);
user.RaiseError(Properties.Resources.ScanNotSaved);
}
else
Expand Down
26 changes: 21 additions & 5 deletions ConsoleUI/GameInstanceListScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

using CKAN.Versioning;
using CKAN.ConsoleUI.Toolkit;

Expand Down Expand Up @@ -75,7 +77,9 @@ public GameInstanceListScreen(GameInstanceManager mgr, RepositoryDataManager rep
new List<string>()
);

if (TryGetInstance(theme, instanceList.Selection, repoData, (ConsoleTheme th) => { d.Run(th, (ConsoleTheme thm) => {}); })) {
if (TryGetInstance(theme, instanceList.Selection, repoData,
(ConsoleTheme th) => { d.Run(th, (ConsoleTheme thm) => {}); },
null)) {
try {
manager.SetCurrentInstance(instanceList.Selection.Name);
} catch (Exception ex) {
Expand Down Expand Up @@ -108,7 +112,9 @@ public GameInstanceListScreen(GameInstanceManager mgr, RepositoryDataManager rep
string.Format(Properties.Resources.InstanceListLoadingInstance, instanceList.Selection.Name),
new List<string>()
);
TryGetInstance(theme, instanceList.Selection, repoData, (ConsoleTheme th) => { d.Run(theme, (ConsoleTheme thm) => {}); });
TryGetInstance(theme, instanceList.Selection, repoData,
(ConsoleTheme th) => { d.Run(theme, (ConsoleTheme thm) => {}); },
null);
// Still launch the screen even if the load fails,
// because you need to be able to fix the name/path.
LaunchSubScreen(theme, new GameInstanceEditScreen(manager, repoData, instanceList.Selection));
Expand Down Expand Up @@ -170,10 +176,15 @@ protected override string MenuTip()
/// <param name="ksp">Game instance</param>
/// <param name="repoData">Repository data manager providing info from repos</param>
/// <param name="render">Function that shows a loading message</param>
/// <param name="progress">Function to call with progress updates 0-100</param>
/// <returns>
/// True if successfully loaded, false if it's locked or the registry was corrupted, etc.
/// </returns>
public static bool TryGetInstance(ConsoleTheme theme, GameInstance ksp, RepositoryDataManager repoData, Action<ConsoleTheme> render)
public static bool TryGetInstance(ConsoleTheme theme,
GameInstance ksp,
RepositoryDataManager repoData,
Action<ConsoleTheme> render,
IProgress<int> progress)
{
bool retry;
do {
Expand All @@ -183,7 +194,10 @@ public static bool TryGetInstance(ConsoleTheme theme, GameInstance ksp, Reposito
// Show loading message
render(theme);
// Try to get the lock; this will throw if another instance is in there
RegistryManager.Instance(ksp, repoData);
var regMgr = RegistryManager.Instance(ksp, repoData);
repoData.Prepopulate(regMgr.registry.Repositories.Values.ToList(),
progress);
var compat = regMgr.registry.CompatibleModules(ksp.VersionCriteria());

} catch (RegistryInUseKraken k) {

Expand Down Expand Up @@ -215,7 +229,9 @@ public static bool TryGetInstance(ConsoleTheme theme, GameInstance ksp, Reposito
} catch (Exception e) {

ConsoleMessageDialog errd = new ConsoleMessageDialog(
string.Format(Properties.Resources.InstanceListLoadingError, Path.Combine(ksp.CkanDir(), "registry.json"), e.Message),
string.Format(Properties.Resources.InstanceListLoadingError,
Path.Combine(ksp.CkanDir(), "registry.json").Replace('/', Path.DirectorySeparatorChar),
e.ToString()),
new List<string>() { Properties.Resources.OK }
);
errd.Run(theme);
Expand Down
9 changes: 7 additions & 2 deletions ConsoleUI/InstallScreen.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Transactions;
using System.Collections.Generic;
using System.Linq;

using CKAN.ConsoleUI.Toolkit;

Expand Down Expand Up @@ -76,7 +77,11 @@ public override void Run(ConsoleTheme theme, Action<ConsoleTheme> process = null
plan.Install.Clear();
}
if (plan.Upgrade.Count > 0) {
inst.Upgrade(plan.Upgrade, dl, ref possibleConfigOnlyDirs, regMgr);
inst.Upgrade(plan.Upgrade
.Select(ident => regMgr.registry.LatestAvailable(
ident, manager.CurrentInstance.VersionCriteria()))
.ToList(),
dl, ref possibleConfigOnlyDirs, regMgr);
plan.Upgrade.Clear();
}
if (plan.Replace.Count > 0) {
Expand Down Expand Up @@ -116,7 +121,7 @@ public override void Run(ConsoleTheme theme, Action<ConsoleTheme> process = null
} catch (MissingCertificateKraken ex) {
RaiseError(ex.ToString());
} catch (InconsistentKraken ex) {
RaiseError(ex.InconsistenciesPretty);
RaiseError(ex.Message);
} catch (TooManyModsProvideKraken ex) {

ConsoleChoiceDialog<CkanModule> ch = new ConsoleChoiceDialog<CkanModule>(
Expand Down
2 changes: 1 addition & 1 deletion ConsoleUI/ModListScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ private bool ScanForMods()
regMgr.ScanUnmanagedFiles();
} catch (InconsistentKraken ex) {
// Warn about inconsistent state
RaiseError(Properties.Resources.ModListScanBad, ex.InconsistenciesPretty);
RaiseError(Properties.Resources.ModListScanBad, ex.Message);
}
return true;
}
Expand Down
36 changes: 35 additions & 1 deletion ConsoleUI/SplashScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ public bool Run(ConsoleTheme theme)
{
// If there's a default instance, try to get the lock for it.
GameInstance ksp = manager.CurrentInstance ?? manager.GetPreferredInstance();
if (ksp != null && !GameInstanceListScreen.TryGetInstance(theme, ksp, repoData, (ConsoleTheme th) => Draw(th, false))) {
if (ksp != null
&& !GameInstanceListScreen.TryGetInstance(theme, ksp, repoData,
(ConsoleTheme th) => Draw(th, false),
new Progress<int>(p => drawProgressBar(theme, 22, 20, p)))) {
Console.ResetColor();
Console.Clear();
Console.CursorVisible = true;
Expand Down Expand Up @@ -94,6 +97,35 @@ private void Draw(ConsoleTheme theme, bool pressAny = false)
}
}

private void drawProgressBar(ConsoleTheme theme, int y, int w, int percent)
{
lock (progBarMutex)
{
try {
var doubleWidth = percent * (w - 2) / 50;
if (doubleWidth > lastProgDblW)
{
int lp = (Console.WindowWidth - w) / 2;
var bar = new string(Symbols.fullBlock, percent * (w - 2) / 100);
if ((doubleWidth & 1) == 1)
{
// "Cheat" an extra half character to make the bar more precise
bar += Symbols.leftHalfBlock;
}
// This can throw if the screen is too small
Console.SetCursorPosition(lp, y);
Console.ForegroundColor = theme.SplashNormalFg;
Console.Write("[");
Console.ForegroundColor = theme.SplashAccentFg;
Console.Write(bar.PadRight(w - 2, ' '));
Console.ForegroundColor = theme.SplashNormalFg;
Console.Write("]");
lastProgDblW = doubleWidth;
}
} catch { }
}
}

private void drawCentered(int y, string val)
{
int lp = (Console.WindowWidth - val.Length) / 2;
Expand All @@ -106,6 +138,8 @@ private void drawCentered(int y, string val)

private GameInstanceManager manager;
private RepositoryDataManager repoData;
private int lastProgDblW = -1;
private object progBarMutex = new object();
}

}
8 changes: 8 additions & 0 deletions ConsoleUI/Toolkit/Symbols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ public static class Symbols {
/// and the letters in the splash screen ASCII art
/// </summary>
public static readonly char lowerHalfBlock = cp437c(0xdc);
/// <summary>
/// Full block used for most of a progress bar
/// </summary>
public static readonly char fullBlock = cp437c(0xdb);
/// <summary>
/// Left half block used for right edge of odd width progress bar
/// </summary>
public static readonly char leftHalfBlock = cp437c(0xdd);

private static char cp437c(byte num) { return dosCodePage.GetChars(new byte[] {num})[0]; }
private static string cp437s(byte num) { return $"{cp437c(num)}"; }
Expand Down
49 changes: 49 additions & 0 deletions Core/Converters/JsonParallelDictionaryConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Concurrent;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using CKAN.Extensions;

namespace CKAN
{
/// <summary>
/// A converter that loads a dictionary in parallel,
/// use with large collections of complex objects for a speed boost
/// </summary>
public class JsonParallelDictionaryConverter<V> : JsonConverter
{
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
=> ParseWithProgress(JObject.Load(reader)
.Properties()
.ToArray(),
serializer);

private object ParseWithProgress(JProperty[] properties,
JsonSerializer serializer)
=> Partitioner.Create(properties, true)
.AsParallel()
.WithMergeOptions(ParallelMergeOptions.NotBuffered)
.Select(prop => new KeyValuePair<string, V>(
prop.Name,
prop.Value.ToObject<V>()))
.WithProgress(properties.Length,
serializer.Context.Context as IProgress<int>)
.ToDictionary();

public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}

// Only convert when we're an explicit attribute
public override bool CanConvert(Type object_type) => false;
}
}
Loading