Powershell 7 Vs 5.1 Performance

0 views
Skip to first unread message

Fatima Teem

unread,
Aug 5, 2024, 5:01:01 AM8/5/24
to donsdiperdeasb
HeyScripting Guy! I am wondering if there is an easier way to work with performance counters? For example, rather than having to pick out a whole bunch of counters, are there groups of counters that I can use? If not, that is ok, but I feel like I had to ask. By the way, the Scripting Guys rock. I just had to say it.

If I use the pathsWithInstances property, it will triple the number of counters (on my laptop anyway). The pathsWithInstances property returns the counter path, and then a path for each instance. On my Hyper-V server with seven drives, the pathsWithInstances property will return an instance for each logical drive, as well as an instance for the _Total instance. Compare the truncated output here with the first few lines of output from the previous command:


Because the counter property accepts an array (in fact, each command above supplies an array), I can create my own array of counter paths and make a single query with the Get-Counter cmdlet. In the following command, I get all of the paths from the above four commands and store them in a single variable.


Okay, that is cheating a bit because I did not time obtaining the paths first. But when I run the command to pick up all the paths and then perform the query, I still see a huge improvement in performance. (I would need to do some additional testing, such as rebooting my machine between each query, to get rid of any caching that might be taking place. It would bear checking, and this might be a great way to speed up querying for lots of performance counters).


Well, BB, this ends another Hey, Scripting Guy! Blog post. I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scri...@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.


PowerShell scripts that leverage .NET directly and avoid the pipeline tend to be faster thanidiomatic PowerShell. Idiomatic PowerShell uses cmdlets and PowerShell functions, often leveragingthe pipeline, and resorting to .NET only when necessary.


Many of the techniques described here aren't idiomatic PowerShell and may reduce the readabilityof a PowerShell script. Script authors are advised to use idiomatic PowerShell unless performancedictates otherwise.


The speeds of assigning to $null, casting to [void], and file redirection to $null are almostidentical. However, calling Out-Null in a large loop can be significantly slower, especially inPowerShell 5.1.


Array addition is inefficient because arrays have a fixed size. Each addition to the array createsa new array big enough to hold all elements of both the left and right operands. The elements ofboth operands are copied into the new array. For small collections, this overhead may not matter.Performance can suffer for large collections.


The performance impact of using array addition grows exponentially with the size of the collectionand the number additions. This code compares explicitly assigning values to an array with usingarray addition and using the Add(T) method on a [List] object. It defines explicit assignmentas the baseline for performance.


When using a [List] object, you need to create the list with a specific type, like [String]or [Int]. When you add objects of a different type to the list, they are cast to the specifiedtype. If they can't be cast to the specified type, the method raises an exception.


In this example, PowerShell creates an [ArrayList] to hold the results written to the pipelineinside the array expression. Just before assigning to $results, PowerShell converts the[ArrayList] to an [Object[]].


Strings are immutable. Each addition to the string actually creates a new string big enough to holdthe contents of both the left and right operands, then copies the elements of both operands intothe new string. For small strings, this overhead may not matter. For large strings, this can affectperformance and memory consumption.


It's common to need to use a shared property to identify the same record in different collections,like using a name to retrieve an ID from one list and an email from another. Iterating over thefirst list to find the matching record in the second collection is slow. In particular, therepeated filtering of the second collection has a large overhead.


Looking up keys in a hash table is much faster than filtering a collection by property values.Instead of checking every item in the collection, PowerShell can check if the key is defined anduse its value.


Write-Host can be an order of magnitude slower than [Console]::WriteLine() for specific hostslike pwsh.exe, powershell.exe, or powershell_ise.exe. However, [Console]::WriteLine() isn'tguaranteed to work in all hosts. Also, output written using [Console]::WriteLine() doesn't getwritten to transcripts started by Start-Transcript.


PowerShell compiles the script code to bytecode that's interpreted. Beginning in PowerShell 3, forcode that's repeatedly executed in a loop, PowerShell can improve performance by Just-in-time (JIT)compiling the code into native code.


Loops that have fewer than 300 instructions are eligible for JIT-compilation. Loops larger than thatare too costly to compile. When the loop has executed 16 times, the script is JIT-compiled in thebackground. When the JIT-compilation completes, execution is transferred to the compiled code.


The Basic for-loop example is the base line for performance. The second example wraps the randomnumber generator in a function that's called in a tight loop. The third example moves the loopinside the function. The function is only called once but the code still generates the same amountof random numbers. Notice the difference in execution times for each example.


Consider the following example. The Input.csv file contains 2100 lines. The Export-Csv commandis wrapped inside the ForEach-Object pipeline. The Export-Csv cmdlet is invoked for everyiteration of the ForEach-Object loop.


For the next example, the Export-Csv command was moved outside of the ForEach-Object pipeline.In this case, Export-Csv is invoked only once, but still processes all objects passed out ofForEach-Object.


The unwrapped example is 372 times faster. Also, notice that the first implementation requiresthe Append parameter, which isn't required for the later implementation.


There are situations where we may need to dynamically create objects based on some input, theperhaps most commonly used way to create a new PSObject and then add new properties using theAdd-Member cmdlet. The performance cost for small collections using this technique may benegligible however it can become very noticeable for big collections. In that case, the recommendedapproach is to use an [OrderedDictionary] and then convert it to a PSObject using the[pscustomobject] type accelerator. For more information, see the Creating ordered dictionariessection of about_Hash_Tables.


PowerShell is primarily a scripting language that is not entirely compiled so it may suffer from degraded performance in relation to other tools built on .NET. With the PowerShell v3 release, the PowerShell team started to JIT compile portions of PowerShell script to greatly improve the performance. The introduction of .NET 5.0 and PowerShell 7.1 raised the bar even further.


Measure-Command can be used to time particular script blocks for their execution time. This can be useful for determine for A\B testing around different types of scripts to produce simple performance results.


Performance counters can also play an important role to view the overall .NET runtime performance for a process. You can use the dotnet-counters global tool to monitor PowerShell process performance for version 7 and later.


Collections, like arrays and array lists, make a huge difference when it comes to performance. As you can see in the above example, adding to an array is much slower than using a structure like an array list. In the below example, we loop over 100x as many items in less than half the time using an ArrayList.


Filtering is often built directly into the calling cmdlets and is often referred to as filtering left. This means that you want to include filters as left as possible in a pipeline to avoid having to iterate over too much data later on. This is especially true for cmdlets like Get-ADUser and Get-ChildItem.


In both of the examples below we are filtering using the built in filter mechanisms. With Get-ADUser, this executes the filter within Active Directory rather than retrieving all users and filtering within this local PowerShell process. Much faster!


The Sort-Object cmdlet is great for sorting complex objects based on properties but might not always be suitable for sorting large data sets of primitive types. The [Array]::Sort() method is useful when sorting numbers, characters or strings.


Similar to arrays, strings can provide challenges when using the standard, built-in concatenation and formatting features. To work around this, you can use classes like the StringBuilder class to improve the performance of string operations.


The Where() method was introduced in PowerShell v4 and provides a shorthand way of filtering collections similar to Where-Object. It has the added benefit of being faster. The big downside of Where-Object is that it requires the pipeline to function which slows it down.


While working on a script for work, I ran into a situation where I needed to some filtering on a bunch of data and the thought struck me as to what was the quickest approach to performing a filter of data. We know that Where-Object is the official filtering cmdlet with PowerShell and that it gets the job done without much issue. With PowerShell V4, we also got the .Where() method which is built for Desired State Configuration, but has uses outside of that in terms of performance vs. using Where-Object, but at the expense of having all of the data stored in memory prior to performing the filter whereas Where-Object takes input from the pipeline and processes each item to see what matches the filter.

3a8082e126
Reply all
Reply to author
Forward
0 new messages