PowerShell betik performansıyla ilgili dikkat edilmesi gerekenler
.NET'ten doğrudan yararlanan ve işlem hattından kaçınan PowerShell betikleri, idiomatic PowerShell'den daha hızlı olma eğilimindedir. Idiomatic PowerShell, genellikle işlem hattından yararlanan ve yalnızca gerektiğinde .NET'e başvuran cmdlet'leri ve PowerShell işlevlerini kullanır.
Not
Burada açıklanan tekniklerin çoğu idiomatic PowerShell değildir ve PowerShell betiğinin okunabilirliğini azaltabilir. Performans aksini gerektirmedikçe betik yazarlarının idiomatic PowerShell kullanması önerilir.
Çıktıyı gizleme
İşlem hattına nesne yazmaktan kaçınmanın birçok yolu vardır.
- Atama veya dosya yeniden yönlendirme
$null
- Atama
[void]
- Boru hattı
Out-Null
öğesine atama $null
, öğesine atama, öğesine atama [void]
ve dosya yeniden yönlendirme $null
hızları neredeyse aynıdır. Ancak, özellikle PowerShell 5.1'de büyük bir döngüde çağrı Out-Null
yapılması önemli ölçüde daha yavaş olabilir.
$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'
}
}
}
Bu testler PowerShell 7.3.4'teki bir Windows 11 makinesinde çalıştırıldı. Sonuçlar aşağıda gösterilmiştir:
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
Süreler ve göreli hızlar donanıma, PowerShell sürümüne ve sistemdeki geçerli iş yüküne bağlı olarak değişebilir.
Dizi ekleme
Öğe listesi oluşturma işlemi genellikle ekleme işlecine sahip bir dizi kullanılarak yapılır:
$results = @()
$results += Get-Something
$results += Get-SomethingElse
$results
Diziler sabit bir boyuta sahip olduğundan dizi ekleme verimsizdir. Diziye her ekleme, hem sol hem de sağ işlenenlerin tüm öğelerini barındıracak kadar büyük yeni bir dizi oluşturur. her iki işlenenin öğeleri yeni diziye kopyalanır. Küçük koleksiyonlar için bu ek yük önemli olmayabilir. Büyük koleksiyonlarda performans olumsuz etkilenebilir.
Birkaç alternatif vardır. Bir diziye gerçekten ihtiyacınız yoksa, bunun yerine yazılan genel bir liste ([List<T>]
):
$results = [System.Collections.Generic.List[object]]::new()
$results.AddRange((Get-Something))
$results.AddRange((Get-SomethingElse))
$results
Dizi ekleme kullanmanın performans etkisi, koleksiyonun boyutu ve sayı eklemeleri ile üstel olarak artar. Bu kod, bir diziye açıkça değer atama işlemini dizi ekleme ve bir nesne üzerinde Add(T)
[List<T>]
yöntemini kullanma ile karşılaştırır. Açık atamayı performans için temel olarak tanımlar.
$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'
}
}
}
Bu testler PowerShell 7.3.4'teki bir Windows 11 makinesinde çalıştırıldı.
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
Büyük koleksiyonlarla çalışırken, dizi ekleme işlemine eklemekten çok daha yavaştır List<T>
.
Nesne [List<T>]
kullanırken, listeyi veya [Int]
gibi [String]
belirli bir türle oluşturmanız gerekir. Listeye farklı türde nesneler eklediğinizde, bunlar belirtilen türe atılır. Belirtilen türe geçirilemiyorsa, yöntemi bir özel durum oluşturur.
$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
Listenin farklı nesne türlerinden oluşan bir koleksiyon olması gerektiğinde, liste türü olarak ile [Object]
oluşturun. Koleksiyonu numaralandırarak içindeki nesnelerin türlerini inceleyebilirsiniz.
$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
Diziye ihtiyacınız varsa, listede yöntemini ToArray()
çağırabilir veya PowerShell'in sizin için dizi oluşturmasına izin vekleyebilirsiniz:
$results = @(
Get-Something
Get-SomethingElse
)
Bu örnekte PowerShell, dizi ifadesinin içinde işlem hattına yazılan sonuçları tutmak için bir [ArrayList]
oluşturur. öğesine atamadan $results
hemen önce PowerShell, öğesini [ArrayList]
öğesine [Object[]]
dönüştürür.
Dize ekleme
Dizeler sabittir. Dizeye yapılan her ekleme, hem sol hem de sağ işlenenlerin içeriğini tutacak kadar büyük yeni bir dize oluşturur ve her iki işlenenin öğelerini de yeni dizeye kopyalar. Küçük dizeler için bu ek yük önemli olmayabilir. Büyük dizeler için bu, performansı ve bellek tüketimini etkileyebilir.
En az iki alternatif vardır:
-join
işleci dizeleri birleştirir- .NET
[StringBuilder]
sınıfı, değiştirilebilir bir dize sağlar
Aşağıdaki örnek, bu üç dize oluşturma yönteminin performansını karşılaştırır.
$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'
}
}
}
Bu testler PowerShell 7.4.2'deki bir Windows 11 makinesinde çalıştırıldı. Çıkış, işlecin en hızlı ve ardından sınıfı olduğunu [StringBuilder]
gösterir-join
.
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
Süreler ve göreli hızlar donanıma, PowerShell sürümüne ve sistemdeki geçerli iş yüküne bağlı olarak değişebilir.
Büyük dosyaları işleme
PowerShell'de bir dosyayı işlemek için idiomatic yöntemi şuna benzer olabilir:
Get-Content $path | Where-Object Length -GT 10
Bu, .NET API'lerini doğrudan kullanmaktan daha yavaş bir büyüklük sırası olabilir. Örneğin, .NET [StreamReader]
sınıfını kullanabilirsiniz:
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()
}
}
öğesini sarmalayan ReadLines
StreamReader
ve okuma işlemini basitleştiren yöntemini [System.IO.File]
de kullanabilirsiniz:
foreach ($line in [System.IO.File]::ReadLines($path)) {
if ($line.Length -gt 10) {
$line
}
}
Büyük koleksiyonlarda özelliğe göre girdileri arama
Bir listeden kimlik ve başka bir listeden e-posta almak için bir ad kullanmak gibi farklı koleksiyonlardaki aynı kaydı tanımlamak için paylaşılan bir özelliğin kullanılması yaygın bir durumdur. İkinci koleksiyonda eşleşen kaydı bulmak için ilk listede yineleme yavaştır. Özellikle, ikinci koleksiyonun yinelenen filtrelemesinin büyük bir yükü vardır.
Biri Kimliği ve Adı, diğeri Ad ve E-posta içeren iki koleksiyon verilip verilse:
$Employees = 1..10000 | ForEach-Object {
[PSCustomObject]@{
Id = $_
Name = "Name$_"
}
}
$Accounts = 2500..7500 | ForEach-Object {
[PSCustomObject]@{
Name = "Name$_"
Email = "Name$_@fabrikam.com"
}
}
Kimlik, Ad ve E-posta özelliklerine sahip nesnelerin listesini döndürmek için bu koleksiyonları uzlaştırmanın olağan yolu şöyle görünebilir:
$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
}
}
Ancak, bu uygulamanın koleksiyondaki her öğe için koleksiyondaki $Accounts
5000 öğenin tümünü bir kez filtrelemesi $Employee
gerekir. Bu tek değerli arama için bile dakikalar sürebilir.
Bunun yerine, anahtar olarak paylaşılan Ad özelliğini ve değer olarak eşleşen hesabı kullanan bir Karma Tablo oluşturabilirsiniz.
$LookupHash = @{}
foreach ($Account in $Accounts) {
$LookupHash[$Account.Name] = $Account
}
Karma tablodaki anahtarları aramak, bir koleksiyonu özellik değerlerine göre filtrelemekten çok daha hızlıdır. PowerShell, koleksiyondaki her öğeyi denetlemek yerine anahtarın tanımlanıp tanımlanmadığını denetleyebilir ve değerini kullanabilir.
$Results = $Employees | ForEach-Object -Process {
$Email = $LookupHash[$_.Name].Email
[pscustomobject]@{
Id = $_.Id
Name = $_.Name
Email = $Email
}
}
Bu çok daha hızlı. Döngü filtresinin tamamlanması dakikalar sürerken karma arama işlemi bir saniyeden kısa sürer.
Write-Host'ı dikkatli kullanma
Komutun Write-Host
yalnızca Başarılı işlem hattına nesne yazmak yerine konak konsoluna biçimlendirilmiş metin yazmanız gerektiğinde kullanılması gerekir.
Write-Host
, powershell.exe
veya powershell_ise.exe
gibi pwsh.exe
belirli konaklara göre daha [Console]::WriteLine()
yavaş bir büyüklük sırası olabilir. Ancak, [Console]::WriteLine()
tüm konaklarda çalışacağı garanti değildir. Ayrıca, kullanılarak [Console]::WriteLine()
yazılan çıkış tarafından Start-Transcript
başlatılan transkriptlere yazılamaz.
JIT derlemesi
PowerShell, yorumlanan bayt kodu için betik kodunu derler. PowerShell 3'te başlayarak, döngüde sürekli yürütülen kodlar için PowerShell, kodu yerel koda derleyerek tam zamanında (JIT) performansı artırabilir.
300'den az yönerge içeren döngüler JIT derlemesi için uygundur. Bundan büyük döngüler derlenemeyecek kadar maliyetlidir. Döngü 16 kez yürütürse betik arka planda JIT ile derlenmiş olur. JIT derlemesi tamamlandığında, yürütme derlenen koda aktarılır.
bir işleve tekrarlanan çağrılardan kaçının
İşlev çağırmak pahalı bir işlem olabilir. Uzun süre çalışan sıkı bir döngüde bir işlevi çağırıyorsanız, döngünün işlevin içinde taşınmasını göz önünde bulundurun.
Aşağıdaki örnekleri değerlendirin:
$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'
}
}
}
Temel döngü örneği, performans için temel çizgidir. İkinci örnek, rastgele sayı oluşturucuyu sıkı bir döngüde çağrılan bir işleve sarmalar. Üçüncü örnek, döngünün işlevinin içine taşınmasını gösterir. İşlev yalnızca bir kez çağrılır, ancak kod yine de aynı miktarda rastgele sayı oluşturur. Her örnek için yürütme sürelerindeki farka dikkat edin.
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
Cmdlet işlem hatlarını sarmalamaktan kaçının
Cmdlet'lerin çoğu ardışık söz dizimi ve işlem olan işlem hattı için uygulanır. Örneğin:
cmdlet1 | cmdlet2 | cmdlet3
Yeni işlem hattını başlatmak pahalı olabilir, bu nedenle bir cmdlet işlem hattını başka bir mevcut işlem hattına sarmalamaktan kaçınmalısınız.
Aşağıdaki örneği inceleyin. Dosya Input.csv
2100 satır içeriyor. komut Export-Csv
işlem hattının ForEach-Object
içine sarmalanır. Döngünün Export-Csv
her yinelemesi için cmdlet çağrılır ForEach-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
Sonraki örnekte, Export-Csv
komut işlem hattının ForEach-Object
dışına taşındı.
Bu durumda, Export-Csv
yalnızca bir kez çağrılır, ancak yine de tarafından ForEach-Object
geçirilen tüm nesneleri işler.
$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
Eşlenmemiş örnek 372 kat daha hızlıdır. Ayrıca, ilk uygulamanın sonraki uygulama için gerekli olmayan Append parametresini gerektirdiğine dikkat edin.
Dinamik olarak yeni nesneler oluşturmak için OrderedDictionary kullanma
Bazı girişlere göre dinamik olarak nesne oluşturmamız gerekebilecek durumlar olabilir. Bu, yeni bir PSObject oluşturmak ve ardından cmdlet'ini kullanarak Add-Member
yeni özellikler eklemek için belki de en yaygın kullanılan yöntemdir. Bu tekniği kullanan küçük koleksiyonların performans maliyeti göz ardı edilebilir ancak büyük koleksiyonlar için çok belirgin hale gelebilir. Bu durumda, önerilen yaklaşım bir kullanmak [OrderedDictionary]
ve ardından tür hızlandırıcısını kullanarak bir PSObject'e[pscustomobject]
dönüştürmektir. Daha fazla bilgi için about_Hash_Tables'nin Sıralı sözlükler oluşturma bölümüne bakın.
değişkeninde $json
aşağıdaki API yanıtının depolandığını varsayalım.
{
"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" ]
]
}
]
}
Şimdi, bu verileri bir CSV'ye aktarmak istediğinizi varsayalım. İlk olarak yeni nesneler oluşturmanız ve cmdlet'ini Add-Member
kullanarak özellikleri ve değerleri eklemeniz gerekir.
$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
}
kullanarak OrderedDictionary
kod şu şekilde çevrilebilir:
$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
}
Her iki durumda $result
da çıkış aynı olacaktır:
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
İkinci yaklaşım, nesne sayısı ve üye özellikleri arttıkça üstel olarak daha verimli hale gelir.
5 özelliğe sahip nesne oluşturmaya yönelik üç tekniğin performans karşılaştırması aşağıdadır:
$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'
}
}
}
Sonuçlar şunlardır:
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
İlgili bağlantılar
PowerShell
Geri Bildirim
https://aka.ms/ContentUserFeedback.
Çok yakında: 2024 boyunca, içerik için geri bildirim mekanizması olarak GitHub Sorunları’nı kullanımdan kaldıracak ve yeni bir geri bildirim sistemiyle değiştireceğiz. Daha fazla bilgi için bkz.Gönderin ve geri bildirimi görüntüleyin