Skip to main content

Thread jobs are faster and lighter weight than other types of jobs. But they still have overhead that can be large when compared to work the job is doing. Thread jobs provide the best performance when the work they perform is greater than the overhead of the session used to run the job.

# Thread job performance
#
# Thread jobs are faster and lighter weight than other types of jobs.
# But they still have overhead that can be large when compared to work the job is doing.
#
# Thread jobs provide the best performance when the work they perform is
# greater than the overhead of the session used to run the job. There are two cases for that meet this criteria.
#
# 1. Work is compute intensive - Running a script on multiple thread jobs can take advantage of multiple processor cores and complete faster.
# 2. Work consists of significant waiting - A script that spends time waiting for I/O or remote call results. Running in parallel usually completes quicker than if run sequentially.
#
# Article: [Thread job performance](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_thread_jobs?view=powershell-7.4#thread-job-performance)
#

# Measure-Command {
#     $logs = $logNames | ForEach-Object {
#         Start-ThreadJob {
#             Get-WinEvent -LogName $using:_ -MaxEvents 5000 2>$null
#         } -ThrottleLimit 10
#     } | Wait-Job | Receive-Job
# }
# TotalMilliseconds : 115994.3 (1 minute 56 seconds)
# $logs.Count
# 50000

$items = @()
$itemLimit = 10
$throttleLimit = 10
$sleepSeconds = 1

# Build the list of items to iterate over, in this case we use server<index>.
for ($i = 0; $i -lt $itemLimit; $i++)
{
    $items += ('server{0}' -f $i)
}

function RunThreadedJobs()
{
    Write-Host "-------------------------`nRunThreadedJobs`n-------------------------" -ForegroundColor Cyan

    $sw = [Diagnostics.Stopwatch]::StartNew()

    $results = $items | ForEach-Object {
        Start-ThreadJob {
            Write-Output -InputObject $using:_ # server<index>
            Start-Sleep -Seconds $using:sleepSeconds
        } -ThrottleLimit $throttleLimit
    } | Wait-Job | Receive-Job

    $sw.Stop()

    $results

    $outputObj = [PSCustomObject]@{
        TotalSeconds = $sw.Elapsed.TotalSeconds
        TotalResults = $results.Count
        TotalItems = $items.Count
        ItemLimit = $itemLimit
        ThrottleLimit = $throttleLimit
        SleepSeconds = $sleepSeconds
    }

    $outputObj | Format-List
    # TotalSeconds  : 2.1986528
    # TotalResults  : 10
    # TotalItems    : 10
    # ItemLimit     : 10
    # ThrottleLimit : 10
    # SleepSeconds  : 1
}

function RunSync()
{
    Write-Host "-------------------------`nRunSync`n-------------------------" -ForegroundColor Cyan

    $sw = [Diagnostics.Stopwatch]::StartNew()

    $results = $items | ForEach-Object {
        Write-Output -InputObject $_ # server<number>
        Start-Sleep -Seconds $sleepSeconds
    }

    $sw.Stop()

    $results

    $outputObj = [PSCustomObject]@{
        TotalSeconds = $sw.Elapsed.TotalSeconds
        TotalResults = $results.Count
        TotalItems = $items.Count
        ItemLimit = $itemLimit
        ThrottleLimit = 'N/A'
        SleepSeconds = $sleepSeconds
    }

    $outputObj | Format-List
    # TotalSeconds  : 10.0176677
    # TotalResults  : 10
    # TotalItems    : 10
    # ItemLimit     : 10
    # ThrottleLimit : N/A
    # SleepSeconds  : 1
}

RunThreadedJobs
RunSync