
Throw away your timestamp calculation snippets for testing script performance in favor of the native PowerShell cmdlet Measure-Command.
Measuring script performance: a timeless problem
As long as you have been writing scripts in any language, you have probably at one point or another been curious how one method of solving problem compares to another method in terms of execution time. To determine how long a script or code segment ran for, you would add variables to capture the current timestamp before and after execution and do the math to calculate the difference. While not necessarily complex, it was tedious.
Measure-Command: PowerShell’s native execution time cmdlet
Microsoft recognized from the get-go with PowerShell v1.0 that having a simple, native way to measure code performance was extremely useful, so they included the cmdlet Measure-Command in the original cmdlet set. Measure-Command wraps a cmdlet or script block to be timed, executes it, and returns the execution time in multiple formats.
Let’s look at an example.
Comparing Test-NetConnection’s InformationLevel options
I previously covered how to use Test-NetConnection as a replacement for ping. Here is a very simple example of Test-NetConnection‘s default ping behavior:
If (Test-NetConnection 8.8.8.8) { Write-Host "Host responds to ping." # Do something on the host... } Else { Write-Host "Host does NOT respond to ping." }
If I run this snippet, Test-NetConnection will perform its fixed number of ping requests at a fixed interval. The boolean result will be correct for the script branching to run. But how long does it take? Is this approach feasible if I need to loop through a list of one hundred hosts? One thousand? Is this the optimal solution?
Wrapping code in Measure-Command script block for testing
To evaluate the performance of this code segment, we wrap the portion of the code we are interested in evaluating in Measure-Command {}:
Measure-Command { Test-NetConnection 8.8.8.8 }
Now when we run this entire script block, we get a detailed execution time information for the code segment:
PS C:\Users\aaron> Measure-Command { Test-NetConnection 8.8.8.8 } Days : 0 Hours : 0 Minutes : 0 Seconds : 4 Milliseconds : 826 Ticks : 48261168 TotalDays : 5.58578333333333E-05 TotalHours : 0.001340588 TotalMinutes : 0.08043528 TotalSeconds : 4.8261168 TotalMilliseconds : 4826.1168
We can see that it took almost five seconds (4.826 seconds, to be more precise) for us to verify the host was available. That may be acceptable when checking a single host, but what if I needed to check one thousand hosts? Based on this measurement, it would take approximately 5000 seconds, or almost 84 minutes JUST to execute the Test-NetConnection cmdlet.
Optimizing and validating improvement
Because we only need to know whether a host responds to ping or not, we only require a boolean value of true or false. We also know Test-NetConnection has a parameter called InformationLevel that defaults to Detailed. However, we can specify an InformationLevel of Quiet to only worry about returning true or false. Let’s see if it has any impact on performance.
Here is our new test script, specifying our InformationLevel of Quiet:
Measure-Command { Test-NetConnection 8.8.8.8 -InformationLevel Quiet }
When we execute this, we can see that by changing the InformationLevel, we have DRASTICALLY reduced the amount of time it takes for Test-NetConnection to execute (in this sample, from 4826ms to 31ms). This is because once a ping reply was received on the first ping attempt, a true could be returned and any additional ping attempts skipped. Given a thousand hosts, we now would only consume approximately 155 seconds with our Test-NetConnection executions.
PS C:\Users\aaron> Measure-Command { Test-NetConnection 8.8.8.8 -InformationLevel Quiet } Days : 0 Hours : 0 Minutes : 0 Seconds : 0 Milliseconds : 31 Ticks : 316953 TotalDays : 3.6684375E-07 TotalHours : 8.80425E-06 TotalMinutes : 0.000528255 TotalSeconds : 0.0316953 TotalMilliseconds : 31.6953
Measure a single cmdlet or an entire script block
In the previous example we restricted our testing to a single cmdlet, but Measure-Command can measure the execution time of whatever script block you wrap in its expression brackets. If you are looking to optimize performance in a large script, you can start by separately wrapping individual functional sections in Measure-Command to look for bottlenecks and then gradually narrow the scope of your Measure-Command test to isolate individual commands that can be improved.
Next Steps
- Revisit an existing script that you think might benefit from some performance optimization. Use Measure-Command to evaluate the performance of different components and see if you can identify an alternative approach that may perform better. Validate the alternative approach using Measure-Command as well.
Reference
- msdn.microsoft.com
Leave a Reply