Looping through information is a fundamental task. It is done many times a day by many people in many ways on computers. In PowerShell, one way to loop is using a For loop (see Slow Loop).
Hidden in this simple example is a performance hit where looping over 100,000 items has a 2 second response time rather than subsecond. That’s the difference of hitting enter and getting a prompt compared to counting 1 one-thousand, 2 one-thousand.
Table of Contents
Slow Loop
$range = 1..100000 For($i=0; $i -lt $range.Count; $i++) { $i }
A Single Line Change
If you needed to do the same loop 5 times in your script, you could speed it up so the entire script runs faster than executing the first loop.
You do this by storing (caching) the array count in a variable and using it in the for loop.
4x Faster
$range = 1..100000 $count = $range.Count For($i=0; $i -lt $count; $i++) { $i }
Good Habits
Making this a habit is good practice. It eliminates waste and enables your scripts be faster when lots of information is thrown at it when you least expect it.
More Results
Test Range TotalSeconds ---- ----- ------------ Test-WithoutCache 10 0.000339 Test-WithCache 10 0.000201 Test-WithoutCache 100 0.001526 Test-WithCache 100 0.000478 Test-WithoutCache 1000 0.017743 Test-WithCache 1000 0.003395 Test-WithoutCache 10000 0.139237 Test-WithCache 10000 0.033718 Test-WithoutCache 100000 1.410456 Test-WithCache 100000 0.358698 Test-WithoutCache 1000000 14.398003 Test-WithCache 1000000 3.835144
Test Harness
Run the tests and read the script. Notice how the functions are stored in an array using the custom function ql. Extending this harness to handle more tests or different ranges is very simple.
Each test is executed, timed, formatted and cast when the new PSObject property TotalSeconds is created.
TotalSeconds = [Double]("{0:#0.#####0}" -f (Measure-Command { & $test (1..$range) }).TotalSeconds)
Function Test-WithoutCache ($data) { $sum = 0 # antipattern # access the array count on each iteration for($idx = 0 ; $idx -lt $data.count; $idx++) { $sum+=$data[$idx] } $sum } Function Test-WithCache ($data) { # capture the count; cache it $count = $data.count $sum = 0 # use the $count variable in the # for loop and improve performance 4x for($idx = 0 ; $idx -lt $count; $idx++) { $sum+=$data[$idx] } $sum } function ql {$args} $tests = ql Test-WithoutCache Test-WithCache $ranges = ql 10 100 1000 10000 100000 1000000 $(ForEach($range in $ranges) { ForEach($test in $tests) { $msg = ("[{0}] Running {1} with {2} items" -f (Get-Date), $test, $range) Write-Host -ForegroundColor Green $msg New-Object PSObject -Property @{ TotalSeconds = [Double]("{0:#0.#####0}" -f (Measure-Command { & $test (1..$range) }).TotalSeconds) Range = $range Test = $test } | Select Test, Range, TotalSeconds } }) | Format-Table -AutoSize