There is C# benchmark for For VS Foreach. It was very interesting to use in code.
C# - For Vs Foreach - Benchmark 2013
Introduction
I could not found greate article for "Which is better practive among "For" VS "Foreach" in PowerShell". I believe there should be some point of view about it, but let's try benchmark int with way how original C# site doing. What about PowerShelll 1.0 and 2.0? Sorry but please forgive me only about PowerShell 3.0.
Target
Like other program languages, PowerShell also have several Loop and I have decided to do benchmark, to compare the perfomance with for
, foreach
, Foreach-Object
Cmdlet, ScriptBlock
and filter
.
for foreach Foreach-Object ScriptBlock Filter
As you know, they wrote like this in PowerShell.
for (<Initialization Pipeline>; <Evaluate Pipeline>;<increment Pipeline>){< Sentence >} foreach (<Variable> in <Loop Pipeline>){< Sentence>} <Parameter> | Foreach-Object {begin}{process}{end} &{Sentence} filter <Name>{param(<List of Parameter>) <Sentence>}
The Benchmark Result
Followed Original site.
Measured in miliseconds Collection length: 30000000
Data Type | Collection Type | Loop Type | Test 1 | Test 2 | Test 3 | Average |
int | int | For | 275899.00 | 265212.00 | 283013.40 | 274708.13 |
int | Foreach | 186429.45 | 176947.98 | 198700.93 | 187359.46 | |
int | Foreach-Object | 1050280.06 | 1055265.18 | 1035663.52 | 1047069.58 | |
int | ScriptBlock | 227393.92 | 240253.13 | 224370.40 | 230672.48 | |
int | Filter | 237666.43 | 233423.09 | 233057.27 | 234715.60 | |
int | List<int> | For | 340137.03 | 355315.28 | 335932.48 | 343794.93 |
int | Foreach | 241257.27 | 260461.74 | 240227.87 | 247315.63 | |
int | Foreach-Object | 1160124.71 | 1290480.25 | 1156974.90 | 1202526.62 | |
int | ScriptBlock | 300991.85 | 322849.46 | 295052.61 | 306297.97 | |
int | Filter | 342645.71 | 308032.91 | 348444.29 | 347944.69 | |
int | ArrayList<int> | For | 258247.16 | 280059.96 | 272251.21 | 270186.11 |
int | Foreach | 162259.60 | 174109.73 | 172552.03 | 169640.45 | |
int | Foreach-Object | 997676.13 | 1079849.90 | 1057534.18 | 1045020.07 | |
int | ScriptBlock | 209276.20 | 232152.56 | 214621.41 | 218683.39 | |
int | Filter | 229675.65 | 230850.35 | 231555.98 | 231299.99 |
Conclusion
Speed of Collections
- ArrayList (Fastest)
- int
- List (Slowest)
Data Type - Operation
All Datatype shows same disposition.
- int (Array)
- List
- ArrayList
Foreach is the fastest, almost no difference between ScriptBlock and Filter. Foreach-Object seeems obviously slow.
- Foreach (Fastest)
- ScriptBlock
- Filter
- Foreach-Object (Slowest)
Which Loop type to be use
I should note such a cost for Foreach-Object Cmdlet.
Data Type | Collection Type | Loop Type | Average | v.s. int For | v.s. int |
int | int | For | 274708.13 | - | - |
int | Foreach | 187359.46 | 68.20% | - | |
int | Foreach-Object | 1047069.58 | 381.16% | - | |
int | ScriptBlock | 230672.48 | 83.97% | - | |
int | Filter | 234715.60 | 85.44% | - | |
int | List<int> | For | 343794.93 | 125.15% | 125.15% |
int | Foreach | 247315.63 | 90.03% | 132.00% | |
int | Foreach-Object | 1202526.62 | 437.75% | 114.85% | |
int | ScriptBlock | 306297.97 | 111.50% | 132.78% | |
int | Filter | 347944.69 | 126.66% | 148.24% | |
int | ArrayList<int> | For | 270186.11 | 98.35% | 98.35% |
int | Foreach | 169640.45 | 61.75% | 90.54% | |
int | Foreach-Object | 1045020.07 | 380.41% | 99.80% | |
int | ScriptBlock | 218683.39 | 79.61% | 94.80% | |
int | Filter | 231299.99 | 84.20% | 98.54% |
It takes time V.S. "4 times of for", "5-6 time of foeach" and "4-4.5 time of ScriptBlock/Filter". Also ScriptBlock was next position of foreach and Filter shows almost same speed. I thought In-Action said Foreach-Object was just a one type of Filter, but benchmark result in it isn't. Filter should be takes cost for ScriptBlock {process{}} + set into PSObject:Function and was called. I don't to write foreach that foreach couldn't use in Pipeline, but now we can put Filter in alter choice. So I recomend as ...
- foreach : for huge data like Log file and when it doesn't need to use the Pipeline.
- Filter : It data was quite big but requried to use the Pipeline.
- ScriptBlock : When using standalone, I mean not for the Pipeline or foreach statesment.
- Foreach-Object : When required to pass Object through the Pipeline, also data was small and speed should be not considerble.
How about Collection size to take cost take exponential
There is no exponential trend but only propotion to Collection size. You can estimate time spend.
- Foreach (Fastest)
- ScriptBlock
- Filter
- Foreach-Object (Slowest)
Here's result with Collection size for 10,100,1000,10000,100000,1000000.
Measured in miliseconds Collection length: 10
Data Type | Collection Type | Loop Type | Test 1 | Test 2 | Test 3 | Average |
int | int | For | 0.15 | 0.22 | 0.21 | 0.19 |
int | int | Foreach | 0.24 | 0.15 | 0.11 | 0.17 |
int | int | Foreach-Object | 0.57 | 0.62 | 0.67 | 0.62 |
int | int | ScriptBlock | 0.19 | 0.26 | 0.22 | 0.22 |
int | int | Filter | 0.21 | 0.20 | 0.19 | 0.20 |
int | List<int> | For | 0.19 | 0.24 | 0.16 | 0.20 |
int | List<int> | Foreach | 0.13 | 0.33 | 0.13 | 0.19 |
int | List<int> | Foreach-Object | 0.48 | 0.66 | 0.64 | 0.59 |
int | List<int> | ScriptBlock | 0.30 | 0.28 | 0.24 | 0.27 |
int | List<int> | Filter | 0.34 | 0.18 | 0.19 | 0.24 |
int | ArrayList<int> | For | 0.12 | 0.23 | 0.13 | 0.16 |
int | ArrayList<int> | Foreach | 0.09 | 0.19 | 0.10 | 0.13 |
int | ArrayList<int> | Foreach-Object | 0.42 | 0.77 | 0.50 | 0.56 |
int | ArrayList<int> | ScriptBlock | 0.15 | 0.25 | 0.20 | 0.20 |
int | ArrayList<int> | Filter | 0.16 | 0.19 | 0.15 | 0.17 |
Measured in miliseconds Collection length: 100
Data Type | Collection Type | Loop Type | Test 1 | Test 2 | Test 3 | Average |
int | int | For | 1.08 | 0.90 | 1.08 | 1.02 |
int | int | Foreach | 0.66 | 0.67 | 0.69 | 0.67 |
int | int | Foreach-Object | 3.64 | 3.49 | 3.59 | 3.57 |
int | int | ScriptBlock | 0.94 | 0.85 | 0.96 | 0.91 |
int | int | Filter | 1.02 | 1.07 | 1.05 | 1.05 |
int | List<int> | For | 1.24 | 1.15 | 1.17 | 1.19 |
int | List<int> | Foreach | 0.92 | 2.91 | 0.97 | 1.60 |
int | List<int> | Foreach-Object | 7.69 | 4.48 | 4.21 | 5.46 |
int | List<int> | ScriptBlock | 1.16 | 1.20 | 1.07 | 1.14 |
int | List<int> | Filter | 1.26 | 1.25 | 1.05 | 1.19 |
int | ArrayList<int> | For | 1.01 | 0.99 | 1.13 | 1.04 |
int | ArrayList<int> | Foreach | 0.67 | 0.77 | 0.57 | 0.67 |
int | ArrayList<int> | Foreach-Object | 8.37 | 3.50 | 3.52 | 5.13 |
int | ArrayList<int> | ScriptBlock | 0.76 | 0.79 | 0.75 | 0.77 |
int | ArrayList<int> | Filter | 0.75 | 0.97 | 0.78 | 0.84 |
Measured in miliseconds Collection length: 1000
Data Type | Collection Type | Loop Type | Test 1 | Test 2 | Test 3 | Average |
int | int | For | 13.13 | 12.11 | 10.33 | 11.86 |
int | int | Foreach | 7.38 | 6.54 | 6.70 | 6.88 |
int | int | Foreach-Object | 41.38 | 39.61 | 37.85 | 39.61 |
int | int | ScriptBlock | 10.03 | 8.14 | 10.27 | 9.48 |
int | int | Filter | 8.97 | 9.04 | 7.33 | 8.45 |
int | List<int> | For | 15.10 | 12.45 | 13.04 | 13.53 |
int | List<int> | Foreach | 9.94 | 9.86 | 8.30 | 9.37 |
int | List<int> | Foreach-Object | 45.20 | 41.35 | 44.35 | 43.63 |
int | List<int> | ScriptBlock | 12.36 | 10.13 | 10.42 | 10.97 |
int | List<int> | Filter | 12.11 | 10.80 | 9.74 | 10.88 |
int | ArrayList<int> | For | 9.13 | 9.46 | 11.98 | 10.19 |
int | ArrayList<int> | Foreach | 6.23 | 6.95 | 6.35 | 6.51 |
int | ArrayList<int> | Foreach-Object | 39.64 | 40.04 | 37.64 | 39.11 |
int | ArrayList<int> | ScriptBlock | 7.65 | 7.15 | 7.17 | 7.32 |
int | ArrayList<int> | Filter | 6.92 | 7.19 | 9.17 | 7.76 |
Measured in miliseconds Collection length: 10000
Data Type | Collection Type | Loop Type | Test 1 | Test 2 | Test 3 | Average |
int | int | For | 105.29 | 100.37 | 97.21 | 100.95 |
int | int | Foreach | 68.30 | 66.19 | 65.25 | 66.58 |
int | int | Foreach-Object | 386.23 | 367.49 | 377.58 | 377.10 |
int | int | ScriptBlock | 87.87 | 80.90 | 83.52 | 84.10 |
int | int | Filter | 84.34 | 82.42 | 80.99 | 82.58 |
int | List<int> | For | 132.17 | 125.16 | 121.24 | 126.19 |
int | List<int> | Foreach | 90.14 | 88.64 | 87.66 | 88.81 |
int | List<int> | Foreach-Object | 425.56 | 427.77 | 417.71 | 423.68 |
int | List<int> | ScriptBlock | 112.23 | 107.41 | 108.86 | 109.50 |
int | List<int> | Filter | 102.32 | 143.87 | 104.03 | 116.74 |
int | ArrayList<int> | For | 102.66 | 94.42 | 97.98 | 98.35 |
int | ArrayList<int> | Foreach | 63.03 | 62.88 | 63.24 | 63.05 |
int | ArrayList<int> | Foreach-Object | 374.20 | 369.76 | 376.02 | 373.33 |
int | ArrayList<int> | ScriptBlock | 86.63 | 80.63 | 80.79 | 82.68 |
int | ArrayList<int> | Filter | 75.81 | 76.07 | 76.78 | 76.22 |
Measured in miliseconds Collection length: 100000
Data Type | Collection Type | Loop Type | Test 1 | Test 2 | Test 3 | Average |
int | int | For | 928.67 | 989.31 | 934.40 | 950.79 |
int | int | Foreach | 639.67 | 655.97 | 648.20 | 647.95 |
int | int | Foreach-Object | 3739.28 | 3814.27 | 3730.59 | 3761.38 |
int | int | ScriptBlock | 817.40 | 820.79 | 808.26 | 815.49 |
int | int | Filter | 818.23 | 825.11 | 838.87 | 827.41 |
int | List<int> | For | 1210.98 | 1221.01 | 1198.10 | 1210.03 |
int | List<int> | Foreach | 843.85 | 843.32 | 879.60 | 855.59 |
int | List<int> | Foreach-Object | 4280.31 | 4326.06 | 4254.13 | 4286.83 |
int | List<int> | ScriptBlock | 1075.82 | 1105.29 | 1072.61 | 1084.57 |
int | List<int> | Filter | 1060.23 | 1081.08 | 1071.59 | 1070.97 |
int | ArrayList<int> | For | 941.65 | 937.21 | 946.71 | 941.86 |
int | ArrayList<int> | Foreach | 622.43 | 593.67 | 589.76 | 601.95 |
int | ArrayList<int> | Foreach-Object | 3574.23 | 3654.80 | 3646.56 | 3625.19 |
int | ArrayList<int> | ScriptBlock | 765.73 | 818.17 | 776.87 | 786.92 |
int | ArrayList<int> | Filter | 759.23 | 747.40 | 743.89 | 750.17 |
Measured in miliseconds Collection length: 1000000
Data Type | Collection Type | Loop Type | Test 1 | Test 2 | Test 3 | Average |
int | int | For | 9744.00 | 9438.96 | 9758.17 | 9647.04 |
int | int | Foreach | 6766.58 | 6401.43 | 6640.43 | 6602.81 |
int | int | Foreach-Object | 38629.84 | 37763.99 | 38132.47 | 38175.43 |
int | int | ScriptBlock | 8480.02 | 8277.16 | 8329.73 | 8362.30 |
int | int | Filter | 8326.80 | 8178.34 | 8076.98 | 8194.04 |
int | List<int> | For | 12840.89 | 12497.00 | 12669.28 | 12669.06 |
int | List<int> | Foreach | 8913.08 | 8733.64 | 8840.35 | 8829.02 |
int | List<int> | Foreach-Object | 43677.32 | 42700.12 | 43606.07 | 43327.84 |
int | List<int> | ScriptBlock | 11180.41 | 10921.40 | 11042.95 | 11048.25 |
int | List<int> | Filter | 10552.67 | 10176.99 | 10198.66 | 10309.44 |
int | ArrayList<int> | For | 9361.94 | 9696.00 | 9582.81 | 9546.92 |
int | ArrayList<int> | Foreach | 6066.82 | 6069.82 | 6185.74 | 6107.46 |
int | ArrayList<int> | Foreach-Object | 36339.03 | 36905.48 | 37547.94 | 36930.82 |
int | ArrayList<int> | ScriptBlock | 7704.85 | 7682.98 | 7671.94 | 7686.59 |
int | ArrayList<int> | Filter | 7319.12 | 7521.99 | 7445.32 | 7428.81 |
Codes Used to Run the Test
Mesure-Object was sat in each function to measure time. I know It should take out of function if you want to measure CAST cost. Declare Filter:
filter Get-FilterTest{ [decimal]$total += $_ }
Sample of For Loop Test:
function Get-intForTest{ param( $int ) Measure-Command{ for ($i = 0; $i -lt $int.length; $i++) { [decimal]$total += $int[$i] } } }
Sample of Foreach Loop Test:
function Get-intForeachTest{ param( $int ) Measure-Command{ foreach ($i in $int) { [decimal]$total += $i } } }
Sample of Foreach-Object Loop Test:
function Get-intForeachObjectTest{ param( $int ) Measure-Command{ $int | ForEach-Object{ [decimal]$total += $_ } } }
Sample of ScriptBlock Loop Test:
function Get-intScriptBlockTest{ param( $int ) Measure-Command{ $int | &{process{[decimal]$total += $_}} } }
Sample of Filter Loop Test:
function Get-intFilterTest{ param( $int ) Measure-Command{ $int | Get-FilterTest } }
Declare Collection.
[int[]]$array=@() $array += 1..30000000
Run code.
(Get-intForTest -int $array).TotalMilliseconds (Get-intForeachTest -int $array).TotalMilliseconds (Get-intForeachObjectTest -int $array).TotalMilliseconds (Get-intScriptBlockTest -int $array).TotalMilliseconds (Get-intFilterTest -int $array).TotalMilliseconds (Get-intFilterTest -int $array).TotalMilliseconds
Here's whole code, it run 4 time to measure.
filter Get-FilterTest{ [decimal]$total += $_ } function Get-intForTest{ param( $int ) Measure-Command{ for ($i = 0; $i -lt $int.length; $i++) { [decimal]$total += $int[$i] } } } function Get-intForeachTest{ param( $int ) Measure-Command{ foreach ($i in $int) { [decimal]$total += $i } } } function Get-intForeachObjectTest{ param( $int ) Measure-Command{ $int | ForEach-Object{ [decimal]$total += $_ } } } function Get-intScriptBlockTest{ param( $int ) Measure-Command{ $int | &{process{[decimal]$total += $_}} } } function Get-intFilterTest{ param( $int ) Measure-Command{ $int | Get-FilterTest } } function Get-ListForTest{ param( $int ) $list = New-Object 'System.Collections.Generic.List`1[System.String]' $int | foreach-Object { $list.Add($_)} #,$list Measure-Command{ for ($i = 0; $i -lt $int.length; $i++) { [decimal]$total += $list[$i] } } } function Get-ListForeachTest{ param( $int ) $list = New-Object 'System.Collections.Generic.List`1[System.String]' $int | foreach-Object { $list.Add($_)} #,$list Measure-Command{ foreach ($l in $list) { [decimal]$total += $l } } } function Get-ListForeachObjectTest{ param( $int ) $list = New-Object 'System.Collections.Generic.List`1[System.String]' $int | foreach-Object { $list.Add($_)} #,$list Measure-Command{ $list | foreach-Object{ [decimal]$total += $_ } } } function Get-ListScriptBlockTest{ param( $int ) $list = New-Object 'System.Collections.Generic.List`1[System.String]' $int | foreach-Object { $list.Add($_)} #,$list Measure-Command{ $list | &{process{[decimal]$total += $_}} } } function Get-ListFilterTest{ param( $int ) $list = New-Object 'System.Collections.Generic.List`1[System.String]' $int | foreach-Object { $list.Add($_)} #,$list Measure-Command{ $list | Get-FilterTest } } function Get-ArrayListForTest{ param( $int ) $arrayList = New-Object System.Collections.ArrayList [Void]($int | foreach-Object { $arrayList.Add($_) }) #,$Arraylist Measure-Command{ for ($i = 0; $i -lt $int.length; $i++) { [decimal]$total += $arrayList[$i] } } } function Get-ArrayListForeachTest{ param( $int ) $arrayList = New-Object System.Collections.ArrayList [Void]($int | foreach-Object { $arrayList.Add($_) }) #,$Arraylist Measure-Command{ foreach ($al in $arrayList) { [decimal]$total += $al } } } function Get-ArrayListForeachObjectTest{ param( $int ) $arrayList = New-Object System.Collections.ArrayList [Void]($int | foreach-Object { $arrayList.Add($_) }) #,$Arraylist Measure-Command{ $arrayList | Foreach-Object{ [decimal]$total += $_ } } } function Get-ArrayListScriptBlockTest{ param( $int ) $arrayList = New-Object System.Collections.ArrayList [Void]($int | foreach-Object { $arrayList.Add($_) }) #,$Arraylist Measure-Command{ $arrayList | &{process{[decimal]$total += $_}} } } filter Get-ArrayListFilterTest{ param( $int ) $arrayList = New-Object System.Collections.ArrayList [Void]($int | foreach-Object { $arrayList.Add($_) }) #,$Arraylist Measure-Command { $arrayList | Get-FilterTest } } [int[]]$array=@() $array += 1..30000000 1..4 | ForEach-Object { (Get-intForTest -int $array).TotalMilliseconds (Get-intForeachTest -int $array).TotalMilliseconds (Get-intForeachObjectTest -int $array).TotalMilliseconds (Get-intScriptBlockTest -int $array).TotalMilliseconds (Get-intFilterTest -int $array).TotalMilliseconds (Get-ListForTest -int $array).TotalMilliseconds (Get-ListForeachTest -int $array).TotalMilliseconds (Get-ListForeachObjectTest -int $array).TotalMilliseconds (Get-ListScriptBlockTest -int $array).TotalMilliseconds (Get-ListFilterTest -int $array).TotalMilliseconds (Get-ArrayListForTest -int $array).TotalMilliseconds (Get-ArrayListForeachTest -int $array).TotalMilliseconds (Get-ArrayListForeachObjectTest -int $array).TotalMilliseconds (Get-ArrayListScriptBlockTest -int $array).TotalMilliseconds (Get-ArrayListFilterTest -int $array).TotalMilliseconds "" }
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)