Summary: Using Measure-Command to determine the fastest approach to a solution.
Last week we were having some fun using PowerShell as a wrapper around the NetSh.exe command’s output. We were left at a decision point.
Which way to go? A For loop to clean up the data, which worked fine or Regular Expressions. Although both work which was the better path to use?
For me initially, when I first started in PowerShell I very often used a For loop for a few reasons.
- It was a simple to navigate loop
- I could see the inner workings in a breakpoint
- it was pretty easy to troubleshoot problems with variables within
But as I progressed in my knowledge of PowerShell I’ve started to touch on Regular expressions as a solution when possible.
A good reason is although they are more complex to work with, the provide a better response time in some cases. As well they also allow me to obtain data without trying to say to myself “Hmmm, where does this substring start and end?”
But right now we have an answer and I’d like to help you figure out how to decide which would be the better way to proceed. We can use Measure-Command to decide which method is faster.
Here’s our first example using a loop to skip on the first line. This is something you might do if you’re first using PowerShell and trying to figure out how to parse data. It is wrapped in the Measure-Command Cmdlet to identify how long it takes
Measure-Command { $list=(netsh.exe wlan show profiles) -match ':'; For ($x=1; $x -lt $list.count; $x++) { $_ } }
Here is our second solution using only a regular expression.
Measure-Command { $list=(netsh.exe wlan show profiles) -match '\s{2,}:\s'; $list }
Now if you run each in PowerShell you might find sometimes for some reason, the more complex loop takes less time, and then sometimes more.
I had my head scratching on this one. “This makes no sense” I was thinking until I realized that with a proper test, I should run multiple samples. Especially since results I saw were roughly less than 1/3 of second in some cases.
The solution to this was easy, I just ran the same solution 100 times with Measure-command
1..100 | Measure-Command { ` $list=(netsh.exe wlan show profiles) -match ':'; ` For ($x=1; $x -lt $list.count; $x++) { $_ } ` }
Now of course this gave me a large result in Milliseconds I would need to divide by 100 to get the results.
(1..100 | Measure-Command { ` $list=(netsh.exe wlan show profiles) -match ':'; ` For ($x=1; $x -lt $list.count; $x++) { $_ } ` }).totalmilliseconds / 100
But another way would be to break apart the Measure-Command objects into an array and use the Measure-Object to do the math. For this we’d need to use For-Each Object to have Measure-Command return each result as a single instance.
(1..100 | foreach-object { Measure-Command { ` $list=(netsh.exe wlan show profiles) -match ':'; ` For ($x=1; $x -lt $list.count; $x++) { $_ } ` }}) | Measure-Object -Property Milliseconds -Average
I prefer this second method as it produces a nice report of how many tests, the actual average and the property we’re measuring. But they both technically work.
The results I got on average for this code was about 159 milliseconds.
I then generated the same solution to test the line using just a regular expression.
(1..100 | Foreach-Object { Measure-Command ` { $list=(netsh.exe wlan show profiles) -match '\s{2,}:\s'; $list ` } }) | Measure-Object -Property Milliseconds -Average
As you can see with over a 100 runs the Regular Expression solution is SLIGHLTY faster. Which means if this were a very large set of data, it could be a big difference in time.
But with this smaller set of data, a For loop is just as valid solution as long as the data results are consistent.
Going forward in this solution, I’m going to choose the moderately faster solution (Regular Expressions) because well, I’m a geek and 2 Milliseconds means a lot to me 😉
Next week we’ll look deeper into parsing the data to only have the Profile name.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Forum. See you tomorrow. Until then, peace.
Your good friend, Doctor Scripto
PowerShell, Doctor Scripto, Sean Kearney
Would suggest you cache the netsh output before running your timings, otherwise you’re adding an external process into your time-test which could skew the results if for some reason there’s a delay in that command.
Completely agreed. The point of the process was more to identify “How long this would take?”. There is definitely areas to improve in the code. The point was to give a basic example of “Here’s what we did” and “Here’s how to see how long it took” when comparing code 🙂
Thanks for the input!