Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
.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.
-
$null'e atama veya dosya yönlendirmesi -
[void]'a dönüştürme - boruya
Out-Null
$null'a atama, [void]'a dönüştürme ve $null'ye dosya yönlendirme hızları neredeyse aynıdır. Ancak, özellikle PowerShell 5.1'de büyük bir döngüde Out-Null çağrı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
Not
PowerShell 7.5'te dizi ekleme iyileştirildi ve artık her işlem için yeni bir dizi oluşturmıyordu. Burada açıklanan performans konuları, 7.5 öncesi PowerShell sürümleri için de geçerlidir. Daha fazla bilgi için bkz. PowerShell 7.5'teki Yenilikler.
Diziler sabit bir boyuta sahip olduğundan dizi ekleme verimsizdir. Diziye her ekleme, sol ve 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. Diziye gerçekten ihtiyacınız yoksa, bunun yerine türlendirilmiş genel bir liste ([List<T>]) kullanmayı düşünün.
$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, dizi ekleme ve Add(T) nesnesinde [List<T>] yöntemini kullanarak bir diziye açıkça değer atamayı 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şlemi List<T>eklemekten çok daha yavaştır.
[List<T>] nesnesi kullanırken, listeyi [string] veya [int]gibi 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 dönüştürülemiyorsa, yöntem 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 [Object] ile 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, listeden ToArray() yöntemini ç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çindeki işlem hattına yazılan sonuçları tutmak için bir [ArrayList] oluşturur.
$results atamasından hemen önce, PowerShell [ArrayList] öğesini bir [Object[]]'ye dönüştürür.
Tür açısından güvenli koleksiyonlar
PowerShell, kodlamayı kolaylaştıran ancak performans üzerindeki etkileri olabilecek, gevşek bir şekilde yazılan bir dildir. Tür güvenli (veya türe özgü) koleksiyonları kullanmayı göz önünde bulundurun. Tür açısından güvenli koleksiyonlar daha az bellek kullanır ve daha hızlıdır. Aşağıdaki örnekleri karşılaştırın:
$Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
$ListInt = [System.Collections.Generic.List[int]]::new()
for ($i = 0; $i -lt 1mb; $i++) {
$ListInt.Add($i)
}
$Stopwatch.Stop()
Write-Host "Time to add 1mb integers to List[int]: $($Stopwatch.Elapsed.TotalSeconds) seconds."
Time to add 1mb integers to List[int]: 9.8841501 seconds.
[int] listesi oluşturmak, [Object] listesi oluşturmaktan daha hızlıdır.
$Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
$ListObject = [System.Collections.Generic.List[Object]]::new()
for ($i = 0; $i -lt 1mb; $i++) {
$ListObject.Add($i)
}
$Stopwatch.Stop()
Write-Host "Time to add 1mb integers to List[Object]: $($Stopwatch.Elapsed.TotalSeconds) seconds."
Time to add 1mb integers to List[Object]: 10.5677782 seconds.
Dize ekleme
Dizeler değiştirilemezdir. 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:
-
-joiniş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ış, -join işlecinin en hızlı olduğunu ve ardından [StringBuilder] sınıfı olduğunu gösterir.
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 yerleşik bir yöntem şöyle görünebilir:
Get-Content $path | Where-Object Length -GT 10
.NET API'lerini doğrudan kullanmaktan kat kat daha yavaş 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()
}
}
Ayrıca, ReadLines'yi saran ve okuma işlemini basitleştiren [System.IO.File]StreamReader yöntemini 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. İkinci koleksiyonun tekrarlı filtrelenmesi özellikle büyük bir ek yük oluşturur.
Biri Kimliği ve Adı, diğeri Ad ve E-postaolan iki koleksiyon verilir:
$Employees = 1..10000 | ForEach-Object {
[pscustomobject]@{
Id = $_
Name = "Name$_"
}
}
$Accounts = 2500..7500 | ForEach-Object {
[pscustomobject]@{
Name = "Name$_"
Email = "Name$_@fabrikam.com"
}
}
Kimliği, Adve E-posta özellikleriyle nesnelerin listesini döndürmek için bu koleksiyonları uzlaştırmanın olağan yolu şu şekilde 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 $Accounts koleksiyonundaki her öğe için $Employee koleksiyonundaki 5000 öğenin tümünü bir kez filtrelemesi gerekir. Bu yalnızca tek bir değer için yapılan arama bile dakikalar sürebilir.
Bunun yerine, paylaşılan Adı özelliğini anahtar ve eşleşen hesabı değer olarak kullanan bir Karma Tablo yapabilirsiniz.
$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 bir saniyeden kısa sürede tamamlanır.
Write-Host dikkatle kullan
Write-Host komutu, Success işlem hattına nesne yazmak yerine yalnızca konak konsoluna biçimlendirilmiş metin yazmanız gerektiğinde kullanılmalıdır.
Write-Host için [Console]::WriteLine() ile karşılaştırıldığında, pwsh.exe, powershell.exe veya powershell_ise.exe gibi belirli konaklar için büyüklük derecesinde daha yavaş olabilir. Ancak [Console]::WriteLine() tüm konaklarda çalışacağı garanti değildir. Ayrıca, [Console]::WriteLine() kullanılarak yazılan çıkış, Start-Transcripttarafından 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üldüğünde, betik arka planda JIT olarak derlenir. 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 yoğun bir döngüde bir işlevi çağırıyorsanız, döngüyü işlevin içine taşımayı düşünün.
Aşağıdaki örnekleri göz önünde bulundurun:
$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 for döngüsü örneği, performans için bir referans noktasıdır. İ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, sıralı sentaks ve işlem süreci olan boru hattı için uygulanmaktadır. Mesela:
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 göz önünde bulundurun.
Input.csv dosyası 2100 satır içerir.
Export-Csv komutu ForEach-Object işlem hattının içinde yer alır.
Export-Csv döngüsünün her yinelemesi için ForEach-Object cmdlet'i çağrılır.
$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 komutu ForEach-Object işlem hattının dışına taşındı.
Bu durumda, Export-Csv yalnızca bir kez çağrılır, ancak yine de ForEach-Object 'den 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
Açılmış örnek 372 kat daha hızlıdır. Ayrıca, ilk uygulamanın sonraki uygulama için gerekli olmayan Ekleme parametresini gerektirdiğine dikkat edin.
Gereksiz koleksiyon numaralandırmasından kaçının
PowerShell karşılaştırma işleçleri, koleksiyonları karşılaştırırken bir kolaylık özelliğine sahiptir. İfadedeki soldaki değer bir koleksiyon olduğunda işleç, koleksiyonun ifadenin sağındaki değerle eşleşen öğelerini döndürür.
Bu özellik, bir koleksiyonu filtrelemek için basit bir yol sağlar. Mesela:
PS> $Collection = 1..99
PS> ($Collection -like '*1*') -join ' '
1 10 11 12 13 14 15 16 17 18 19 21 31 41 51 61 71 81 91
Ancak, yalnızca boole sonucu bekleyen bir koşullu deyimde koleksiyon karşılaştırması kullandığınızda, bu özellik düşük performansa neden olabilir.
Örneğin:
if ($Collection -like '*1*') { 'Found' }
Bu örnekte PowerShell, sağ taraftaki değeri koleksiyondaki her değerle karşılaştırır ve bir sonuç koleksiyonu döndürür. Sonuç boş olmadığından null olmayan sonuç olarak $truedeğerlendirilir. İlk eşleşme bulunduğunda koşul doğrudur, ancak PowerShell yine de koleksiyonun tamamını numaralandırır. Bu numaralandırmanın büyük koleksiyonlar için önemli bir performans etkisi olabilir.
Performansı artırmanın bir yolu, koleksiyonun Where() yöntemini kullanmaktır. Yöntem, ilk eşleşmeyi bulduğunda koleksiyonu değerlendirmeyi durdurur.
# Create an array of 1048576 items
$Collection = foreach ($x in 1..1MB) { $x }
(Measure-Command { if ($Collection -like '*1*') { 'Found' } }).TotalMilliseconds
633.3695
(Measure-Command { if ($Collection.Where({ $_ -like '*1*' }, 'first')) { 'Found' } }).TotalMilliseconds
2.607
Bir milyon öğe için yönteminin Where() kullanılması önemli ölçüde daha hızlıdır.
Nesne oluşturma
New-Object cmdlet'ini kullanarak nesne oluşturmak yavaş olabilir. Aşağıdaki kod, New-Object cmdlet'ini kullanarak nesne oluşturma performansını [pscustomobject] tür hızlandırıcısı ile karşılaştırır.
Measure-Command {
$test = 'PSCustomObject'
for ($i = 0; $i -lt 100000; $i++) {
$resultObject = [pscustomobject]@{
Name = 'Name'
Path = 'FullName'
}
}
} | Select-Object @{n='Test';e={$test}},TotalSeconds
Measure-Command {
$test = 'New-Object'
for ($i = 0; $i -lt 100000; $i++) {
$resultObject = New-Object -TypeName psobject -Property @{
Name = 'Name'
Path = 'FullName'
}
}
} | Select-Object @{n='Test';e={$test}},TotalSeconds
Test TotalSeconds
---- ------------
PSCustomObject 0.48
New-Object 3.37
PowerShell 5.0, tüm .NET türleri için new() statik yöntemini ekledi. Aşağıdaki kod, New-Object cmdlet'ini kullanarak nesne oluşturma performansını new() yöntemiyle karşılaştırır.
Measure-Command {
$test = 'new() method'
for ($i = 0; $i -lt 100000; $i++) {
$sb = [System.Text.StringBuilder]::new(1000)
}
} | Select-Object @{n='Test';e={$test}},TotalSeconds
Measure-Command {
$test = 'New-Object'
for ($i = 0; $i -lt 100000; $i++) {
$sb = New-Object -TypeName System.Text.StringBuilder -ArgumentList 1000
}
} | Select-Object @{n='Test';e={$test}},TotalSeconds
Test TotalSeconds
---- ------------
new() method 0.59
New-Object 3.17
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, psobject yeni bir [OrderedDictionary] kullanmak ve ardından tür hızlandırıcısını kullanarak bunu bir [pscustomobject]'e dönüştürmektir. Daha fazla bilgi için about_Hash_TablesSıralı sözlük oluşturma bölümüne bakın.
$jsondeğişkeninde 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 Add-Member cmdlet'ini 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
}
OrderedDictionarykullanarak 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 da $result çı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