Skip to content

Instantly share code, notes, and snippets.

@zett42
Last active September 4, 2022 19:48
Show Gist options
  • Save zett42/7dd95d4b37d35b00e1155646c2d43fdc to your computer and use it in GitHub Desktop.
Save zett42/7dd95d4b37d35b00e1155646c2d43fdc to your computer and use it in GitHub Desktop.
Test performance of various ways to join arrays of objects on a given property in PowerShell
class MyObject1 {
[string] $Name
[int] $Foo
}
class MyObject2 {
[string] $Name
[int] $Bar
}
# Creates two related arrays of given size.
# 1st array contains objects of class MyObject1 with random $name property.
# 2nd array contains objects of class MyObject2, whose $name properties matches the name property of random elements from 1st array.
Function New-TestData( [int] $size ) {
Write-Host "Creating two arrays of $size objects..."
$array1 = foreach( $i in 0..($size - 1) ) { [MyObject1]@{
#$array1 = foreach( $i in 0..($size -1 ) ) { [PSCustomObject]@{
Name = [Guid]::NewGuid().ToString('n')
Foo = $i
}}
$rnd = [Random]::new()
$array2 = foreach( $i in 0..($size - 1) ) { [MyObject2]@{
#$array2 = foreach( $i in 0..($size - 1) ) { [PSCustomObject]@{
# Get name of random element from $df1
Name = $array1[ $rnd.NextSingle() * ($array1.Count - 1) ].Name
Bar = $i + $size
}}
[PSCustomObject] @{ array1 = $array1; array2 = $array2 }
}
Function Write-TestResultOfRun( $duration, $joinedArray ) {
Write-Host "Run took $('{0:0.##} s' -f $duration.TotalSeconds)"
Write-Host "Joined count: $($joinedArray.Count)"
Write-Host "Some rows of joined objects:`n$($joinedArray[0..2] | Format-Table | Out-String)"
}
$totalRuns = 10
$dataSize = 1e5
$durations = 1..$totalRuns | ForEach-Object {
Write-Host -ForegroundColor Green "----- Run $_ of $totalRuns -----"
Start-Job {
# From https://github.com/iRon7/Join-Object
Import-Module .\iron_join.psm1
# In this gist
Import-Module .\_JoinPerfTestHelper.psm1
$data = New-TestData $using:dataSize
Write-Host 'Joining...'
$duration = Measure-Command {
$joinedArray = Join-Object -LeftObject $data.array1 -RightObject $data.array2 -On Name -JoinType Inner
}
Write-TestResultOfRun $duration $joinedArray
$duration # To be captured in $durations
} | Receive-Job -Wait -AutoRemoveJob
}
"`nAVG: {0:0.##} s" -f ($durations | Measure-Object TotalSeconds -Average).Average
$totalRuns = 10
$dataSize = 1e5
$durations = 1..$totalRuns | ForEach-Object {
Write-Host -ForegroundColor Green "----- Run $_ of $totalRuns -----"
Start-Job {
# From https://github.com/RamblingCookieMonster/PowerShell/blob/master/Join-Object.ps1
Import-Module .\ramblingcookiemonster_Join-Object.psm1
# In this gist
Import-Module .\_JoinPerfTestHelper.psm1
$data = New-TestData $using:dataSize
Write-Host 'Joining...'
$duration = Measure-Command {
$joinedArray = Join-Object -Left $data.array1 -Right $data.array2 -LeftJoinProperty Name -RightJoinProperty Name `
-LeftProperties Name, Foo -RightProperties Bar -Type OnlyIfInBoth
}
Write-TestResultOfRun $duration $joinedArray
$duration # To be captured in $durations
} | Receive-Job -Wait -AutoRemoveJob
}
"`nAVG: {0:0.##} s" -f ($durations | Measure-Object TotalSeconds -Average).Average
Function Join-Enumerable{
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[Collections.IEnumerable] $Left,
[Parameter(Mandatory)]
[Collections.IEnumerable] $Right,
[Parameter(Mandatory)]
$LeftKey,
[Parameter(Mandatory)]
$RightKey,
[Parameter(Mandatory)]
[scriptblock] $Result,
[Parameter()]
[switch] $NoEnumerate
)
if( $RightKey -isnot [scriptblock] ) {
$RightKey = [scriptblock]::Create( "`$args.{$RightKey}" )
}
if( $LeftKey -isnot [scriptblock] ) {
$LeftKey = [scriptblock]::Create( "`$args.{$LeftKey}" )
}
$enumerator = [Linq.Enumerable]::Join( $Left, $Right,
[Func[object, object]] $RightKey,
[Func[object, object]] $LeftKey,
[Func[object, object, object]] $Result )
if( $NoEnumerate ) {
return , $enumerator # Outputs the enumerator itself
}
$enumerator # Enumerates and outputs each result element
}
$totalRuns = 10
$dataSize = 1e5
$durations = 1..$totalRuns | ForEach-Object {
Write-Host -ForegroundColor Green "----- Run $_ of $totalRuns -----"
Start-Job {
# In this gist
Import-Module .\zett42_JoinEnumerable.psm1
# In this gist
Import-Module .\_JoinPerfTestHelper.psm1
$data = New-TestData $using:dataSize
Write-Host 'Joining...'
class MyJoinedObject {
[string] $Name
[int] $Foo
[int] $Bar
}
$duration = Measure-Command {
$joinedArray = Join-Enumerable -Left $data.array1 -Right $data.array2 -LeftKey Name -RightKey Name -Result {
[MyJoinedObject] @{
#[PSCustomObject] @{
Name = $args[0].Name
Foo = $args[0].Foo
Bar = $args[1].Bar
}
}
}
Write-TestResultOfRun $duration $joinedArray
$duration # To be captured in $durations
} | Receive-Job -Wait -AutoRemoveJob
}
"`nAVG: {0:0.##} s" -f ($durations | Measure-Object TotalSeconds -Average).Average
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment