-
Notifications
You must be signed in to change notification settings - Fork 62
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
Three-way diffing Synchronizer #1707
Conversation
…isk state and game state
…d an error with the sync code exposed by the test
src/Abstractions/NexusMods.Abstractions.Loadouts.Synchronizers/Rules/Signature.cs
Outdated
Show resolved
Hide resolved
src/Abstractions/NexusMods.Abstractions.Loadouts.Synchronizers/Rules/Signature.cs
Show resolved
Hide resolved
/// <summary> | ||
/// A grouping of actions that can be performed on a file. The SyncTree has a `Actions` | ||
/// field and that field is used to put the items in the tree into these specific groups. | ||
/// </summary> | ||
public class SyncActionGroupings | ||
{ | ||
private ConcurrentDictionary<Actions, ConcurrentBag<SyncTreeNode>> _groupings = new(); | ||
|
||
/// <summary> | ||
/// Gets the group of nodes that have the specified action. | ||
/// </summary> | ||
/// <param name="action"></param> | ||
public IReadOnlyCollection<SyncTreeNode> this[Actions action] => _groupings.GetOrAdd(action, static _ => []); | ||
|
||
/// <summary> | ||
/// Adds a node to the groupings based on the actions it has. | ||
/// </summary> | ||
/// <param name="node"></param> | ||
public void Add(SyncTreeNode node) | ||
{ | ||
foreach (var flag in Enum.GetValues<Actions>()) | ||
{ | ||
if (node.Actions.HasFlag(flag)) | ||
AddOne(flag, node); | ||
} | ||
} | ||
|
||
private void AddOne(Actions flag, SyncTreeNode node) | ||
{ | ||
_groupings.GetOrAdd(flag, static _ => []).Add(node); | ||
} | ||
} |
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.
I think there's a ton of performance potential in this class. Without getting into the weeds, the biggest performance wins for the least amount of effort would be the following:
- Using normal
Dictionary
andList
. I might be mistaken, but it doesn't look like we're using instances of this class in a multithreaded context. - Using a static array instead of calling
Enum.GetValues<Actions>
. All theEnum
methods actually use reflection, so this is extra slow. - Prepopulating the groupings' dictionary. Following number 2, we can create a dictionary will all keys. This would also mean we're never writing to the dictionary, and can replace
ConcurrentDictionary
with a normalDictionary
. - Similar to number 2,
HasFlag
can be replaced withHasFlagFast
.
There's more to be done here, but those 4 points together have the best performance/effort ratio.
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.
Good point, I haven't yet added any parallel code around this but it's fairly trivial to do so. My goal was to try and get this code done so it would un-block people while I'm out on holiday. I plan on using the next few days to do performance improvements and other small feature additions separate PRs
This switches our synchronizer over to a much simpler, 3-way merge approach. For a in-depth overview of the changes here, see the included ADR 0014.
Due to the change in how this code operates, I had to delete most of the associated benchmarks. It won't be hard to add them back in when I do a performance pass on this code, but it's plenty fast for now. The various tests we have in place for managing and un-managing games all seem to work.
Added a test in SDV that exercises the code that moves files into the matching mods when ingesting.