Рекомендации по производительности сценариев PowerShell
Скрипты PowerShell, использующие .NET напрямую и избегающие конвейера, как правило, быстрее, чем идиоматическая PowerShell. Idiomatic PowerShell использует командлеты и функции PowerShell, часто используя конвейер и прибегая к .NET только при необходимости.
Заметка
Многие описанные здесь методы не идиоматичны PowerShell и могут снизить удобочитаемость скрипта PowerShell. Авторы скриптов рекомендуется использовать идиоматический PowerShell, если производительность не диктует в противном случае.
Подавление выходных данных
Существует множество способов избежать записи объектов в конвейер.
- Назначение или перенаправление файлов в
$null
- Приведение к
[void]
- Канал в
Out-Null
Скорость назначения $null
, приведение к [void]
и перенаправление файлов в $null
почти идентичны. Однако вызов Out-Null
в большом цикле может быть значительно медленнее, особенно в 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'
}
}
}
Эти тесты были запущены на компьютере с Windows 11 в PowerShell 7.3.4. Результаты показаны ниже.
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
Время и относительные скорости могут отличаться в зависимости от оборудования, версии PowerShell и текущей рабочей нагрузки в системе.
Добавление массива
Создание списка элементов часто выполняется с помощью массива с оператором сложения:
$results = @()
$results += Get-Something
$results += Get-SomethingElse
$results
Добавление массива неэффективно, так как массивы имеют фиксированный размер. Каждое дополнение к массиву создает достаточно большой массив для хранения всех элементов как левых, так и правых операндов. Элементы обоих операндов копируются в новый массив. Для небольших коллекций это может не иметь значения. Производительность может страдать от больших коллекций.
Существует несколько альтернатив. Если на самом деле не требуется массив, рекомендуется использовать типизированный универсальный список ([List<T>]
):
$results = [System.Collections.Generic.List[object]]::new()
$results.AddRange((Get-Something))
$results.AddRange((Get-SomethingElse))
$results
Влияние на производительность добавления массива увеличивается экспоненциально с размером коллекции и числами. Этот код сравнивает явное назначение значений массиву с использованием добавления массива и использования метода Add(T)
в объекте [List<T>]
. Он определяет явное назначение в качестве базового показателя производительности.
$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'
}
}
}
Эти тесты были запущены на компьютере с Windows 11 в 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
При работе с большими коллекциями добавление массива значительно медленнее, чем добавление в List<T>
.
При использовании объекта [List<T>]
необходимо создать список с определенным типом, например [String]
или [Int]
. При добавлении объектов другого типа в список они приведение к указанному типу. Если они не могут быть приведение к указанному типу, метод вызывает исключение.
$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
Если вам нужно, чтобы список был коллекцией различных типов объектов, создайте его с [Object]
в качестве типа списка. Вы можете перечислить коллекцию, чтобы проверить типы объектов в нем.
$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
Если требуется массив, можно вызвать метод ToArray()
в списке или позволить PowerShell создать этот массив:
$results = @(
Get-Something
Get-SomethingElse
)
В этом примере PowerShell создает [ArrayList]
для хранения результатов, записанных в конвейер внутри выражения массива. Перед назначением $results
PowerShell преобразует [ArrayList]
в [Object[]]
.
Добавление строки
Строки неизменяемы. Каждое дополнение к строке фактически создает новую строку достаточно большой для хранения содержимого левых и правых операндов, а затем копирует элементы обоих операндов в новую строку. Для небольших строк это может не иметь значения. Для больших строк это может повлиять на производительность и потребление памяти.
Существует по крайней мере два варианта:
- Оператор
-join
объединяет строки - Класс
[StringBuilder]
.NET предоставляет изменяемую строку
В следующем примере сравнивается производительность этих трех методов построения строки.
$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'
}
}
}
Эти тесты были запущены на компьютере с Windows 11 в PowerShell 7.4.2. В выходных данных показано, что оператор -join
является самым быстрым, за которым следует класс [StringBuilder]
.
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
Время и относительные скорости могут отличаться в зависимости от оборудования, версии PowerShell и текущей рабочей нагрузки в системе.
Обработка больших файлов
Идиоматический способ обработки файла в PowerShell может выглядеть примерно так:
Get-Content $path | Where-Object Length -GT 10
Это может быть более медленным порядком, чем использование API .NET напрямую. Например, можно использовать класс .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()
}
}
Вы также можете использовать метод ReadLines
[System.IO.File]
, который упаковывает StreamReader
, упрощает процесс чтения:
foreach ($line in [System.IO.File]::ReadLines($path)) {
if ($line.Length -gt 10) {
$line
}
}
Поиск записей по свойству в больших коллекциях
Обычно необходимо использовать общее свойство для идентификации одной записи в разных коллекциях, например с помощью имени для получения идентификатора из одного списка и электронной почты из другого. Итерации по первому списку, чтобы найти соответствующую запись во второй коллекции, медленно. В частности, повторная фильтрация второй коллекции имеет большие затраты.
Учитывая две коллекции, одна из них с идентификатором
$Employees = 1..10000 | ForEach-Object {
[PSCustomObject]@{
Id = $_
Name = "Name$_"
}
}
$Accounts = 2500..7500 | ForEach-Object {
[PSCustomObject]@{
Name = "Name$_"
Email = "Name$_@fabrikam.com"
}
}
Обычный способ примирить эти коллекции для возврата списка объектов с идентификатором
$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
}
}
Однако эта реализация должна фильтровать все 5000 элементов в коллекции $Accounts
один раз для каждого элемента в коллекции $Employee
. Это может занять несколько минут, даже для поиска с одним значением.
Вместо этого можно сделать хэш-таблицу
$LookupHash = @{}
foreach ($Account in $Accounts) {
$LookupHash[$Account.Name] = $Account
}
Поиск ключей в хэш-таблице гораздо быстрее, чем фильтрация коллекции по значениям свойств. Вместо проверки каждого элемента в коллекции PowerShell может проверить, определен ли ключ и использовать его значение.
$Results = $Employees | ForEach-Object -Process {
$Email = $LookupHash[$_.Name].Email
[pscustomobject]@{
Id = $_.Id
Name = $_.Name
Email = $Email
}
}
Это гораздо быстрее. Пока фильтр циклов занимает несколько минут, хэш-поиск занимает менее секунды.
Тщательно используйте Write-Host
Команда Write-Host
должна использоваться только в том случае, если необходимо написать форматированный текст в консоль узла, а не записывать объекты в конвейер Success.
Write-Host
может быть порядком медленнее, чем [Console]::WriteLine()
для определенных узлов, таких как pwsh.exe
, powershell.exe
или powershell_ise.exe
. Однако [Console]::WriteLine()
не гарантируется работать во всех узлах. Кроме того, выходные данные, написанные с помощью [Console]::WriteLine()
, не записываются в расшифровки, запущенные Start-Transcript
.
Компиляция JIT
PowerShell компилирует код скрипта в байт-код, который интерпретируется. Начиная с PowerShell 3, для кода, который многократно выполняется в цикле, PowerShell может повысить производительность, скомпилируя код в машинный код.
Циклы, имеющие менее 300 инструкций, могут быть доступны для JIT-компиляции. Циклы больше, чем это слишком затратно для компиляции. При выполнении цикла 16 раз скрипт компилируется в фоновом режиме. После завершения компиляции JIT выполнение передается в скомпилированный код.
Избегайте повторных вызовов функции
Вызов функции может быть дорогой операцией. Если вы вызываете функцию в длительном жестком цикле, рассмотрите возможность перемещения цикла внутри функции.
Рассмотрим следующие примеры:
$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'
}
}
}
Пример
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
Избегайте упаковки конвейеров командлетов
Большинство командлетов реализованы для конвейера, который является последовательным синтаксисом и процессом. Например:
cmdlet1 | cmdlet2 | cmdlet3
Инициализация нового конвейера может быть дорогой, поэтому следует избегать упаковки конвейера командлета в другой существующий конвейер.
Рассмотрим следующий пример. Файл Input.csv
содержит 2100 строк. Команда Export-Csv
упаковывается в конвейер ForEach-Object
. Командлет Export-Csv
вызывается для каждой итерации цикла 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
В следующем примере команда Export-Csv
была перемещена за пределы конвейера ForEach-Object
.
В этом случае Export-Csv
вызывается только один раз, но по-прежнему обрабатывает все объекты, передаваемые из 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
Пример распакученного кода 372 раза быстрее. Кроме того, обратите внимание, что для первой реализации требуется параметр добавления
Создание объекта
Создание объектов с помощью командлета New-Object
может быть медленным. Следующий код сравнивает производительность создания объектов с помощью командлета New-Object
с акселератором типа [pscustomobject]
.
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 добавил статический метод new()
для всех типов .NET. Следующий код сравнивает производительность создания объектов с помощью командлета New-Object
с методом new()
.
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
Использование OrderedDictionary для динамического создания новых объектов
Существуют ситуации, когда может потребоваться динамически создавать объекты на основе некоторых входных данных, возможно, наиболее часто используемый способ создания нового PSObject, а затем добавить новые свойства с помощью командлета Add-Member
. Затраты на производительность небольших коллекций, использующих этот метод, могут быть незначительными, однако это может стать очень заметным для больших коллекций. В этом случае рекомендуется использовать [OrderedDictionary]
, а затем преобразовать его в PSObject с помощью акселератора типов [pscustomobject]
. Дополнительные сведения см. в разделе Создание упорядоченных словарей раздела about_Hash_Tables.
Предположим, что у вас есть следующий ответ API, хранящийся в переменной $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" ]
]
}
]
}
Предположим, что вы хотите экспортировать эти данные в CSV-файл. Сначала необходимо создать новые объекты и добавить свойства и значения с помощью командлета Add-Member
.
$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
код можно преобразовать в:
$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
}
В обоих случаях выходные данные $result
будут одинаковыми:
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
Последний подход становится экспоненциально более эффективным по мере увеличения числа объектов и свойств элементов.
Ниже приведено сравнение производительности трех методов создания объектов с 5 свойствами:
$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'
}
}
}
И это результаты:
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
Связанные ссылки
$null
[void]
- out-NULL
List<T>
- метода
[String]
[Int]
[Object]
- метода
[ArrayList]
[StringBuilder]
[StreamReader]
- метода
- узла записи
-
надстройки
PowerShell