One of the things that I have been seeing are the different ways to compile a report or to create a collection of custom objects in Powershell. In fact, there was a comment on a previous post saying that I should use the V2 style of custom objects instead of what I was using already. Whether it is creating a custom object using New-Object PSObject and then using the Add-Member cmdlet to add more information to the object, or using a hash table to display the data or some other method to accomplish this task such as Select-Object.
This made me think of not only which one provided the fastest results, but which one would give me the type of output that I desired?
James Brundage’s blog does a very nice jobs discussing the Pros and Cons of using Add-Member vs Select-Object.
MOW, Brandon Shell and Karl Prosser tested this before with some interesting results. These guys are a lot smarter than me and showed some nice things including using C# code to create a custom object, which turned out to be crazy fast. I wanted to expand on the testing a little more by adding more objects that were being collected to really see the differences in the time it takes to complete. Now I am still learning C# so I will not be using the C# code that they used in their test.
I am going to be looking at Add-Member, Select-Object and creating a Hash Table to see which one has the best performance and which one gives me the type of output that I am usually looking for, keeping the order of the properties as I have defined at the beginning.
Basically I wrote a script and hard-coded the number of objects and the amount of cycles that each method would be using and then finding out the number of seconds it takes for each method to run. The script I am using will be at the bottom of the post, so feel free to take it and run it on your own machine. Keep in mind that my results will not match what you may get just because of what each machine might have for hardware and the current processes running on your machine vs. mine.
Here are the sample codes for each method:
Add-Member
for($i = 0;$i -lt 50000;$i++) {$a = new-Object psobject
$a | add-member -membertype noteproperty -name test1 -value “test1” }
Select-Object
{for($i = 0;$i -lt 50000;$i++) {$a = 1 |Select-Object test1,test2,test3,test4,test5,test6,test7,test8,test9,test10
$a.test1 = “test1” }
Hash Table
for($i = 0;$i -lt 50000;$i++) { New-Object PSObject -Property @{test1 = “test1” }
}
It took a few minutes to run the script and give me the information I needed.
At first I had it writing verbose so I could see how long it was taking. Then I had the bright idea of creating a collection so it would be easier to see and compare each method based on the number of objects and the amount of cycles.
Results from script:
Name Objects Cycles Seconds—- ——- —— ——-
Add-Member 1 50000 27.9131198
Select-Object 1 50000 19.3800657
HashTable 1 50000 11.0661309
Add-Member 2 50000 45.7721692
Select-Object 2 50000 18.9221812
HashTable 2 50000 11.8333989
Add-Member 5 50000 100.5090867
Select-Object 5 50000 19.3345412
HashTable 5 50000 14.1277553
Add-Member 10 50000 191.9198304
Select-Object 10 50000 20.0706683
HashTable 10 50000 18.2829811
1st run:
Add-Member 1 50000 27.9131198Select-Object 1 50000 19.3800657
HashTable 1 50000 11.0661309
Breaking this down by number of objects, the first run with only 1 object shows that the Add-Member method was the slowest of the bunch followed by Select-Object and finishing with the Hash Table. Everything is within a decent time of each other, so you might think that any of these would be fine to use.
2nd run:
Add-Member 2 50000 45.7721692Select-Object 2 50000 18.9221812
HashTable 2 50000 11.8333989
Using 2 objects begin to show the speed difference between Add-Member and the rest of the methods. Where the Add-Member method was only 8 seconds slow before has now jumped up to being 27 seconds slower just by adding an extra object. Select-Object actually was a little faster than before and the Hash Table was slower by milliseconds.
3rd run:
Add-Member 5 50000 100.5090867Select-Object 5 50000 19.3345412
HashTable 5 50000 14.1277553
Using 5 objects really shows the performance hit that using Add-Member creates. It takes 100 seconds to complete this action whereas the Select-Object is 81 seconds faster and the Hash Table is 86 seconds faster. We are talking almost 5 times faster to complete this.
4th run:
Add-Member 10 50000 191.9198304Select-Object 10 50000 20.0706683
HashTable 10 50000 18.2829811
Using 10 objects in the last run pretty much sums up what has already been shown, that Add-Member is not the way to go if you are looking for performance when creating a custom object. Nearly 200 seconds to work with 10 objects across 50000 cycles. Select-Object comes in seconds on this with a run of 20 seconds and the Hash Table custom object was the quickest at 18.2 seconds.
The winner by a nose in this test was using the Hash Table method to create a custom object. The caveat to this is that using a Hash Table means that you will not always get the property order that you put in. See picture below.
To sum it up, if your looking for speed, use Hash Tables, if your looking for a specific order or reporting with custom objects, use the Select-Object or Add-Member methods. To learn more about the differences and when to use each of these, check out this blog.
I know that there are other ways to accomplish this such as using system.collections.generic.dictionary or System.Collections.ArrayList. But my main focus did not concern these this time.
I wanted to do more with the code and remove the hard-coding of the objects with the switch statement, but I simply ran out of time.
CustomObjectPerformance.ps1
[int]$cycles = 50000
$report = @()
ForEach ($obj in $objects) {
Switch ($obj) {
1 {
$temp = “” | Select Name, Objects,Cycles, Seconds
Write-Host -fore Green “Beginning performance test for Add-Member custom objects using $cycles iterations and $($obj) objects”
$run = $(Measure-Command {for($i = 0;$i -lt $cycles;$i++) {
$a = new-Object psobject
$a | add-member -membertype noteproperty -name test1 -value “test1”
}}).TotalSeconds
Write-Host -ForegroundColor Cyan “$($run) seconds”
$temp.Name = “Add-Member”
$temp.Objects = $obj
$temp.Cycles = $cycles
$temp.seconds = $run
$report += $temp
}
2 {
$temp = “” | Select Name, Objects,Cycles, Seconds
Write-Host -fore Green “Beginning performance test for Add-Member custom objects using $cycles iterations and $($obj) objects”
$run = $(Measure-Command {for($i = 0;$i -lt $cycles;$i++) {
$a = new-Object psobject
$a | add-member -membertype noteproperty -name test1 -value “test1”
$a | add-member -membertype noteproperty -name test2 -value “test2”
}}).TotalSeconds
Write-Host -ForegroundColor Cyan “$($run) seconds”
$temp.Name = “Add-Member”
$temp.Objects = $obj
$temp.Cycles = $cycles
$temp.seconds = $run
$report += $temp
}
5 {
$temp = “” | Select Name, Objects,Cycles, Seconds
Write-Host -fore Green “Beginning performance test for Add-Member custom objects using $cycles iterations and $($obj) objects”
$run = $(Measure-Command {for($i = 0;$i -lt $cycles;$i++) {
$a = new-Object psobject
$a | add-member -membertype noteproperty -name test1 -value “test1”
$a | add-member -membertype noteproperty -name test2 -value “test2”
$a | add-member -membertype noteproperty -name test3 -value “test3”
$a | add-member -membertype noteproperty -name test4 -value “test4”
$a | add-member -membertype noteproperty -name test5 -value “test5”
}}).TotalSeconds
Write-Host -ForegroundColor Cyan “$($run) seconds”
$temp.Name = “Add-Member”
$temp.Objects = $obj
$temp.Cycles = $cycles
$temp.seconds = $run
$report += $temp
}
10 {
$temp = “” | Select Name, Objects,Cycles, Seconds
Write-Host -fore Green “Beginning performance test for Add-Member custom objects using $cycles iterations and $($obj) objects”
$run = $(Measure-Command {for($i = 0;$i -lt $cycles;$i++) {
$a = new-Object psobject
$a | add-member -membertype noteproperty -name test1 -value “test1”
$a | add-member -membertype noteproperty -name test2 -value “test2”
$a | add-member -membertype noteproperty -name test3 -value “test3”
$a | add-member -membertype noteproperty -name test4 -value “test4”
$a | add-member -membertype noteproperty -name test5 -value “test5”
$a | add-member -membertype noteproperty -name test6 -value “test6”
$a | add-member -membertype noteproperty -name test7 -value “test7”
$a | add-member -membertype noteproperty -name test8 -value “test8”
$a | add-member -membertype noteproperty -name test9 -value “test9”
$a | add-member -membertype noteproperty -name test10 -value “test10”
}}).TotalSeconds
Write-Host -ForegroundColor Cyan “$($run) seconds”
$temp.Name = “Add-Member”
$temp.Objects = $obj
$temp.Cycles = $cycles
$temp.seconds = $run
$report += $temp
}
}Switch ($obj) {
1 {
$temp = “” | Select Name, Objects,Cycles, Seconds
Write-Host -fore Green “Beginning performance test for Select-Object custom objects using 50000 iterations and $($obj) objects”
$run = $(Measure-Command {for($i = 0;$i -lt 50000;$i++) {
$a = 1 |Select-Object test1,test2,test3,test4,test5,test6,test7,test8,test9,test10
$a.test1 = “test1”
}}).TotalSeconds
Write-Host -ForegroundColor Cyan “$($run) seconds”
$temp.Name = “Select-Object”
$temp.Objects = $obj
$temp.Cycles = $cycles
$temp.seconds = $run
$report += $temp
}
2 {
$temp = “” | Select Name, Objects,Cycles, Seconds
Write-Host -fore Green “Beginning performance test for Select-Object custom objects using $cycles iterations and $($obj) objects”
$run = $(Measure-Command {for($i = 0;$i -lt $cycles;$i++) {
$a = 1 |Select-Object test1,test2,test3,test4,test5,test6,test7,test8,test9,test10
$a.test1 = “test1”; $a.test2 = “test2”
}}).TotalSeconds
Write-Host -ForegroundColor Cyan “$($run) seconds”
$temp.Name = “Select-Object”
$temp.Objects = $obj
$temp.Cycles = $cycles
$temp.seconds = $run
$report += $temp
}
5 {
$temp = “” | Select Name, Objects,Cycles, Seconds
Write-Host -fore Green “Beginning performance test for Select-Object custom objects using $cycles iterations and $($obj) objects”
$run = $(Measure-Command {for($i = 0;$i -lt $cycles;$i++) {
$a = 1 |Select-Object test1,test2,test3,test4,test5,test6,test7,test8,test9,test10
$a.test1 = “test1”; $a.test2 = “test2”; $a.test3 = “test3”; $a.test4 = “test4”; $a.test5 = “test5”
}}).TotalSeconds
Write-Host -ForegroundColor Cyan “$($run) seconds”
$temp.Name = “Select-Object”
$temp.Objects = $obj
$temp.Cycles = $cycles
$temp.seconds = $run
$report += $temp
}
10 {
$temp = “” | Select Name, Objects,Cycles, Seconds
Write-Host -fore Green “Beginning performance test for Select-Object custom objects using $cycles iterations and $($obj) objects”
$run = $(Measure-Command {for($i = 0;$i -lt $cycles;$i++) {
$a = 1 |Select-Object test1,test2,test3,test4,test5,test6,test7,test8,test9,test10
$a.test1 = “test1”; $a.test2 = “test2”; $a.test3 = “test3”; $a.test4 = “test4”; $a.test5 = “test5”
$a.test6 = “test6”; $a.test7 = “test7”; $a.test8 = “test8”; $a.test9 = “test9”; $a.test10 = “test10”
}}).TotalSeconds
Write-Host -ForegroundColor Cyan “$($run) seconds”
$temp.Name = “Select-Object”
$temp.Objects = $obj
$temp.Cycles = $cycles
$temp.seconds = $run
$report += $temp
}
}Switch ($obj) {
1 {
$temp = “” | Select Name, Objects,Cycles, Seconds
Write-Host -fore Green “Beginning performance test for Hash Table custom objects using $cycles iterations and $($obj) objects”
$run = $(Measure-Command {for($i = 0;$i -lt $cycles;$i++) {
New-Object PSObject -Property @{
test1 = “test1”
}
}}).TotalSeconds
Write-Host -ForegroundColor Cyan “$($run) seconds”
$temp.Name = “HashTable”
$temp.Objects = $obj
$temp.Cycles = $cycles
$temp.seconds = $run
$report += $temp
}
2 {
$temp = “” | Select Name, Objects,Cycles, Seconds
Write-Host -fore Green “Beginning performance test for Hash Table custom objects using $cycles iterations and $($obj) objects”
$run = $(Measure-Command {for($i = 0;$i -lt $cycles;$i++) {
New-Object PSObject -Property @{
test1 = “test1”; test2 = “test2”
}
}}).TotalSeconds
Write-Host -ForegroundColor Cyan “$($run) seconds”
$temp.Name = “HashTable”
$temp.Objects = $obj
$temp.Cycles = $cycles
$temp.seconds = $run
$report += $temp
}
5 {
$temp = “” | Select Name, Objects,Cycles, Seconds
Write-Host -fore Green “Beginning performance test for Hash Table custom objects using $cycles iterations and $($obj) objects”
$run = $(Measure-Command {for($i = 0;$i -lt $cycles;$i++) {
New-Object PSObject -Property @{
test1 = “test1”; test2 = “test2”; test3 = “test3”; test4 = “test4”; test5 = “test5”
}
}}).TotalSeconds
Write-Host -ForegroundColor Cyan “$($run) seconds”
$temp.Name = “HashTable”
$temp.Objects = $obj
$temp.Cycles = $cycles
$temp.seconds = $run
$report += $temp
}
10 {
$temp = “” | Select Name, Objects,Cycles, Seconds
Write-Host -fore Green “Beginning performance test for Hash Table custom objects using $cycles iterations and $($obj) objects”
$run = $(Measure-Command {for($i = 0;$i -lt $cycles;$i++) {
New-Object PSObject -Property @{
test1 = “test1”; test2 = “test2”; test3 = “test3”; test4 = “test4”; test5 = “test5”
test6 = “test6”; test7 = “test7”; test8 = “test8”; test9 = “test9”; test10 = “test10”
}
}}).TotalSeconds
Write-Host -ForegroundColor Cyan “$($run) seconds”
$temp.Name = “HashTable”
$temp.Objects = $obj
$temp.Cycles = $cycles
$temp.seconds = $run
$report += $temp
}
}
}
$report
I was hit with PSObject creation performance problem while reading records from a database table into an array.
For this case I’ve found a faster method that preserves field order:
measure-command { $nRows = 50000; for ($i=0; $i -lt $nRows; $i++) { $record = “” | select field1,field2,field3,field4,field5,field6 } }
Days : 0
Hours : 0
Minutes : 0
Seconds : 5
Milliseconds : 210
Ticks : 52105968
TotalDays : 6,03078333333333E-05
TotalHours : 0,001447388
TotalMinutes : 0,08684328
TotalSeconds : 5,2105968
TotalMilliseconds : 5210,5968
________________________________________________________________________________________________________________________________________________________________________________________
measure-command { $nRows = 50000; $template = “” | select field1,field2,field3,field4,field5,field6; for ($i=0; $i -lt $nRows; $i++) { $record = $template.PSObject.Copy() } }
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 822
Ticks : 8228597
TotalDays : 9,52383912037037E-06
TotalHours : 0,000228572138888889
TotalMinutes : 0,0137143283333333
TotalSeconds : 0,8228597
TotalMilliseconds : 822,8597
Regards, and thanks for your post
Pingback: Custom PowerShell Objects and Performance Revisited | Learn Powershell | Achieve More
Pingback: PowerShell eigene Objekte erstellen Custom Objects - PowerShell Get-Script -Name Peter Kriegel | Management
Pingback: Pimp my script, create custom objects | ITshifts.NET
Pingback: Episode 127 – Matt Royer From Intel on vPro PowerShell Support « PowerScripting Podcast