เพื่อเสริมมาก่อนคำตอบที่เป็นประโยชน์กับการแนะนำของเมื่อใช้งานง่ายซึ่งวิธีการและการเปรียบเทียบประสิทธิภาพ
สำหรับคอลเลกชันอินพุตขนาดเล็ก (อาร์เรย์) คุณอาจไม่สังเกตเห็นความแตกต่างและโดยเฉพาะอย่างยิ่งในบรรทัดคำสั่งบางครั้งความสามารถในการพิมพ์คำสั่งได้อย่างง่ายดายนั้นสำคัญกว่า
นี่คือง่ายต่อการอีกทางเลือกหนึ่งซึ่ง แต่เป็นที่ช้าที่สุดวิธี ; มันใช้ไวยากรณ์แบบง่ายที่ForEach-Object
เรียกว่าคำสั่งการดำเนินการ (อีกครั้ง PSv3 +):; เช่นโซลูชัน PSv3 + ต่อไปนี้ง่ายต่อการผนวกเข้ากับคำสั่งที่มีอยู่:
$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
เพื่อความสมบูรณ์: วิธีการอาร์เรย์ PSv4 + ที่.ForEach()
ไม่ค่อยมีใครรู้จักซึ่งมีการกล่าวถึงในบทความนี้เป็นอีกทางเลือกหนึ่ง :
# By property name (string):
$objects.ForEach('Name')
# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
วิธีนี้คล้ายกับการแจกแจงสมาชิกโดยมีการแลกเปลี่ยนเดียวกันยกเว้นว่าจะไม่ใช้ตรรกะของท่อ มันช้ากว่าเล็กน้อยแม้ว่าจะยังเร็วกว่าไปป์ไลน์อย่างเห็นได้ชัด
สำหรับการแยกค่าคุณสมบัติเดียวตามชื่อ ( อาร์กิวเมนต์สตริง ) โซลูชันนี้จะเทียบเท่ากับการแจงนับสมาชิก (แม้ว่าหลังจะง่ายกว่าทางไวยากรณ์)
สคริปต์บล็อกตัวแปรช่วยให้พลแปลง ; มันเป็นได้เร็วขึ้น - all-in-หน่วยความจำในครั้งเดียว - ทางเลือกให้กับท่อที่ใช้ForEach-Object
cmdlet (%
)
การเปรียบเทียบประสิทธิภาพของแนวทางต่างๆ
ต่อไปนี้คือตัวอย่างการกำหนดเวลาสำหรับวิธีการต่างๆโดยพิจารณาจากคอลเล็กชันอินพุตของ10,000
อ็อบเจ็กต์โดยเฉลี่ยใน 10 รัน ตัวเลขที่แน่นอนไม่สำคัญและแตกต่างกันไปตามปัจจัยหลายประการ แต่ควรให้ความรู้สึกถึงประสิทธิภาพที่สัมพันธ์กัน (การกำหนดเวลามาจาก Windows 10 VM แบบ single-core:
สำคัญ
ประสิทธิภาพสัมพัทธ์จะแตกต่างกันไปขึ้นอยู่กับว่าออบเจ็กต์อินพุตเป็นอินสแตนซ์ของชนิด. NET ปกติ (เช่นเอาต์พุตโดยGet-ChildItem
) หรือ[pscustomobject]
อินสแตนซ์ (เช่นเอาต์พุตโดยConvert-FromCsv
)
เหตุผลก็คือ[pscustomobject]
คุณสมบัติได้รับการจัดการแบบไดนามิกโดย PowerShell และสามารถเข้าถึงได้เร็วกว่าคุณสมบัติทั่วไปของชนิด. NET ปกติ (กำหนดแบบคงที่) สถานการณ์ทั้งสองจะกล่าวถึงด้านล่าง
การทดสอบใช้คอลเลกชันที่มีอยู่แล้วในหน่วยความจำเต็มเป็นอินพุตเพื่อมุ่งเน้นไปที่ประสิทธิภาพการสกัดคุณสมบัติบริสุทธิ์ ด้วยการเรียกใช้ cmdlet / function แบบสตรีมเป็นอินพุตความแตกต่างของประสิทธิภาพโดยทั่วไปจะมีความเด่นชัดน้อยกว่ามากเนื่องจากเวลาที่ใช้ในการโทรนั้นอาจเป็นส่วนใหญ่ของเวลาที่ใช้
เพื่อความกะทัดรัด%
จะใช้นามแฝงสำหรับForEach-Object
cmdlet
ข้อสรุปทั่วไปใช้ได้กับทั้งประเภท. NET ปกติและ[pscustomobject]
อินพุต:
การแจงนับสมาชิก ( $collection.Name
) และforeach ($obj in $collection)
โซลูชันเป็นวิธีที่เร็วที่สุดโดยมีค่า 10 หรือเร็วกว่าโซลูชันที่ใช้ไปป์ไลน์ที่เร็วที่สุด
น่าแปลกที่% Name
มีประสิทธิภาพมากยิ่งกว่า% { $_.Name }
- เห็นปัญหา GitHub นี้
PowerShell Core มีประสิทธิภาพเหนือกว่า Windows Powershell ที่นี่อย่างต่อเนื่อง
การกำหนดเวลาด้วยประเภท. NET ปกติ :
- PowerShell Core v7.0.0-preview.3
Factor Command Secs (10-run avg.)
------ ------- ------------------
1.00 $objects.Name 0.005
1.06 foreach($o in $objects) { $o.Name } 0.005
6.25 $objects.ForEach('Name') 0.028
10.22 $objects.ForEach({ $_.Name }) 0.046
17.52 $objects | % { $_.Name } 0.079
30.97 $objects | Select-Object -ExpandProperty Name 0.140
32.76 $objects | % Name 0.148
- Windows PowerShell v5.1.18362.145
Comparing property-value extraction methods with 10000 input objects, averaged over 10 runs...
Factor Command Secs (10-run avg.)
------ ------- ------------------
1.00 $objects.Name 0.012
1.32 foreach($o in $objects) { $o.Name } 0.015
9.07 $objects.ForEach({ $_.Name }) 0.105
10.30 $objects.ForEach('Name') 0.119
12.70 $objects | % { $_.Name } 0.147
27.04 $objects | % Name 0.312
29.70 $objects | Select-Object -ExpandProperty Name 0.343
สรุป:
- ใน PowerShell หลัก , มีประสิทธิภาพดีกว่าอย่างเห็นได้ชัด
.ForEach('Name')
.ForEach({ $_.Name })
ใน Windows PowerShell อยากรู้อยากเห็นว่ารุ่นหลังเร็วกว่าแม้ว่าจะเพียงเล็กน้อยก็ตาม
การกำหนดเวลาด้วย[pscustomobject]
อินสแตนซ์ :
- PowerShell Core v7.0.0-preview.3
Factor Command Secs (10-run avg.)
------ ------- ------------------
1.00 $objects.Name 0.006
1.11 foreach($o in $objects) { $o.Name } 0.007
1.52 $objects.ForEach('Name') 0.009
6.11 $objects.ForEach({ $_.Name }) 0.038
9.47 $objects | Select-Object -ExpandProperty Name 0.058
10.29 $objects | % { $_.Name } 0.063
29.77 $objects | % Name 0.184
- Windows PowerShell v5.1.18362.145
Factor Command Secs (10-run avg.)
------ ------- ------------------
1.00 $objects.Name 0.008
1.14 foreach($o in $objects) { $o.Name } 0.009
1.76 $objects.ForEach('Name') 0.015
10.36 $objects | Select-Object -ExpandProperty Name 0.085
11.18 $objects.ForEach({ $_.Name }) 0.092
16.79 $objects | % { $_.Name } 0.138
61.14 $objects | % Name 0.503
สรุป:
สังเกตว่าการ[pscustomobject]
ป้อนข้อมูล.ForEach('Name')
ทำได้ดีกว่าตัวแปรที่ใช้บล็อกสคริปต์.ForEach({ $_.Name })
อย่างไร
ในทำนองเดียวกันการ[pscustomobject]
ป้อนข้อมูลทำให้การใช้ไปป์ไลน์Select-Object -ExpandProperty Name
เร็วขึ้นใน Windows PowerShell แทบจะเทียบเท่ากับ.ForEach({ $_.Name })
แต่ใน PowerShell Core ยังคงช้าลงประมาณ 50%
กล่าวโดยย่อ: ด้วยข้อยกเว้นที่แปลก% Name
ด้วย[pscustomobject]
วิธีการอ้างอิงคุณสมบัติแบบสตริงจะมีประสิทธิภาพดีกว่าคุณสมบัติที่ใช้สคริปต์บล็อก
ซอร์สโค้ดสำหรับการทดสอบ :
บันทึก:
$count = 1e4 # max. input object count == 10,000
$runs = 10 # number of runs to average
# Note: Using [pscustomobject] instances rather than instances of
# regular .NET types changes the performance characteristics.
# Set this to $true to test with [pscustomobject] instances below.
$useCustomObjectInput = $false
# Create sample input objects.
if ($useCustomObjectInput) {
# Use [pscustomobject] instances.
$objects = 1..$count | % { [pscustomobject] @{ Name = "$foobar_$_"; Other1 = 1; Other2 = 2; Other3 = 3; Other4 = 4 } }
} else {
# Use instances of a regular .NET type.
# Note: The actual count of files and folders in your home dir. tree
# may be less than $count
$objects = Get-ChildItem -Recurse $HOME | Select-Object -First $count
}
Write-Host "Comparing property-value extraction methods with $($objects.Count) input objects, averaged over $runs runs..."
# An array of script blocks with the various approaches.
$approaches = { $objects | Select-Object -ExpandProperty Name },
{ $objects | % Name },
{ $objects | % { $_.Name } },
{ $objects.ForEach('Name') },
{ $objects.ForEach({ $_.Name }) },
{ $objects.Name },
{ foreach($o in $objects) { $o.Name } }
# Time the approaches and sort them by execution time (fastest first):
Time-Command $approaches -Count $runs | Select Factor, Command, Secs*
$results = @($objects | %{ $_.Name })
เพียงเลือกชื่อ: บางครั้งการพิมพ์ในบรรทัดคำสั่งจะสะดวกกว่าแม้ว่าฉันคิดว่าคำตอบของสก็อตต์มักจะดีกว่า