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

Speedup cold runs of PSSA by using a runspace pool and parallelizing the slowest rule (AvoidAlias) #1178

10 changes: 7 additions & 3 deletions Engine/CommandInfoCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Concurrent;
using System.Management.Automation;
using System.Linq;
using System.Management.Automation.Runspaces;

namespace Microsoft.Windows.PowerShell.ScriptAnalyzer
{
Expand All @@ -14,16 +15,17 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer
internal class CommandInfoCache
{
private readonly ConcurrentDictionary<CommandLookupKey, Lazy<CommandInfo>> _commandInfoCache;

private readonly Helper _helperInstance;
private readonly RunspacePool _runspacePool;

/// <summary>
/// Create a fresh command info cache instance.
/// </summary>
public CommandInfoCache(Helper pssaHelperInstance)
public CommandInfoCache(Helper pssaHelperInstance, RunspacePool runspacePool)
{
_commandInfoCache = new ConcurrentDictionary<CommandLookupKey, Lazy<CommandInfo>>();
_helperInstance = pssaHelperInstance;
_runspacePool = runspacePool;
}

/// <summary>
Expand Down Expand Up @@ -64,14 +66,16 @@ public CommandInfo GetCommandInfoLegacy(string commandOrAliasName, CommandTypes?
/// Get a CommandInfo object of the given command name
/// </summary>
/// <returns>Returns null if command does not exists</returns>
private static CommandInfo GetCommandInfoInternal(string cmdName, CommandTypes? commandType)
private CommandInfo GetCommandInfoInternal(string cmdName, CommandTypes? commandType)
{
// 'Get-Command ?' would return % for example due to PowerShell interpreting is a single-character-wildcard search and not just the ? alias.
// For more details see https://github.com/PowerShell/PowerShell/issues/9308
cmdName = WildcardPattern.Escape(cmdName);

using (var ps = System.Management.Automation.PowerShell.Create())
{
ps.RunspacePool = _runspacePool;

ps.AddCommand("Get-Command")
.AddParameter("Name", cmdName)
.AddParameter("ErrorAction", "SilentlyContinue");
Expand Down
15 changes: 11 additions & 4 deletions Engine/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Language;
using System.Management.Automation.Runspaces;

namespace Microsoft.Windows.PowerShell.ScriptAnalyzer
{
Expand All @@ -29,6 +30,7 @@ public class Helper
private PSVersionTable psVersionTable;

private readonly Lazy<CommandInfoCache> _commandInfoCacheLazy;
private readonly RunspacePool _runSpacePool;

#endregion

Expand Down Expand Up @@ -113,7 +115,11 @@ internal set
/// </summary>
private Helper()
{
_commandInfoCacheLazy = new Lazy<CommandInfoCache>(() => new CommandInfoCache(pssaHelperInstance: this));
// There are 5 rules that use the CommandInfo cache but one rule (AvoidAlias) makes parallel queries.
// Therefore 10 runspaces was a heuristic measure where no more speed improvement was seen.
_runSpacePool = RunspaceFactory.CreateRunspacePool(1, 10);
_runSpacePool.Open();
_commandInfoCacheLazy = new Lazy<CommandInfoCache>(() => new CommandInfoCache(pssaHelperInstance: this, runspacePool: _runSpacePool));
}

/// <summary>
Expand Down Expand Up @@ -299,11 +305,12 @@ public PSModuleInfo GetModuleManifest(string filePath, out IEnumerable<ErrorReco
Collection<PSObject> psObj = null;
using (var ps = System.Management.Automation.PowerShell.Create())
{
ps.RunspacePool = _runSpacePool;
ps.AddCommand("Test-ModuleManifest")
.AddParameter("Path", filePath)
.AddParameter("WarningAction", ActionPreference.SilentlyContinue);
try
{
ps.AddCommand("Test-ModuleManifest");
ps.AddParameter("Path", filePath);
ps.AddParameter("WarningAction", ActionPreference.SilentlyContinue);
psObj = ps.Invoke();
}
catch (CmdletInvocationException e)
Expand Down