-
-
Notifications
You must be signed in to change notification settings - Fork 345
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
Memoize lazily evaluated sequences #2953
Conversation
e67caf6
to
548322e
Compare
This does reduce the startup time immensely, nice job! diff --git a/GUI/GUIMod.cs b/GUI/GUIMod.cs
index 06198755..69649ed2 100644
--- a/GUI/GUIMod.cs
+++ b/GUI/GUIMod.cs
@@ -196,9 +196,6 @@ public GUIMod(CkanModule mod, IRegistryQuerier registry, KspVersionCriteria curr
public GUIMod(string identifier, IRegistryQuerier registry, KspVersionCriteria current_ksp_version, bool? incompatible = null)
{
Identifier = identifier;
- IsIncompatible = incompatible
- ?? registry.AllAvailable(identifier)
- .All(m => !m.IsCompatibleKSP(current_ksp_version));
IsAutodetected = registry.IsAutodetected(identifier);
DownloadCount = registry.DownloadCount(identifier);
if (registry.IsAutodetected(identifier))
@@ -216,6 +213,8 @@ public GUIMod(string identifier, IRegistryQuerier registry, KspVersionCriteria c
{
}
+ IsIncompatible = incompatible ?? LatestCompatibleMod is null;
+
// Let's try to find the compatibility for this mod. If it's not in the registry at
// all (because it's a DarkKAN mod) then this might fail.
The debug output will go from (master)
over (your branch)
to (with my diff applied)
|
I like it!! |
I'm just gonna drop the file names + line numbers where my IDE reports List of reports
|
Nice! Latest commit should address all of those. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, startup and instance switching is so much faster now!
Generally CKAN feels much more responsive, this is amazing.
The code for the Memoize type looks reasonable, and everything seems to still work ;) 👍
I feel like the universe was reminding me that paying attention to weird output and tracking it down is almost always rewarded. 🎉 |
Background
In #2928 I learned how
yield return
works; yes, it pauses execution of the function until we request the next element of the sequence, but if you try to access earlier members of the sequence, the whole function will start over again from the beginning, re-running any expensive logic that we were hoping to skip. In other words, it's lazily-evaluated but not memoizing.This is particularly inconvenient when we want to retrieve and use a sequence and take a special action when it's empty. The idiomatic approach is to use
.Any()
followed by aforeach
, but this suffers from the duplicate execution problem described above; any expensive steps that we take at the start of the function will be repeated (including network requests!). #2928 worked around this with abool foundAny
, which is kind of painful to read.Changes
Now a new
.Memoize()
LINQ-style function is added toCKAN.Extensions
based on some work from StackOverflow. It acts as a memoizing wrapper around another enumerable. Each item of the upstream sequence is requested once and only once the first time a downstream consumer requests it, after which it is stored in a list, from which any subsequent requests for the same item are served without accessing the upstream enumerable. This will allow us to use lazy evaluation safely.This function is used to clean up some instances I found of duplicated execution of enumerables (I searched for
.Any()
and checked whether there was aforeach
later for the same variable). If you find more, I'd be happy to add more fixes here.Separate but related, I noticed when running GUI with
--debug
that we seem to check a lot of module compatibilities over and over. I think this is becauseAvailableModule.Latest
's use ofLastOrDefault
requires iterating over its whole sequence. Now weReverse
the sequence (which can be done quickly for aSortedDictionary
) and useFirstOrDefault
instead to reduce the number of modules checked.