Make Foreach-Object
2 times faster by reducing unnecessary allocations and boxing (#10047)
This commit is contained in:
parent
5bc3d15a30
commit
201d7aa275
@ -155,6 +155,7 @@
|
||||
<Value>rs</Value>
|
||||
<Value>ps</Value>
|
||||
<Value>op</Value>
|
||||
<Value>sb</Value>
|
||||
</CollectionProperty>
|
||||
</AnalyzerSettings>
|
||||
</Analyzer>
|
||||
|
@ -437,8 +437,11 @@ namespace System.Management.Automation
|
||||
// that can mess up the runspace our CommandInfo object came from.
|
||||
|
||||
var runspace = (RunspaceBase)_context.CurrentRunspace;
|
||||
if (!runspace.RunActionIfNoRunningPipelinesWithThreadCheck(
|
||||
() => GetMergedCommandParameterMetadata(out result)))
|
||||
if (runspace.CanRunActionInCurrentPipeline())
|
||||
{
|
||||
GetMergedCommandParameterMetadata(out result);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Events.SubscribeEvent(
|
||||
source: null,
|
||||
|
@ -806,8 +806,12 @@ namespace System.Management.Automation
|
||||
{nameof(UpdatableHelp), @"Software\Policies\Microsoft\PowerShellCore\UpdatableHelp"},
|
||||
{nameof(ConsoleSessionConfiguration), @"Software\Policies\Microsoft\PowerShellCore\ConsoleSessionConfiguration"}
|
||||
};
|
||||
private static readonly ConcurrentDictionary<Tuple<ConfigScope, string>, PolicyBase> s_cachedPoliciesFromRegistry =
|
||||
new ConcurrentDictionary<Tuple<ConfigScope, string>, PolicyBase>();
|
||||
|
||||
private static readonly ConcurrentDictionary<ConfigScope, ConcurrentDictionary<string, PolicyBase>> s_cachedPoliciesFromRegistry =
|
||||
new ConcurrentDictionary<ConfigScope, ConcurrentDictionary<string, PolicyBase>>();
|
||||
|
||||
private static readonly Func<ConfigScope, ConcurrentDictionary<string, PolicyBase>> s_subCacheCreationDelegate =
|
||||
key => new ConcurrentDictionary<string, PolicyBase>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// The implementation of fetching a specific kind of policy setting from the given configuration scope.
|
||||
@ -915,6 +919,8 @@ namespace System.Management.Automation
|
||||
private static T GetPolicySettingFromGPO<T>(ConfigScope[] preferenceOrder) where T : PolicyBase, new()
|
||||
{
|
||||
PolicyBase policy = null;
|
||||
string policyName = typeof(T).Name;
|
||||
|
||||
foreach (ConfigScope scope in preferenceOrder)
|
||||
{
|
||||
if (InternalTestHooks.BypassGroupPolicyCaching)
|
||||
@ -923,13 +929,10 @@ namespace System.Management.Automation
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = Tuple.Create(scope, typeof(T).Name);
|
||||
if (!s_cachedPoliciesFromRegistry.TryGetValue(key, out policy))
|
||||
var subordinateCache = s_cachedPoliciesFromRegistry.GetOrAdd(scope, s_subCacheCreationDelegate);
|
||||
if (!subordinateCache.TryGetValue(policyName, out policy))
|
||||
{
|
||||
lock (s_cachedPoliciesFromRegistry)
|
||||
{
|
||||
policy = s_cachedPoliciesFromRegistry.GetOrAdd(key, tuple => GetPolicySettingFromGPOImpl<T>(tuple.Item1));
|
||||
}
|
||||
policy = subordinateCache.GetOrAdd(policyName, key => GetPolicySettingFromGPOImpl<T>(scope));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -971,30 +971,16 @@ namespace System.Management.Automation.Runspaces
|
||||
}
|
||||
}
|
||||
|
||||
internal bool RunActionIfNoRunningPipelinesWithThreadCheck(Action action)
|
||||
internal bool CanRunActionInCurrentPipeline()
|
||||
{
|
||||
bool ranit = false;
|
||||
bool shouldRunAction = false;
|
||||
lock (_pipelineListLock)
|
||||
{
|
||||
// If we have no running pipeline, or if the currently running pipeline is
|
||||
// the same as the current thread, then execute the action.
|
||||
|
||||
var pipelineRunning = _currentlyRunningPipeline as PipelineBase;
|
||||
if (pipelineRunning == null ||
|
||||
Thread.CurrentThread.Equals(pipelineRunning.NestedPipelineExecutionThread))
|
||||
{
|
||||
shouldRunAction = true;
|
||||
}
|
||||
return pipelineRunning == null ||
|
||||
Thread.CurrentThread == pipelineRunning.NestedPipelineExecutionThread;
|
||||
}
|
||||
|
||||
if (shouldRunAction)
|
||||
{
|
||||
action();
|
||||
ranit = true;
|
||||
}
|
||||
|
||||
return ranit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -7,14 +7,13 @@ using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Management.Automation.Configuration;
|
||||
using System.Management.Automation.Internal;
|
||||
using System.Management.Automation.Runspaces;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.PowerShell.Commands;
|
||||
|
||||
namespace System.Management.Automation.Host
|
||||
{
|
||||
/// <summary>
|
||||
@ -927,7 +926,10 @@ namespace System.Management.Automation.Host
|
||||
/// </summary>
|
||||
internal static TranscriptionOption GetSystemTranscriptOption(TranscriptionOption currentTranscript)
|
||||
{
|
||||
var transcription = Utils.GetPolicySetting<Transcription>(Utils.SystemWideThenCurrentUserConfig);
|
||||
var transcription = InternalTestHooks.BypassGroupPolicyCaching
|
||||
? Utils.GetPolicySetting<Transcription>(Utils.SystemWideThenCurrentUserConfig)
|
||||
: s_transcriptionSettingCache.Value;
|
||||
|
||||
if (transcription != null)
|
||||
{
|
||||
// If we have an existing system transcript for this process, use that.
|
||||
@ -948,6 +950,9 @@ namespace System.Management.Automation.Host
|
||||
|
||||
internal static TranscriptionOption systemTranscript = null;
|
||||
private static object s_systemTranscriptLock = new Object();
|
||||
private static Lazy<Transcription> s_transcriptionSettingCache = new Lazy<Transcription>(
|
||||
() => Utils.GetPolicySetting<Transcription>(Utils.SystemWideThenCurrentUserConfig),
|
||||
isThreadSafe: true);
|
||||
|
||||
private static TranscriptionOption GetTranscriptOptionFromSettings(Transcription transcriptConfig, TranscriptionOption currentTranscript)
|
||||
{
|
||||
|
@ -975,7 +975,8 @@ namespace System.Management.Automation
|
||||
try
|
||||
{
|
||||
var runspace = (RunspaceBase)context.CurrentRunspace;
|
||||
shouldGenerateEvent = !runspace.RunActionIfNoRunningPipelinesWithThreadCheck(() =>
|
||||
if (runspace.CanRunActionInCurrentPipeline())
|
||||
{
|
||||
InvokeWithPipeImpl(
|
||||
useLocalScope,
|
||||
functionsToDefine,
|
||||
@ -986,7 +987,12 @@ namespace System.Management.Automation
|
||||
scriptThis,
|
||||
outputPipe,
|
||||
invocationInfo,
|
||||
args));
|
||||
args);
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldGenerateEvent = true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -322,6 +322,7 @@ namespace System.Management.Automation
|
||||
internal Guid Id { get; private set; }
|
||||
|
||||
internal bool HasLogged { get; set; }
|
||||
internal bool SkipLogging { get; set; }
|
||||
internal bool IsFilter { get; private set; }
|
||||
|
||||
internal bool IsProductCode
|
||||
@ -824,6 +825,13 @@ namespace System.Management.Automation
|
||||
set { _scriptBlockData.HasLogged = value; }
|
||||
}
|
||||
|
||||
internal bool SkipLogging
|
||||
{
|
||||
get { return _scriptBlockData.SkipLogging; }
|
||||
|
||||
set { _scriptBlockData.SkipLogging = value; }
|
||||
}
|
||||
|
||||
internal Assembly AssemblyDefiningPSTypes { set; get; }
|
||||
|
||||
internal HelpInfo GetHelpInfo(ExecutionContext context, CommandInfo commandInfo, bool dontSearchOnRemoteComputer,
|
||||
@ -1111,7 +1119,7 @@ namespace System.Management.Automation
|
||||
{
|
||||
if (context.EngineSessionState.CurrentScope.LocalsTuple == null)
|
||||
{
|
||||
// If the locals tuple is, that means either:
|
||||
// If the locals tuple is null, that means either:
|
||||
// * we're invoking a script block for a module
|
||||
// * something unexpected
|
||||
context.EngineSessionState.CurrentScope.LocalsTuple = locals;
|
||||
@ -1361,52 +1369,60 @@ namespace System.Management.Automation
|
||||
|
||||
internal static void LogScriptBlockCreation(ScriptBlock scriptBlock, bool force)
|
||||
{
|
||||
if (scriptBlock.HasLogged && !InternalTestHooks.ForceScriptBlockLogging)
|
||||
{
|
||||
// Fast exit if the script block is already logged and we are not force re-logging in tests.
|
||||
return;
|
||||
}
|
||||
|
||||
ScriptBlockLogging logSetting = GetScriptBlockLoggingSetting();
|
||||
if (force || logSetting?.EnableScriptBlockLogging == true)
|
||||
{
|
||||
if (!scriptBlock.HasLogged || InternalTestHooks.ForceScriptBlockLogging)
|
||||
// If script block logging is explicitly disabled, or it's from a trusted
|
||||
// file or internal, skip logging.
|
||||
if (logSetting?.EnableScriptBlockLogging == false ||
|
||||
scriptBlock.ScriptBlockData.IsProductCode)
|
||||
{
|
||||
// If script block logging is explicitly disabled, or it's from a trusted
|
||||
// file or internal, skip logging.
|
||||
if (logSetting?.EnableScriptBlockLogging == false ||
|
||||
scriptBlock.ScriptBlockData.IsProductCode)
|
||||
scriptBlock.SkipLogging = true;
|
||||
return;
|
||||
}
|
||||
|
||||
string scriptBlockText = scriptBlock.Ast.Extent.Text;
|
||||
bool written = false;
|
||||
|
||||
// Maximum size of ETW events is 64kb. Split a message if it is larger than 20k (Unicode) characters.
|
||||
if (scriptBlockText.Length < 20000)
|
||||
{
|
||||
written = WriteScriptBlockToLog(scriptBlock, 0, 1, scriptBlock.Ast.Extent.Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
// But split the segments into random sizes (10k + between 0 and 10kb extra)
|
||||
// so that attackers can't creatively force their scripts to span well-known
|
||||
// segments (making simple rules less reliable).
|
||||
int segmentSize = 10000 + (new Random()).Next(10000);
|
||||
int segments = (int)Math.Floor((double)(scriptBlockText.Length / segmentSize)) + 1;
|
||||
int currentLocation = 0;
|
||||
int currentSegmentSize = 0;
|
||||
|
||||
for (int segment = 0; segment < segments; segment++)
|
||||
{
|
||||
return;
|
||||
}
|
||||
currentLocation = segment * segmentSize;
|
||||
currentSegmentSize = Math.Min(segmentSize, scriptBlockText.Length - currentLocation);
|
||||
|
||||
string scriptBlockText = scriptBlock.Ast.Extent.Text;
|
||||
bool written = false;
|
||||
|
||||
// Maximum size of ETW events is 64kb. Split a message if it is larger than 20k (Unicode) characters.
|
||||
if (scriptBlockText.Length < 20000)
|
||||
{
|
||||
written = WriteScriptBlockToLog(scriptBlock, 0, 1, scriptBlock.Ast.Extent.Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
// But split the segments into random sizes (10k + between 0 and 10kb extra)
|
||||
// so that attackers can't creatively force their scripts to span well-known
|
||||
// segments (making simple rules less reliable).
|
||||
int segmentSize = 10000 + (new Random()).Next(10000);
|
||||
int segments = (int)Math.Floor((double)(scriptBlockText.Length / segmentSize)) + 1;
|
||||
int currentLocation = 0;
|
||||
int currentSegmentSize = 0;
|
||||
|
||||
for (int segment = 0; segment < segments; segment++)
|
||||
{
|
||||
currentLocation = segment * segmentSize;
|
||||
currentSegmentSize = Math.Min(segmentSize, scriptBlockText.Length - currentLocation);
|
||||
|
||||
string textToLog = scriptBlockText.Substring(currentLocation, currentSegmentSize);
|
||||
written = WriteScriptBlockToLog(scriptBlock, segment, segments, textToLog);
|
||||
}
|
||||
}
|
||||
|
||||
if (written)
|
||||
{
|
||||
scriptBlock.HasLogged = true;
|
||||
string textToLog = scriptBlockText.Substring(currentLocation, currentSegmentSize);
|
||||
written = WriteScriptBlockToLog(scriptBlock, segment, segments, textToLog);
|
||||
}
|
||||
}
|
||||
|
||||
if (written)
|
||||
{
|
||||
scriptBlock.HasLogged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scriptBlock.SkipLogging = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1582,6 +1598,9 @@ namespace System.Management.Automation
|
||||
private static string s_lastSeenCertificate = string.Empty;
|
||||
private static bool s_hasProcessedCertificate = false;
|
||||
private static CmsMessageRecipient[] s_encryptionRecipients = null;
|
||||
private static Lazy<ScriptBlockLogging> s_sbLoggingSettingCache = new Lazy<ScriptBlockLogging>(
|
||||
() => Utils.GetPolicySetting<ScriptBlockLogging>(Utils.SystemWideThenCurrentUserConfig),
|
||||
isThreadSafe: true);
|
||||
|
||||
// Reset any static caches if the certificate has changed
|
||||
private static void ResetCertificateCacheIfNeeded(string certificate)
|
||||
@ -1596,7 +1615,12 @@ namespace System.Management.Automation
|
||||
|
||||
private static ScriptBlockLogging GetScriptBlockLoggingSetting()
|
||||
{
|
||||
return Utils.GetPolicySetting<ScriptBlockLogging>(Utils.SystemWideThenCurrentUserConfig);
|
||||
if (InternalTestHooks.BypassGroupPolicyCaching)
|
||||
{
|
||||
return Utils.GetPolicySetting<ScriptBlockLogging>(Utils.SystemWideThenCurrentUserConfig);
|
||||
}
|
||||
|
||||
return s_sbLoggingSettingCache.Value;
|
||||
}
|
||||
|
||||
// Quick check for script blocks that may have suspicious content. If this
|
||||
@ -1919,17 +1943,16 @@ namespace System.Management.Automation
|
||||
|
||||
internal static void LogScriptBlockStart(ScriptBlock scriptBlock, Guid runspaceId)
|
||||
{
|
||||
// When invoking, log the creation of the script block if it has suspicious
|
||||
// content
|
||||
bool forceLogCreation = false;
|
||||
if (scriptBlock._scriptBlockData.HasSuspiciousContent)
|
||||
// Fast exit if the script block can skip logging.
|
||||
if (!scriptBlock.SkipLogging)
|
||||
{
|
||||
forceLogCreation = true;
|
||||
}
|
||||
// When invoking, log the creation of the script block if it has suspicious content
|
||||
bool forceLogCreation = scriptBlock._scriptBlockData.HasSuspiciousContent;
|
||||
|
||||
// We delay logging the creation util the 'Start' so that we can be sure we've
|
||||
// properly analyzed the script block's security.
|
||||
LogScriptBlockCreation(scriptBlock, forceLogCreation);
|
||||
// We delay logging the creation until the 'Start' so that we can be sure we've
|
||||
// properly analyzed the script block's security.
|
||||
LogScriptBlockCreation(scriptBlock, forceLogCreation);
|
||||
}
|
||||
|
||||
if (GetScriptBlockLoggingSetting()?.EnableScriptBlockInvocationLogging == true)
|
||||
{
|
||||
|
@ -25,7 +25,7 @@
|
||||
},
|
||||
"namingRules" : {
|
||||
"allowCommonHungarianPrefixes" : true,
|
||||
"allowedHungarianPrefixes" : [ "n", "r", "l", "i", "io", "is", "fs", "lp", "dw", "h", "rs", "ps", "op" ]
|
||||
"allowedHungarianPrefixes" : [ "n", "r", "l", "i", "io", "is", "fs", "lp", "dw", "h", "rs", "ps", "op", "sb" ]
|
||||
},
|
||||
"maintainabilityRules" : {
|
||||
"topLevelTypes" : [
|
||||
|
Loading…
Reference in New Issue
Block a user