Pertimbangan performa pembuatan skrip PowerShell
Skrip PowerShell yang memanfaatkan .NET secara langsung dan menghindari alur cenderung lebih cepat daripada PowerShell idiomatik. Idiomatic PowerShell menggunakan cmdlet dan fungsi PowerShell, sering memanfaatkan alur, dan menggunakan ke .NET hanya jika perlu.
Catatan
Banyak teknik yang dijelaskan di sini bukan PowerShell idiomatik dan dapat mengurangi keterbacaan skrip PowerShell. Penulis skrip disarankan untuk menggunakan PowerShell idiomatik kecuali performa menentukan sebaliknya.
Menekan output
Ada banyak cara untuk menghindari penulisan objek ke alur.
- Penugasan atau pengalihan file ke
$null
- Transmisi ke
[void]
- Pipa ke
Out-Null
Kecepatan penugasan ke $null
, transmisi ke [void]
, dan pengalihan file hampir $null
identik. Namun, panggilan Out-Null
dalam perulangan besar bisa secara signifikan lebih lambat, terutama di PowerShell 5.1.
$tests = @{
'Assign to $null' = {
$arrayList = [System.Collections.ArrayList]::new()
foreach ($i in 0..$args[0]) {
$null = $arraylist.Add($i)
}
}
'Cast to [void]' = {
$arrayList = [System.Collections.ArrayList]::new()
foreach ($i in 0..$args[0]) {
[void] $arraylist.Add($i)
}
}
'Redirect to $null' = {
$arrayList = [System.Collections.ArrayList]::new()
foreach ($i in 0..$args[0]) {
$arraylist.Add($i) > $null
}
}
'Pipe to Out-Null' = {
$arrayList = [System.Collections.ArrayList]::new()
foreach ($i in 0..$args[0]) {
$arraylist.Add($i) | Out-Null
}
}
}
10kb, 50kb, 100kb | ForEach-Object {
$groupResult = foreach ($test in $tests.GetEnumerator()) {
$ms = (Measure-Command { & $test.Value $_ }).TotalMilliseconds
[pscustomobject]@{
Iterations = $_
Test = $test.Key
TotalMilliseconds = [math]::Round($ms, 2)
}
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
}
$groupResult = $groupResult | Sort-Object TotalMilliseconds
$groupResult | Select-Object *, @{
Name = 'RelativeSpeed'
Expression = {
$relativeSpeed = $_.TotalMilliseconds / $groupResult[0].TotalMilliseconds
[math]::Round($relativeSpeed, 2).ToString() + 'x'
}
}
}
Pengujian ini dijalankan pada komputer Windows 11 di PowerShell 7.3.4. Hasilnya ditunjukkan di bawah ini:
Iterations Test TotalMilliseconds RelativeSpeed
---------- ---- ----------------- -------------
10240 Assign to $null 36.74 1x
10240 Redirect to $null 55.84 1.52x
10240 Cast to [void] 62.96 1.71x
10240 Pipe to Out-Null 81.65 2.22x
51200 Assign to $null 193.92 1x
51200 Cast to [void] 200.77 1.04x
51200 Redirect to $null 219.69 1.13x
51200 Pipe to Out-Null 329.62 1.7x
102400 Redirect to $null 386.08 1x
102400 Assign to $null 392.13 1.02x
102400 Cast to [void] 405.24 1.05x
102400 Pipe to Out-Null 572.94 1.48x
Waktu dan kecepatan relatif dapat bervariasi tergantung pada perangkat keras, versi PowerShell, dan beban kerja saat ini pada sistem.
Penambahan array
Membuat daftar item sering dilakukan menggunakan array dengan operator penambahan:
$results = @()
$results += Get-Something
$results += Get-SomethingElse
$results
Penambahan array tidak efisien karena array memiliki ukuran tetap. Setiap tambahan ke array membuat array baru yang cukup besar untuk menahan semua elemen operand kiri dan kanan. Elemen kedua operand disalin ke dalam array baru. Untuk koleksi kecil, overhead ini mungkin tidak masalah. Performa dapat menderita untuk koleksi besar.
Ada beberapa alternatif. Jika Anda tidak benar-benar memerlukan array, pertimbangkan untuk menggunakan daftar generik yang ditik ([List<T>]
):
$results = [System.Collections.Generic.List[object]]::new()
$results.AddRange((Get-Something))
$results.AddRange((Get-SomethingElse))
$results
Dampak performa penggunaan penambahan array tumbuh secara eksponensial dengan ukuran koleksi dan penambahan angka. Kode ini membandingkan secara eksplisit menetapkan nilai ke array dengan menggunakan penambahan array dan menggunakan Add(T)
metode pada [List<T>]
objek. Ini mendefinisikan penetapan eksplisit sebagai garis besar untuk performa.
$tests = @{
'PowerShell Explicit Assignment' = {
param($count)
$result = foreach($i in 1..$count) {
$i
}
}
'.Add(T) to List<T>' = {
param($count)
$result = [Collections.Generic.List[int]]::new()
foreach($i in 1..$count) {
$result.Add($i)
}
}
'+= Operator to Array' = {
param($count)
$result = @()
foreach($i in 1..$count) {
$result += $i
}
}
}
5kb, 10kb, 100kb | ForEach-Object {
$groupResult = foreach($test in $tests.GetEnumerator()) {
$ms = (Measure-Command { & $test.Value -Count $_ }).TotalMilliseconds
[pscustomobject]@{
CollectionSize = $_
Test = $test.Key
TotalMilliseconds = [math]::Round($ms, 2)
}
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
}
$groupResult = $groupResult | Sort-Object TotalMilliseconds
$groupResult | Select-Object *, @{
Name = 'RelativeSpeed'
Expression = {
$relativeSpeed = $_.TotalMilliseconds / $groupResult[0].TotalMilliseconds
[math]::Round($relativeSpeed, 2).ToString() + 'x'
}
}
}
Pengujian ini dijalankan pada komputer Windows 11 di PowerShell 7.3.4.
CollectionSize Test TotalMilliseconds RelativeSpeed
-------------- ---- ----------------- -------------
5120 PowerShell Explicit Assignment 26.65 1x
5120 .Add(T) to List<T> 110.98 4.16x
5120 += Operator to Array 402.91 15.12x
10240 PowerShell Explicit Assignment 0.49 1x
10240 .Add(T) to List<T> 137.67 280.96x
10240 += Operator to Array 1678.13 3424.76x
102400 PowerShell Explicit Assignment 11.18 1x
102400 .Add(T) to List<T> 1384.03 123.8x
102400 += Operator to Array 201991.06 18067.18x
Saat Anda bekerja dengan koleksi besar, penambahan array secara dramatis lebih lambat daripada menambahkan ke List<T>
.
Saat menggunakan [List<T>]
objek, Anda perlu membuat daftar dengan jenis tertentu, seperti [String]
atau [Int]
. Saat Anda menambahkan objek dari jenis yang berbeda ke daftar, objek tersebut ditransmisikan ke jenis yang ditentukan. Jika mereka tidak dapat ditransmisikan ke jenis yang ditentukan, metode akan menimbulkan pengecualian.
$intList = [System.Collections.Generic.List[int]]::new()
$intList.Add(1)
$intList.Add('2')
$intList.Add(3.0)
$intList.Add('Four')
$intList
MethodException:
Line |
5 | $intList.Add('Four')
| ~~~~~~~~~~~~~~~~~~~~
| Cannot convert argument "item", with value: "Four", for "Add" to type
"System.Int32": "Cannot convert value "Four" to type "System.Int32".
Error: "The input string 'Four' was not in a correct format.""
1
2
3
Saat Anda memerlukan daftar untuk menjadi kumpulan berbagai jenis objek, buat dengan [Object]
sebagai jenis daftar. Anda dapat menghitung koleksi memeriksa jenis objek di dalamnya.
$objectList = [System.Collections.Generic.List[object]]::new()
$objectList.Add(1)
$objectList.Add('2')
$objectList.Add(3.0)
$objectList | ForEach-Object { "$_ is $($_.GetType())" }
1 is int
2 is string
3 is double
Jika Anda memerlukan array, Anda dapat memanggil ToArray()
metode pada daftar atau Anda dapat membiarkan PowerShell membuat array untuk Anda:
$results = @(
Get-Something
Get-SomethingElse
)
Dalam contoh ini, PowerShell membuat untuk menahan hasil yang [ArrayList]
ditulis ke alur di dalam ekspresi array. Tepat sebelum menetapkan ke $results
, PowerShell mengonversi ke [ArrayList]
[Object[]]
.
Penambahan string
String tidak dapat diubah. Setiap penambahan pada string benar-benar membuat string baru yang cukup besar untuk menahan konten operand kiri dan kanan, lalu menyalin elemen kedua operand ke dalam string baru. Untuk string kecil, overhead ini mungkin tidak masalah. Untuk string besar, ini dapat memengaruhi performa dan konsumsi memori.
Setidaknya ada dua alternatif:
- Operator
-join
menggabungkan string - Kelas .NET
[StringBuilder]
menyediakan string yang dapat diubah
Contoh berikut membandingkan performa ketiga metode membangun string ini.
$tests = @{
'StringBuilder' = {
$sb = [System.Text.StringBuilder]::new()
foreach ($i in 0..$args[0]) {
$sb = $sb.AppendLine("Iteration $i")
}
$sb.ToString()
}
'Join operator' = {
$string = @(
foreach ($i in 0..$args[0]) {
"Iteration $i"
}
) -join "`n"
$string
}
'Addition Assignment +=' = {
$string = ''
foreach ($i in 0..$args[0]) {
$string += "Iteration $i`n"
}
$string
}
}
10kb, 50kb, 100kb | ForEach-Object {
$groupResult = foreach ($test in $tests.GetEnumerator()) {
$ms = (Measure-Command { & $test.Value $_ }).TotalMilliseconds
[pscustomobject]@{
Iterations = $_
Test = $test.Key
TotalMilliseconds = [math]::Round($ms, 2)
}
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
}
$groupResult = $groupResult | Sort-Object TotalMilliseconds
$groupResult | Select-Object *, @{
Name = 'RelativeSpeed'
Expression = {
$relativeSpeed = $_.TotalMilliseconds / $groupResult[0].TotalMilliseconds
[math]::Round($relativeSpeed, 2).ToString() + 'x'
}
}
}
Pengujian ini dijalankan pada komputer Windows 11 di PowerShell 7.4.2. Output menunjukkan bahwa -join
operator adalah yang tercepat, diikuti oleh [StringBuilder]
kelas .
Iterations Test TotalMilliseconds RelativeSpeed
---------- ---- ----------------- -------------
10240 Join operator 14.75 1x
10240 StringBuilder 62.44 4.23x
10240 Addition Assignment += 619.64 42.01x
51200 Join operator 43.15 1x
51200 StringBuilder 304.32 7.05x
51200 Addition Assignment += 14225.13 329.67x
102400 Join operator 85.62 1x
102400 StringBuilder 499.12 5.83x
102400 Addition Assignment += 67640.79 790.01x
Waktu dan kecepatan relatif dapat bervariasi tergantung pada perangkat keras, versi PowerShell, dan beban kerja saat ini pada sistem.
Memproses file besar
Cara idiomatik untuk memproses file di PowerShell mungkin terlihat seperti:
Get-Content $path | Where-Object Length -GT 10
Ini bisa menjadi urutan besarnya lebih lambat daripada menggunakan API .NET secara langsung. Misalnya, Anda dapat menggunakan kelas .NET [StreamReader]
:
try {
$reader = [System.IO.StreamReader]::new($path)
while (-not $reader.EndOfStream) {
$line = $reader.ReadLine()
if ($line.Length -gt 10) {
$line
}
}
}
finally {
if ($reader) {
$reader.Dispose()
}
}
Anda juga dapat menggunakan ReadLines
metode [System.IO.File]
, yang membungkus StreamReader
, menyederhanakan proses membaca:
foreach ($line in [System.IO.File]::ReadLines($path)) {
if ($line.Length -gt 10) {
$line
}
}
Mencari entri menurut properti dalam koleksi besar
Biasanya perlu menggunakan properti bersama untuk mengidentifikasi rekaman yang sama dalam koleksi yang berbeda, seperti menggunakan nama untuk mengambil ID dari satu daftar dan email dari daftar lain. Iterasi di atas daftar pertama untuk menemukan rekaman yang cocok di koleksi kedua lambat. Secara khusus, pemfilteran berulang koleksi kedua memiliki overhead besar.
Mengingat dua koleksi, satu dengan ID dan Nama, yang lain dengan Nama dan Email:
$Employees = 1..10000 | ForEach-Object {
[PSCustomObject]@{
Id = $_
Name = "Name$_"
}
}
$Accounts = 2500..7500 | ForEach-Object {
[PSCustomObject]@{
Name = "Name$_"
Email = "Name$_@fabrikam.com"
}
}
Cara biasa untuk mendamaikan koleksi ini untuk mengembalikan daftar objek dengan properti ID, Nama, dan Email mungkin terlihat seperti ini:
$Results = $Employees | ForEach-Object -Process {
$Employee = $_
$Account = $Accounts | Where-Object -FilterScript {
$_.Name -eq $Employee.Name
}
[pscustomobject]@{
Id = $Employee.Id
Name = $Employee.Name
Email = $Account.Email
}
}
Namun, implementasi tersebut harus memfilter semua 5000 item dalam $Accounts
koleksi sekali untuk setiap item dalam $Employee
koleksi. Itu bisa memakan waktu beberapa menit, bahkan untuk pencarian nilai tunggal ini.
Sebagai gantinya, Anda dapat membuat Tabel Hash yang menggunakan properti Nama bersama sebagai kunci dan akun yang cocok sebagai nilai.
$LookupHash = @{}
foreach ($Account in $Accounts) {
$LookupHash[$Account.Name] = $Account
}
Mencari kunci dalam tabel hash jauh lebih cepat daripada memfilter koleksi menurut nilai properti. Alih-alih memeriksa setiap item dalam koleksi, PowerShell dapat memeriksa apakah kunci ditentukan dan menggunakan nilainya.
$Results = $Employees | ForEach-Object -Process {
$Email = $LookupHash[$_.Name].Email
[pscustomobject]@{
Id = $_.Id
Name = $_.Name
Email = $Email
}
}
Ini jauh lebih cepat. Sementara filter perulangan membutuhkan waktu beberapa menit untuk diselesaikan, pencarian hash membutuhkan waktu kurang dari satu detik.
Gunakan Write-Host dengan hati-hati
Perintah Write-Host
hanya boleh digunakan ketika Anda perlu menulis teks yang diformat ke konsol host, daripada menulis objek ke alur Sukses .
Write-Host
bisa menjadi urutan besaran yang lebih lambat daripada [Console]::WriteLine()
untuk host tertentu seperti pwsh.exe
, , powershell.exe
atau powershell_ise.exe
. Namun, [Console]::WriteLine()
tidak dijamin berfungsi di semua host. Selain itu, output yang ditulis menggunakan [Console]::WriteLine()
tidak ditulis ke transkrip yang dimulai oleh Start-Transcript
.
Kompilasi JIT
PowerShell mengkompilasi kode skrip ke bytecode yang ditafsirkan. Dimulai di PowerShell 3, untuk kode yang berulang kali dijalankan dalam perulangan, PowerShell dapat meningkatkan performa dengan Just-in-time (JIT) yang mengkompilasi kode ke dalam kode asli.
Perulangan yang memiliki kurang dari 300 instruksi memenuhi syarat untuk kompilasi JIT. Perulangan yang lebih besar dari itu terlalu mahal untuk dikompilasi. Ketika perulangan telah dijalankan 16 kali, skrip dikompilasi JIT di latar belakang. Ketika kompilasi JIT selesai, eksekusi ditransfer ke kode yang dikompilasi.
Hindari panggilan berulang ke fungsi
Memanggil fungsi bisa menjadi operasi yang mahal. Jika Anda memanggil fungsi dalam perulangan ketat yang berjalan lama, pertimbangkan untuk memindahkan perulangan di dalam fungsi.
Perhatikan contoh berikut:
$tests = @{
'Simple for-loop' = {
param([int] $RepeatCount, [random] $RanGen)
for ($i = 0; $i -lt $RepeatCount; $i++) {
$null = $RanGen.Next()
}
}
'Wrapped in a function' = {
param([int] $RepeatCount, [random] $RanGen)
function Get-RandomNumberCore {
param ($rng)
$rng.Next()
}
for ($i = 0; $i -lt $RepeatCount; $i++) {
$null = Get-RandomNumberCore -rng $RanGen
}
}
'for-loop in a function' = {
param([int] $RepeatCount, [random] $RanGen)
function Get-RandomNumberAll {
param ($rng, $count)
for ($i = 0; $i -lt $count; $i++) {
$null = $rng.Next()
}
}
Get-RandomNumberAll -rng $RanGen -count $RepeatCount
}
}
5kb, 10kb, 100kb | ForEach-Object {
$rng = [random]::new()
$groupResult = foreach ($test in $tests.GetEnumerator()) {
$ms = Measure-Command { & $test.Value -RepeatCount $_ -RanGen $rng }
[pscustomobject]@{
CollectionSize = $_
Test = $test.Key
TotalMilliseconds = [math]::Round($ms.TotalMilliseconds,2)
}
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
}
$groupResult = $groupResult | Sort-Object TotalMilliseconds
$groupResult | Select-Object *, @{
Name = 'RelativeSpeed'
Expression = {
$relativeSpeed = $_.TotalMilliseconds / $groupResult[0].TotalMilliseconds
[math]::Round($relativeSpeed, 2).ToString() + 'x'
}
}
}
Contoh dasar untuk perulangan adalah garis dasar untuk performa. Contoh kedua membungkus generator angka acak dalam fungsi yang disebut dalam perulangan yang ketat. Contoh ketiga memindahkan perulangan di dalam fungsi. Fungsi ini hanya dipanggil sekali tetapi kode masih menghasilkan jumlah angka acak yang sama. Perhatikan perbedaan waktu eksekusi untuk setiap contoh.
CollectionSize Test TotalMilliseconds RelativeSpeed
-------------- ---- ----------------- -------------
5120 for-loop in a function 9.62 1x
5120 Simple for-loop 10.55 1.1x
5120 Wrapped in a function 62.39 6.49x
10240 Simple for-loop 17.79 1x
10240 for-loop in a function 18.48 1.04x
10240 Wrapped in a function 127.39 7.16x
102400 for-loop in a function 179.19 1x
102400 Simple for-loop 181.58 1.01x
102400 Wrapped in a function 1155.57 6.45x
Hindari membungkus alur cmdlet
Sebagian besar cmdlet diimplementasikan untuk alur, yang merupakan sintaks dan proses berurutan. Contohnya:
cmdlet1 | cmdlet2 | cmdlet3
Menginisialisasi alur baru bisa mahal, oleh karena itu Anda harus menghindari pembungkusan alur cmdlet ke dalam alur lain yang ada.
Pertimbangkan contoh berikut. File Input.csv
berisi 2100 baris. Export-Csv
Perintah dibungkus di ForEach-Object
dalam alur. Export-Csv
Cmdlet dipanggil untuk setiap iterasi perulanganForEach-Object
.
$measure = Measure-Command -Expression {
Import-Csv .\Input.csv | ForEach-Object -Begin { $Id = 1 } -Process {
[PSCustomObject]@{
Id = $Id
Name = $_.opened_by
} | Export-Csv .\Output1.csv -Append
}
}
'Wrapped = {0:N2} ms' -f $measure.TotalMilliseconds
Wrapped = 15,968.78 ms
Untuk contoh berikutnya, Export-Csv
perintah dipindahkan ke luar ForEach-Object
alur.
Dalam hal ini, Export-Csv
hanya dipanggil sekali, tetapi masih memproses semua objek yang dilewatkan dari ForEach-Object
.
$measure = Measure-Command -Expression {
Import-Csv .\Input.csv | ForEach-Object -Begin { $Id = 2 } -Process {
[PSCustomObject]@{
Id = $Id
Name = $_.opened_by
}
} | Export-Csv .\Output2.csv
}
'Unwrapped = {0:N2} ms' -f $measure.TotalMilliseconds
Unwrapped = 42.92 ms
Contoh yang tidak dibungkus adalah 372 kali lebih cepat. Selain itu, perhatikan bahwa implementasi pertama memerlukan parameter Tambahkan , yang tidak diperlukan untuk implementasi nanti.
Menggunakan OrderedDictionary untuk membuat objek baru secara dinamis
Ada situasi di mana kita mungkin perlu membuat objek secara dinamis berdasarkan beberapa input, cara yang mungkin paling umum digunakan untuk membuat PSObject baru dan kemudian menambahkan properti baru menggunakan Add-Member
cmdlet. Biaya performa untuk koleksi kecil menggunakan teknik ini mungkin dapat diabaikan namun dapat menjadi sangat terlihat untuk koleksi besar. Dalam hal ini, pendekatan yang direkomendasikan adalah menggunakan [OrderedDictionary]
dan kemudian mengonversinya ke PSObject menggunakan [pscustomobject]
akselerator jenis. Untuk informasi selengkapnya, lihat bagian Membuat kamus yang diurutkan dari about_Hash_Tables.
Asumsikan Anda memiliki respons API berikut yang disimpan dalam variabel $json
.
{
"tables": [
{
"name": "PrimaryResult",
"columns": [
{ "name": "Type", "type": "string" },
{ "name": "TenantId", "type": "string" },
{ "name": "count_", "type": "long" }
],
"rows": [
[ "Usage", "63613592-b6f7-4c3d-a390-22ba13102111", "1" ],
[ "Usage", "d436f322-a9f4-4aad-9a7d-271fbf66001c", "1" ],
[ "BillingFact", "63613592-b6f7-4c3d-a390-22ba13102111", "1" ],
[ "BillingFact", "d436f322-a9f4-4aad-9a7d-271fbf66001c", "1" ],
[ "Operation", "63613592-b6f7-4c3d-a390-22ba13102111", "7" ],
[ "Operation", "d436f322-a9f4-4aad-9a7d-271fbf66001c", "5" ]
]
}
]
}
Sekarang, misalkan Anda ingin mengekspor data ini ke CSV. Pertama, Anda perlu membuat objek baru dan menambahkan properti dan nilai menggunakan Add-Member
cmdlet.
$data = $json | ConvertFrom-Json
$columns = $data.tables.columns
$result = foreach ($row in $data.tables.rows) {
$obj = [psobject]::new()
$index = 0
foreach ($column in $columns) {
$obj | Add-Member -MemberType NoteProperty -Name $column.name -Value $row[$index++]
}
$obj
}
OrderedDictionary
Menggunakan , kode dapat diterjemahkan ke:
$data = $json | ConvertFrom-Json
$columns = $data.tables.columns
$result = foreach ($row in $data.tables.rows) {
$obj = [ordered]@{}
$index = 0
foreach ($column in $columns) {
$obj[$column.name] = $row[$index++]
}
[pscustomobject] $obj
}
Dalam kedua kasus $result
, output akan sama:
Type TenantId count_
---- -------- ------
Usage 63613592-b6f7-4c3d-a390-22ba13102111 1
Usage d436f322-a9f4-4aad-9a7d-271fbf66001c 1
BillingFact 63613592-b6f7-4c3d-a390-22ba13102111 1
BillingFact d436f322-a9f4-4aad-9a7d-271fbf66001c 1
Operation 63613592-b6f7-4c3d-a390-22ba13102111 7
Operation d436f322-a9f4-4aad-9a7d-271fbf66001c 5
Pendekatan terakhir menjadi secara eksponensial lebih efisien saat jumlah objek dan properti anggota meningkat.
Berikut adalah perbandingan performa tiga teknik untuk membuat objek dengan 5 properti:
$tests = @{
'[ordered] into [pscustomobject] cast' = {
param([int] $iterations, [string[]] $props)
foreach ($i in 1..$iterations) {
$obj = [ordered]@{}
foreach ($prop in $props) {
$obj[$prop] = $i
}
[pscustomobject] $obj
}
}
'Add-Member' = {
param([int] $iterations, [string[]] $props)
foreach ($i in 1..$iterations) {
$obj = [psobject]::new()
foreach ($prop in $props) {
$obj | Add-Member -MemberType NoteProperty -Name $prop -Value $i
}
$obj
}
}
'PSObject.Properties.Add' = {
param([int] $iterations, [string[]] $props)
# this is how, behind the scenes, `Add-Member` attaches
# new properties to our PSObject.
# Worth having it here for performance comparison
foreach ($i in 1..$iterations) {
$obj = [psobject]::new()
foreach ($prop in $props) {
$obj.PSObject.Properties.Add(
[psnoteproperty]::new($prop, $i))
}
$obj
}
}
}
$properties = 'Prop1', 'Prop2', 'Prop3', 'Prop4', 'Prop5'
1kb, 10kb, 100kb | ForEach-Object {
$groupResult = foreach ($test in $tests.GetEnumerator()) {
$ms = Measure-Command { & $test.Value -iterations $_ -props $properties }
[pscustomobject]@{
Iterations = $_
Test = $test.Key
TotalMilliseconds = [math]::Round($ms.TotalMilliseconds, 2)
}
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
}
$groupResult = $groupResult | Sort-Object TotalMilliseconds
$groupResult | Select-Object *, @{
Name = 'RelativeSpeed'
Expression = {
$relativeSpeed = $_.TotalMilliseconds / $groupResult[0].TotalMilliseconds
[math]::Round($relativeSpeed, 2).ToString() + 'x'
}
}
}
Dan ini adalah hasilnya:
Iterations Test TotalMilliseconds RelativeSpeed
---------- ---- ----------------- -------------
1024 [ordered] into [pscustomobject] cast 22.00 1x
1024 PSObject.Properties.Add 153.17 6.96x
1024 Add-Member 261.96 11.91x
10240 [ordered] into [pscustomobject] cast 65.24 1x
10240 PSObject.Properties.Add 1293.07 19.82x
10240 Add-Member 2203.03 33.77x
102400 [ordered] into [pscustomobject] cast 639.83 1x
102400 PSObject.Properties.Add 13914.67 21.75x
102400 Add-Member 23496.08 36.72x