Skip to content

Restore caching in Helper.GetCommandInfo #1074

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

Merged
Merged
Changes from 1 commit
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
49 changes: 41 additions & 8 deletions Engine/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class Helper
private readonly static Version minSupportedPSVersion = new Version(3, 0);
private Dictionary<string, Dictionary<string, object>> ruleArguments;
private PSVersionTable psVersionTable;
private Dictionary<string, CommandInfo> commandInfoCache;
private Dictionary<CommandLookupKey, CommandInfo> commandInfoCache;

#endregion

Expand Down Expand Up @@ -142,7 +142,7 @@ public void Initialize()
ruleArguments = new Dictionary<string, Dictionary<string, object>>(StringComparer.OrdinalIgnoreCase);
if (commandInfoCache == null)
{
commandInfoCache = new Dictionary<string, CommandInfo>(StringComparer.OrdinalIgnoreCase);
commandInfoCache = new Dictionary<CommandLookupKey, CommandInfo>();
}

IEnumerable<CommandInfo> aliases = this.invokeCommand.GetCommands("*", CommandTypes.Alias, true);
Expand Down Expand Up @@ -700,15 +700,16 @@ public CommandInfo GetCommandInfoLegacy(string name, CommandTypes? commandType =
cmdletName = name;
}

var key = new CommandLookupKey(name, commandType);
lock (getCommandLock)
{
if (commandInfoCache.ContainsKey(cmdletName))
if (commandInfoCache.ContainsKey(key))
{
return commandInfoCache[cmdletName];
return commandInfoCache[key];
}

var commandInfo = GetCommandInfoInternal(cmdletName, commandType);
commandInfoCache.Add(cmdletName, commandInfo);
commandInfoCache.Add(key, commandInfo);
return commandInfo;
}
}
Expand All @@ -726,15 +727,16 @@ public CommandInfo GetCommandInfo(string name, CommandTypes? commandType = null)
return null;
}

var key = new CommandLookupKey(name, commandType);
lock (getCommandLock)
{
if (commandInfoCache.ContainsKey(name))
if (commandInfoCache.ContainsKey(key))
{
return commandInfoCache[name];
return commandInfoCache[key];
}

var commandInfo = GetCommandInfoInternal(name, commandType);

commandInfoCache.Add(key, commandInfo);
return commandInfo;
}
}
Expand Down Expand Up @@ -1910,6 +1912,37 @@ public Version GetPSVersion()
}

#endregion Methods

private struct CommandLookupKey : IEquatable<CommandLookupKey>
{
private readonly string Name;

private readonly CommandTypes CommandTypes;

internal CommandLookupKey(string name, CommandTypes? commandTypes)
{
Name = name;
CommandTypes = commandTypes ?? CommandTypes.All;
}

public bool Equals(CommandLookupKey other)
{
return CommandTypes == other.CommandTypes
&& Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase);
}

public override int GetHashCode()
{
// Algorithm from https://stackoverflow.com/questions/1646807/quick-and-simple-hash-code-combinations
unchecked
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On this part, worth reading through PowerShell/PowerShell#7134 and the comment it starts with. We discussed the hashcode stuff a bit, but not sure (1) how far we want to take the bikeshedding on hashcodes or (2) if there's a convenient function to do this in both .NET Core and .NET Framework

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PSSA already has many conditional compilation options for different version of powershell, therefore using a special, optimised version for .net core would be acceptable (the only restriction is that it should be part of .net standard 2.0 at the moment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option might be new Tuple(Name.ToUpperInvariant(), CommandTypes).GetHashCode(). I don't think older framework versions have System.ValueTuple, so this would be forced to allocate :(

Copy link
Collaborator

@bergmeister bergmeister Oct 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a NuGet package for System.ValueTuple that supports full .net and .net core

{
int hash = 17;
hash = hash * 31 + Name.ToUpperInvariant().GetHashCode();
hash = hash * 31 + CommandTypes.GetHashCode();
return hash;
}
}
}
}


Expand Down