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.