Instantly share code, notes, and snippets.
Created
June 23, 2018 14:19
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save alx9r/3be86f9913d1198a5d8d534ecde318d4 to your computer and use it in GitHub Desktop.
Demonstration of performance impact of sharing AuthorizationManager when opening runspaces in parallel.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# All this C# is to synthesize a signal that corresponds to when a runspace is | |
# ready to be used. | |
# see also PowerShell/PowerShell#7034 | |
Add-Type ' | |
using System.Management.Automation; | |
using System.Management.Automation.Runspaces; | |
using System.Threading.Tasks; | |
using System.Collections.Concurrent; | |
using System.Linq; | |
using System; | |
namespace ns | |
{ | |
public class RunspaceStateTransition : IEquatable<RunspaceStateTransition> | |
{ | |
public Nullable<RunspaceAvailability> Availability { get; private set; } | |
public Nullable<RunspaceState> State { get; private set; } | |
public RunspaceStateTransition | |
( | |
Nullable<RunspaceAvailability> availability, | |
Nullable<RunspaceState> state | |
) | |
{ | |
Availability = availability; | |
State = state; | |
} | |
public bool Equals(RunspaceStateTransition other) | |
{ | |
if (other==null) return false; | |
return Availability == other.Availability && | |
State == other.State; | |
} | |
} | |
public static class OpenRunspaceWaiterTask | |
{ | |
public static Task Create(Runspace runspace) | |
{ | |
var tcs = new TaskCompletionSource<object>(); | |
System.EventHandler<RunspaceAvailabilityEventArgs> availabilityHandler = null; | |
System.EventHandler<RunspaceStateEventArgs> stateHandler = null; | |
var log = new ConcurrentQueue<RunspaceStateTransition>(); | |
availabilityHandler = (s,e) => { | |
log.Enqueue( new RunspaceStateTransition(e.RunspaceAvailability,null)); | |
if ( RunspaceIsOpen(runspace.InitialSessionState,log.ToArray()) ) | |
{ | |
runspace.AvailabilityChanged -= availabilityHandler; | |
runspace.StateChanged -= stateHandler; | |
tcs.SetResult(null); | |
} | |
}; | |
stateHandler = (s,e) => { | |
log.Enqueue( new RunspaceStateTransition(null,e.RunspaceStateInfo.State)); | |
if ( RunspaceIsOpen(runspace.InitialSessionState,log.ToArray()) ) | |
{ | |
runspace.AvailabilityChanged -= availabilityHandler; | |
runspace.StateChanged -= stateHandler; | |
tcs.SetResult(null); | |
} | |
}; | |
runspace.AvailabilityChanged += availabilityHandler; | |
runspace.StateChanged += stateHandler; | |
return tcs.Task; | |
} | |
public static bool RunspaceIsOpen( | |
InitialSessionState initialSessionState, | |
RunspaceStateTransition[] log | |
) | |
{ | |
RunspaceStateTransition[] expected = null; | |
if ( initialSessionState == null || | |
initialSessionState.Modules.Count == 0 ) | |
{ | |
expected = new[] { | |
new RunspaceStateTransition(null,RunspaceState.Opening), | |
new RunspaceStateTransition(RunspaceAvailability.Available,null), | |
new RunspaceStateTransition(null,RunspaceState.Opened ) | |
}; | |
} | |
else | |
{ | |
expected = new [] { | |
new RunspaceStateTransition(null,RunspaceState.Opening), | |
new RunspaceStateTransition(RunspaceAvailability.Available,null), | |
new RunspaceStateTransition(null,RunspaceState.Opened ), | |
new RunspaceStateTransition(RunspaceAvailability.Busy,null), | |
new RunspaceStateTransition(RunspaceAvailability.Available,null) | |
}; | |
} | |
return log.SequenceEqual(expected); | |
} | |
} | |
} | |
' -ReferencedAssemblies @( | |
'System.Threading.Tasks' | |
'System.Management.Automation' | |
'System.Collections.Concurrent' | |
'System.Linq' | |
) | |
Import-Module 'Pester' | |
$p = [System.Environment]::ProcessorCount | |
"ProcessorCount: $p" | |
# one AuthorizationManager per runspace | |
$initialSessionState = [initialsessionstate]::CreateDefault() | |
$initialSessionState.ImportPSModule('Pester') | |
$runspace = 1..$p | | |
% { | |
$thisIss = $initialSessionState.Clone() | |
$thisIss.AuthorizationManager = [initialsessionstate]::CreateDefault().AuthorizationManager | |
[runspacefactory]::CreateRunspace($thisIss) | |
} | |
$awaiter = $runspace | % { [ns.OpenRunspaceWaiterTask]::Create($_) } | |
$runspace.OpenAsync() | |
$t_onePer = Measure-Command { [System.Threading.Tasks.Task]::WaitAll($awaiter) } | |
[pscustomobject]@{ | |
Name = 'One Per' | |
'Elapsed(ms)' = [int]$t_onePer.TotalMilliseconds | |
} | |
# shared AuthorizationManager | |
$initialSessionState = [initialsessionstate]::CreateDefault() | |
$initialSessionState.ImportPSModule('Pester') | |
$runspace = 1..$p | | |
% { | |
[runspacefactory]::CreateRunspace($initialSessionState) | |
} | |
$awaiter = $runspace | % { [ns.OpenRunspaceWaiterTask]::Create($_) } | |
$runspace.OpenAsync() | |
$t_shared = Measure-Command { [System.Threading.Tasks.Task]::WaitAll($awaiter) } | |
[pscustomobject]@{ | |
Name = 'Shared' | |
'Elapsed(ms)' = [int]$t_shared.TotalMilliseconds | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment