Quantcast
Channel: TechNet Blogs
Viewing all articles
Browse latest Browse all 36188

Runspace twospace, redspace bluespace…

$
0
0

This is the final of four posts on ways to accomplish parallel processing.  In the previous posts, we looked at using Jobs (or -AsJob), and WorkFlows.   Following the theme of those prior posts, we'll take a look at Runspaces, specifically under the microscope of looking at how the processes and threads are handled to see if it operates as we would think a true multithreaded application would work.

If you are looking for a basic how-to on runspaces. take a look at Boe Prox's article on TechNet for a good tutorial on how runspaces work.

I have to admit that before taking on this task to compare the different parallel processing methods of PowerShell, I had never heard of runspaces.  For me, it was the toughest parallel processing method to learn.  Don't let that dissuade you from taking on the challenge of learning this, the juice is definitely worth the squeeze.

Example Code

The script below is an example of a runspace that basically connects to all servers in my test lab and queries the security event logs for the latest 4000 event 4624s (successful logons).  The code to execute on the remote servers is in the scriptblock toward the bottom of the script and is passed as a scriptblock object parameter to the function higher in the script.

# RUNSPACE SAMPE

Function Process-Parallel($arr, $SB) 
{ 
    # CREATE THE RUNSPACE POOL
    $RunspacePool = [RunspaceFactory]::CreateRunspacePool(1,10) 
    
    # OPEN THE RUNSPACE POOL 
    $RunspacePool.Open()
 
    # CREATE THE RUNSPACE COLLECTION
    $RunspaceCollection = New-Object system.collections.arraylist 
    ForEach ($obj in $arr) 
    { 
        # CREATE A POWERSHELL OBJECT  
        $Powershell = [PowerShell]::Create()
 
        $Powershell.RunspacePool = $RunspacePool 
        $Powershell.AddScript($SB) | Out-Null 
        $Powershell.AddArgument($obj) | Out-Null 

        # CREATE THE RUNSPACE OBJECT
        $RS = New-Object -TypeName PSObject -Property @{
            Runspace = $PowerShell.BeginInvoke() 
            PowerShell = $PowerShell 
        } 
    
        $RunspaceCollection.Add($RS) | Out-Null 
    }

    # CREATE A RETURN ARRAY
    $Return = @() 

    While($RunspaceCollection) 
    {
        # GO THROUGH ALL THE RUNSPACES IN THE COLLECTION 
        ForEach($Runspace in $RunspaceCollection.ToArray())
        {
            # CHECK TO SEE IF RUNSPACE COMPLETED
            If($Runspace.RunSpace.IsCompleted -eq $true)
            {
                # ASSIGN THE RESULT TO $return
                $Return += $Runspace.Powershell.EndInvoke($Runspace.RunSpace) 
                $Runspace.Powershell.dispose() 
                $RunspaceCollection.Remove($Runspace)
            }
        }
    }
    Return $Return 
}

#  GET THE COMPUTERS TO BE CHECKED
$arr = get-adcomputer -filter * -server dc1.contoso.com:3268 

#  PUT TOGETHER SCRIPTBLOCK TO RUN
$SB = { 
    param($Computer) 
    $strCompName = $Computer.Name 
    $Count = (Get-Eventlog Security -ComputerName $Computer.Name -Newest 4000| `Where-Object {$_.EventID -eq '4624'}).count
    Return "$strCompName,$Count"
}

#  EXECUTE THE COMMAND
$Stats = Measure-Command {$OutTest = process-parallel $arr $SB} 

$OutTest 

$Stats

The Analysis

Using ProcMon from the SysInternals Tools Suite, I captured the execution of this script.  As you can see from the capture screenshot below, the runspace ran under a single PowerShell_ISE process, unlike the captures that we looked at with Jobs and WorkFlows (using InlineScript).  This gives runspaces an advantage since they do not consume excessive resources with multiple PowerShell environments.

 

In the filtered ProcMon capture below, you can see that the single PowerShell process ID spawns multiple threads to do the parallel processing, truly multi-threaded.

Now, the important part, the performance.  The runspace was blazing fast, executing the query in 37 seconds.  To give a frame of reference, execution times for  the exact same task with the different methods is listed below:

  • ForEach loop took 1 minute and 23 seconds
  •  Jobs took 1 minute and 20 seconds
  • WorkFlow took 1 minute and 17 seconds
  • RunSpace took 37 seconds

Just plain wow!!  I did have to tweak the Maximum number of runspaces  (2nd number in parenthesis below) to see what combination worked best for my environment.  My lab is just a normal desktop running 20 VMs, so it is pretty taxed at most times.  Your mileage may vary.

$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1,10)

Now What?

The thing you have to consider with workflows is that there is a steep learning curve when compared to the other methods listed above.  In my experience, I tend to put that kind of effort into scripts that will return a dividend on the time investment.  It really comes down to your use case for the script.  If you need blazing fast performance and have the time and skillset to learn and embrace it, this is the only choice.

 

Happy Scripting!!


Viewing all articles
Browse latest Browse all 36188

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>