Performance (Part 2): From 2 sec to 200ms

by Oct 18, 2018

In the previous tip we added considerable speed to a common script pattern. Now, let’s squeeze out even more performance with a pretty unusual trick. Here is how far we got in the last tip:

$start = Get-Date

$bucket = 1..100000 | ForEach-Object {
    "I am adding $_"
}

$bucket.Count

(Get-Date) - $start

We managed to get down from 6+ minutes to only 46 seconds on PowerShell 5.1 and 1.46 seconds on PowerShell 6.1.

Now take a look at a slight modification – it yields the exact same result:

$start = Get-Date

$bucket = 1..100000 | & { process {
    "I am adding $_"
} }

$bucket.Count

(Get-Date) - $start

This marvel takes only 0.2 seconds on PowerShell 5.1 and 0.5 seconds on PowerShell 6.1.

As you can see, the code simply replaces the ForEach-Object cmdlet with the equivalent & { process { $_ }}. As it turns out, pipeline operations are severely slowed down by advanced functions with cmdlet binding. Once you use a simple function (or a pure script block), you can speed up things drastically. When you take into account where we started with yesterdays’ tip, we managed to speed up things from 6+ minutes (!) to 200ms, yielding the exact same result.

One thing to notice: these optimization techniques apply to loops that iterate often. If you use loops that just iterate a couple of hundred times, you are likely to not experience any significant difference. However, the more often a loop iterates, the more severely will you be bitten by bad design.

Twitter This Tip! ReTweet this Tip!