|
1 | 1 | --- |
2 | 2 | description: Scripting for Performance in PowerShell |
3 | | -ms.date: 12/05/2024 |
| 3 | +ms.date: 08/18/2025 |
4 | 4 | title: PowerShell scripting performance considerations |
5 | 5 | --- |
6 | 6 |
|
@@ -116,7 +116,7 @@ $results |
116 | 116 | >[!NOTE] |
117 | 117 | > In PowerShell 7.5, array addition was optimized and no longer creates a new array for each |
118 | 118 | > operation. The performance considerations described here still apply to PowerShell versions |
119 | | -> prior to 7.5. For more information, see [What's New in PowerShell 7.5][01]. |
| 119 | +> prior to 7.5. For more information, see [What's New in PowerShell 7.5][02]. |
120 | 120 |
|
121 | 121 | Array addition is inefficient because arrays have a fixed size. Each addition to the array creates |
122 | 122 | a new array big enough to hold all elements of both the left and right operands. The elements of |
@@ -276,7 +276,7 @@ performance and memory consumption. |
276 | 276 |
|
277 | 277 | There are at least two alternatives: |
278 | 278 |
|
279 | | -- The [`-join` operator][13] concatenates strings |
| 279 | +- The [`-join` operator][07] concatenates strings |
280 | 280 | - The .NET `[StringBuilder]` class provides a mutable string |
281 | 281 |
|
282 | 282 | The following example compares the performance of these three methods of building a string. |
@@ -438,7 +438,7 @@ $Results = $Employees | ForEach-Object -Process { |
438 | 438 | However, that implementation has to filter all 5000 items in the `$Accounts` collection once for |
439 | 439 | every item in the `$Employee` collection. That can take minutes, even for this single-value lookup. |
440 | 440 |
|
441 | | -Instead, you can make a [Hash Table][02] that uses the shared **Name** property as a key and the |
| 441 | +Instead, you can make a [Hash Table][01] that uses the shared **Name** property as a key and the |
442 | 442 | matching account as the value. |
443 | 443 |
|
444 | 444 | ```powershell |
@@ -632,6 +632,49 @@ Unwrapped = 42.92 ms |
632 | 632 | The unwrapped example is **372 times faster**. Also, notice that the first implementation requires |
633 | 633 | the **Append** parameter, which isn't required for the later implementation. |
634 | 634 |
|
| 635 | +## Avoid unnecessary collection enumeration |
| 636 | + |
| 637 | +The [PowerShell comparison operators][05] have a convience feature when comparing collections. When |
| 638 | +the left-hand value in the expression is a collection, the operator returns the elements of the |
| 639 | +collection that match the right-hand value of the expression. |
| 640 | + |
| 641 | +This feature provides a simple way to filter a collection. For example: |
| 642 | + |
| 643 | +```powershell |
| 644 | +PS> $Collection = 1..99 |
| 645 | +PS> ($Collection -like '*1*') -join ' ' |
| 646 | +
|
| 647 | +1 10 11 12 13 14 15 16 17 18 19 21 31 41 51 61 71 81 91 |
| 648 | +``` |
| 649 | + |
| 650 | +However, when you use a collection comparison in a conditional statement that only expects a |
| 651 | +[boolean][04] result, this feature can result in poor performance. |
| 652 | + |
| 653 | +Take for example: |
| 654 | + |
| 655 | +```powershell |
| 656 | +if ($Collection -like '*1*') { 'Found' } |
| 657 | +``` |
| 658 | + |
| 659 | +In this example, PowerShell compares the right-hand value to every value in the collection and |
| 660 | +returns a collection of results. Since the result isn't empty, the non-null result evaluates as |
| 661 | +`$true`. The condition is true when the first match is found, but PowerShell still enumerates the |
| 662 | +entire collection. This enumeration can have a significant performance impact for large collections. |
| 663 | + |
| 664 | +One way to improve performance is to use the [`Where()`][03] method of the collection. The `Where()` |
| 665 | +method stops evaluating the collection after it finds the first match. |
| 666 | + |
| 667 | +```powershell |
| 668 | +# Create an array of 1048576 items |
| 669 | +$Collection = foreach ($x in 1..1MB) { $x } |
| 670 | +(Measure-Command { if ($Collection -like '*1*') { 'Found' } }).TotalMilliseconds |
| 671 | +633.3695 |
| 672 | +(Measure-Command { if ($Collection.Where({ $_ -like '*1*' }, 'first')) { 'Found' } }).TotalMilliseconds |
| 673 | +2.607 |
| 674 | +``` |
| 675 | + |
| 676 | +For a million items, using the `Where()` method is significantly faster. |
| 677 | + |
635 | 678 | ## Object creation |
636 | 679 |
|
637 | 680 | Creating objects using the `New-Object` cmdlet can be slow. The following code compares the |
@@ -701,7 +744,7 @@ perhaps most commonly used way to create a new **PSObject** and then add new pro |
701 | 744 | negligible however it can become very noticeable for big collections. In that case, the recommended |
702 | 745 | approach is to use an `[OrderedDictionary]` and then convert it to a **PSObject** using the |
703 | 746 | `[pscustomobject]` type accelerator. For more information, see the _Creating ordered dictionaries_ |
704 | | -section of [about_Hash_Tables][19]. |
| 747 | +section of [about_Hash_Tables][06]. |
705 | 748 |
|
706 | 749 | Assume you have the following API response stored in the variable `$json`. |
707 | 750 |
|
@@ -868,39 +911,41 @@ Iterations Test TotalMilliseconds RelativeSpeed |
868 | 911 |
|
869 | 912 | ## Related links |
870 | 913 |
|
871 | | -- [`$null`][03] |
872 | | -- [`[void]`][04] |
873 | | -- [Out-Null][05] |
874 | | -- [`List<T>`][06] |
875 | | -- [`Add(T)` method][07] |
876 | | -- [`[string]`][08] |
877 | | -- [`[int]`][09] |
878 | | -- [`[Object]`][10] |
879 | | -- [`ToArray()` method][11] |
880 | | -- [`[ArrayList]`][12] |
881 | | -- [`[StringBuilder]`][14] |
882 | | -- [`[StreamReader]`][15] |
883 | | -- [`[File]::ReadLines()` method][16] |
884 | | -- [Write-Host][17] |
885 | | -- [Add-Member][18] |
886 | | - |
887 | | -<!-- Link reference definitions --> |
888 | | -[01]: ../../whats-new/what-s-new-in-powershell-75.md#engine-improvements |
889 | | -[02]: ../../learn/deep-dives/everything-about-hashtable.md |
890 | | -[03]: ../../learn/deep-dives/everything-about-null.md |
891 | | -[04]: xref:System.Void |
892 | | -[05]: xref:Microsoft.PowerShell.Core.Out-Null |
893 | | -[06]: xref:System.Collections.Generic.List`1 |
894 | | -[07]: xref:System.Collections.Generic.List`1.Add%2A |
895 | | -[08]: xref:System.String |
896 | | -[09]: xref:System.Int32 |
897 | | -[10]: xref:System.Object |
898 | | -[11]: xref:System.Collections.Generic.List%601.ToArray%2A#system-collections-generic-list-1-toarray |
899 | | -[12]: xref:System.Collections.ArrayList |
900 | | -[13]: /powershell/module/microsoft.powershell.core/about/about_join |
901 | | -[14]: xref:System.Text.StringBuilder |
902 | | -[15]: xref:System.IO.StreamReader |
| 914 | +- [`$null`][01] |
| 915 | +- [System.Void][21] |
| 916 | +- [Out-Null][08] |
| 917 | +- [List\<T\>][13] |
| 918 | +- [Add(T) method][14] |
| 919 | +- [System.String][19] |
| 920 | +- [System.Int32][15] |
| 921 | +- [System.Object][18] |
| 922 | +- [ToArray() method][12] |
| 923 | +- [System.Collections.ArrayList][11] |
| 924 | +- [System.Text.StringBuilder][20] |
| 925 | +- [System.IO.StreamReader][17] |
| 926 | +- [File::ReadLines() method][16] |
| 927 | +- [Write-Host][10] |
| 928 | +- [Add-Member][09] |
| 929 | + |
| 930 | +<!-- link references --> |
| 931 | +[01]: ../../learn/deep-dives/everything-about-hashtable.md |
| 932 | +[02]: ../../whats-new/what-s-new-in-powershell-75.md#engine-improvements |
| 933 | +[03]: /powershell/module/microsoft.powershell.core/about/about_arrays#where |
| 934 | +[04]: /powershell/module/microsoft.powershell.core/about/about_booleans |
| 935 | +[05]: /powershell/module/microsoft.powershell.core/about/about_comparison_operators |
| 936 | +[06]: /powershell/module/microsoft.powershell.core/about/about_hash_tables#creating-ordered-dictionaries |
| 937 | +[07]: /powershell/module/microsoft.powershell.core/about/about_join |
| 938 | +[08]: xref:Microsoft.PowerShell.Core.Out-Null |
| 939 | +[09]: xref:Microsoft.PowerShell.Utility.Add-Member |
| 940 | +[10]: xref:Microsoft.PowerShell.Utility.Write-Host |
| 941 | +[11]: xref:System.Collections.ArrayList |
| 942 | +[12]: xref:System.Collections.Generic.List%601.ToArray%2A#system-collections-generic-list-1-toarray |
| 943 | +[13]: xref:System.Collections.Generic.List`1 |
| 944 | +[14]: xref:System.Collections.Generic.List`1.Add%2A |
| 945 | +[15]: xref:System.Int32 |
903 | 946 | [16]: xref:System.IO.File.ReadLines%2A |
904 | | -[17]: xref:Microsoft.PowerShell.Utility.Write-Host |
905 | | -[18]: xref:Microsoft.PowerShell.Utility.Add-Member |
906 | | -[19]: /powershell/module/microsoft.powershell.core/about/about_hash_tables#creating-ordered-dictionaries |
| 947 | +[17]: xref:System.IO.StreamReader |
| 948 | +[18]: xref:System.Object |
| 949 | +[19]: xref:System.String |
| 950 | +[20]: xref:System.Text.StringBuilder |
| 951 | +[21]: xref:System.Void |
0 commit comments