Custom Powershell Objects and Performance

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.9131198
Select-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.7721692
Select-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.5090867
Select-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.9198304
Select-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[]]$objects = 1,2,5,10
[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

About Boe Prox

Microsoft Cloud and Datacenter MVP working as a SQL DBA.
This entry was posted in powershell and tagged , , , , . Bookmark the permalink.

5 Responses to Custom Powershell Objects and Performance

  1. davidodaf says:

    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

  2. Pingback: Custom PowerShell Objects and Performance Revisited | Learn Powershell | Achieve More

  3. Pingback: PowerShell eigene Objekte erstellen Custom Objects - PowerShell Get-Script -Name Peter Kriegel | Management

  4. Pingback: Pimp my script, create custom objects | ITshifts.NET

  5. Pingback: Episode 127 – Matt Royer From Intel on vPro PowerShell Support « PowerScripting Podcast

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s