Considerações de desempenho de script do PowerShell
Os scripts do PowerShell que aproveitam o .NET diretamente e evitam o pipeline tendem a ser mais rápidos do que o PowerShell idiomático. O PowerShell idioma usa cmdlets e funções do PowerShell, muitas vezes aproveitando o pipeline e recorrendo ao .NET somente quando necessário.
Nota
Muitas das técnicas descritas aqui não são idiomáticas do PowerShell e podem reduzir a legibilidade de um script do PowerShell. Os autores de script são aconselhados a usar o PowerShell idioma, a menos que o desempenho determine o contrário.
Suprimindo a saída
Há muitas maneiras de evitar a gravação de objetos no pipeline.
- Atribuição ou redirecionamento de arquivo para
$null
- Conversão para
[void]
- Pipe para
Out-Null
As velocidades de atribuição a $null
, conversão em [void]
e redirecionamento de arquivo para $null
são quase idênticas. No entanto, chamar Out-Null
em um loop grande pode ser significativamente mais lento, especialmente no 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'
}
}
}
Esses testes foram executados em um computador Windows 11 no PowerShell 7.3.4. Os resultados são mostrados abaixo:
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
Os tempos e as velocidades relativas podem variar dependendo do hardware, da versão do PowerShell e da carga de trabalho atual no sistema.
Adição de matriz
A geração de uma lista de itens geralmente é feita usando uma matriz com o operador de adição:
$results = @()
$results += Get-Something
$results += Get-SomethingElse
$results
A adição de matriz é ineficiente porque as matrizes têm um tamanho fixo. Cada adição à matriz cria uma nova matriz grande o suficiente para conter todos os elementos dos operandos esquerdo e direito. Os elementos de ambos os operandos são copiados para a nova matriz. Para coleções pequenas, essa sobrecarga pode não importar. O desempenho pode sofrer com grandes coleções.
Há algumas alternativas. Se você realmente não precisar de uma matriz, considere usar uma lista genérica tipada ([List<T>]
):
$results = [System.Collections.Generic.List[object]]::new()
$results.AddRange((Get-Something))
$results.AddRange((Get-SomethingElse))
$results
O impacto no desempenho do uso da adição de matriz aumenta exponencialmente com o tamanho da coleção e as adições numéricas. Esse código compara explicitamente a atribuição de valores a uma matriz com o uso da adição de matriz e o uso do método Add(T)
em um objeto [List<T>]
. Ele define a atribuição explícita como a linha de base para o desempenho.
$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'
}
}
}
Esses testes foram executados em um computador Windows 11 no 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
Quando você está trabalhando com grandes coleções, a adição de matriz é dramaticamente mais lenta do que adicionar a um List<T>
.
Ao usar um objeto [List<T>]
, você precisa criar a lista com um tipo específico, como [String]
ou [Int]
. Quando você adiciona objetos de um tipo diferente à lista, eles são convertidos no tipo especificado. Se eles não puderem ser convertidos no tipo especificado, o método gerará uma exceção.
$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
Quando você precisar que a lista seja uma coleção de diferentes tipos de objetos, crie-a com [Object]
como o tipo de lista. Você pode enumerar a coleção inspecionando os tipos dos objetos nela.
$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
Se você precisar de uma matriz, poderá chamar o método ToArray()
na lista ou permitir que o PowerShell crie a matriz para você:
$results = @(
Get-Something
Get-SomethingElse
)
Neste exemplo, o PowerShell cria uma [ArrayList]
para manter os resultados gravados no pipeline dentro da expressão de matriz. Pouco antes de atribuir a $results
, o PowerShell converte o [ArrayList]
em um [Object[]]
.
Adição de cadeia de caracteres
Cadeias de caracteres são imutáveis. Cada adição à cadeia de caracteres realmente cria uma nova cadeia de caracteres grande o suficiente para manter o conteúdo dos operandos esquerdo e direito e copia os elementos de ambos os operandos para a nova cadeia de caracteres. Para cadeias de caracteres pequenas, essa sobrecarga pode não importar. Para cadeias de caracteres grandes, isso pode afetar o desempenho e o consumo de memória.
Há pelo menos duas alternativas:
- O operador
-join
concatena cadeias de caracteres - A classe de
[StringBuilder]
do .NET fornece uma cadeia de caracteres mutável
O exemplo a seguir compara o desempenho desses três métodos de criação de uma cadeia de caracteres.
$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'
}
}
}
Esses testes foram executados em um computador Windows 11 no PowerShell 7.4.2. A saída mostra que o operador -join
é o mais rápido, seguido pela classe [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
Os tempos e as velocidades relativas podem variar dependendo do hardware, da versão do PowerShell e da carga de trabalho atual no sistema.
Processando arquivos grandes
A maneira idiomática de processar um arquivo no PowerShell pode ser semelhante a:
Get-Content $path | Where-Object Length -GT 10
Essa pode ser uma ordem de magnitude mais lenta do que usar as APIs do .NET diretamente. Por exemplo, você pode usar a classe de [StreamReader]
do .NET:
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()
}
}
Você também pode usar o método ReadLines
de [System.IO.File]
, que encapsula StreamReader
, simplifica o processo de leitura:
foreach ($line in [System.IO.File]::ReadLines($path)) {
if ($line.Length -gt 10) {
$line
}
}
Pesquisando entradas por propriedade em grandes coleções
É comum precisar usar uma propriedade compartilhada para identificar o mesmo registro em coleções diferentes, como usar um nome para recuperar uma ID de uma lista e um email de outra. A iteração na primeira lista para localizar o registro correspondente na segunda coleção é lenta. Em particular, a filtragem repetida da segunda coleção tem uma grande sobrecarga.
Considerando duas coleções, uma com uma ID e Name, outra com Name e Email:
$Employees = 1..10000 | ForEach-Object {
[PSCustomObject]@{
Id = $_
Name = "Name$_"
}
}
$Accounts = 2500..7500 | ForEach-Object {
[PSCustomObject]@{
Name = "Name$_"
Email = "Name$_@fabrikam.com"
}
}
A maneira usual de reconciliar essas coleções para retornar uma lista de objetos com as propriedades ID, Namee Email pode ter esta aparência:
$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
}
}
No entanto, essa implementação precisa filtrar todos os 5.000 itens na coleção $Accounts
uma vez para cada item na coleção $Employee
. Isso pode levar minutos, mesmo para essa pesquisa de valor único.
Em vez disso, você pode fazer um de Tabela de Hash
$LookupHash = @{}
foreach ($Account in $Accounts) {
$LookupHash[$Account.Name] = $Account
}
Procurar chaves em uma tabela de hash é muito mais rápido do que filtrar uma coleção por valores de propriedade. Em vez de verificar cada item da coleção, o PowerShell pode verificar se a chave está definida e usar seu valor.
$Results = $Employees | ForEach-Object -Process {
$Email = $LookupHash[$_.Name].Email
[pscustomobject]@{
Id = $_.Id
Name = $_.Name
Email = $Email
}
}
Isso é muito mais rápido. Enquanto o filtro de loop levou minutos para ser concluído, a pesquisa de hash leva menos de um segundo.
Use Write-Host cuidadosamente
O comando Write-Host
só deve ser usado quando você precisar gravar texto formatado no console do host, em vez de gravar objetos no pipeline Success.
Write-Host
pode ser uma ordem de magnitude mais lenta que [Console]::WriteLine()
para hosts específicos, como pwsh.exe
, powershell.exe
ou powershell_ise.exe
. No entanto, [Console]::WriteLine()
não é garantido para funcionar em todos os hosts. Além disso, a saída gravada usando [Console]::WriteLine()
não é gravada em transcrições iniciadas pelo Start-Transcript
.
Compilação JIT
O PowerShell compila o código de script para bytecode interpretado. A partir do PowerShell 3, para o código executado repetidamente em um loop, o PowerShell pode melhorar o desempenho compilando o código em código nativo.
Loops com menos de 300 instruções são qualificados para compilação JIT. Loops maiores do que esses são muito caros para serem compilados. Quando o loop é executado 16 vezes, o script é compilado em segundo plano. Quando a compilação JIT for concluída, a execução será transferida para o código compilado.
Evitar chamadas repetidas para uma função
Chamar uma função pode ser uma operação cara. Se você estiver chamando uma função em um loop apertado de execução longa, considere mover o loop dentro da função.
Considere os seguintes exemplos:
$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'
}
}
}
O exemplo de básico do
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
Evitar encapsular pipelines de cmdlet
A maioria dos cmdlets é implementada para o pipeline, que é uma sintaxe sequencial e um processo. Por exemplo:
cmdlet1 | cmdlet2 | cmdlet3
A inicialização de um novo pipeline pode ser cara, portanto, você deve evitar encapsular um pipeline de cmdlet em outro pipeline existente.
Considere o exemplo a seguir. O arquivo Input.csv
contém 2100 linhas. O comando Export-Csv
é encapsulado dentro do pipeline ForEach-Object
. O cmdlet Export-Csv
é invocado para cada iteração do loop 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
Para o próximo exemplo, o comando Export-Csv
foi movido para fora do pipeline de ForEach-Object
.
Nesse caso, Export-Csv
é invocado apenas uma vez, mas ainda processa todos os objetos passados de 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
O exemplo desembrulhado é 372 vezes mais rápido. Além disso, observe que a primeira implementação requer o parâmetro Acrescentar, que não é necessário para a implementação posterior.
Criação de objeto
A criação de objetos usando o cmdlet New-Object
pode ser lenta. O código a seguir compara o desempenho da criação de objetos usando o cmdlet New-Object
com o acelerador de tipo [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
O PowerShell 5.0 adicionou o método estático new()
para todos os tipos de .NET. O código a seguir compara o desempenho da criação de objetos usando o cmdlet New-Object
com o método 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
Usar OrderedDictionary para criar dinamicamente novos objetos
Há situações em que talvez seja necessário criar objetos dinamicamente com base em alguma entrada, a maneira talvez mais usada para criar um novo PSObject e, em seguida, adicionar novas propriedades usando o cmdlet Add-Member
. O custo de desempenho para pequenas coleções usando essa técnica pode ser insignificante, no entanto, pode se tornar muito perceptível para grandes coleções. Nesse caso, a abordagem recomendada é usar um [OrderedDictionary]
e convertê-lo em um PSObject usando o acelerador de tipo [pscustomobject]
. Para obter mais informações, consulte a seção Criando dicionários ordenados de about_Hash_Tables.
Suponha que você tenha a seguinte resposta de API armazenada na variável $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" ]
]
}
]
}
Agora, suponha que você queira exportar esses dados para um CSV. Primeiro, você precisa criar novos objetos e adicionar as propriedades e valores usando o cmdlet 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
}
Usando um OrderedDictionary
, o código pode ser traduzido para:
$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
}
Em ambos os casos, a saída $result
seria a mesma:
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
Esta última abordagem torna-se exponencialmente mais eficiente à medida que o número de objetos e propriedades de membro aumenta.
Aqui está uma comparação de desempenho de três técnicas para criar objetos com cinco propriedades:
$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'
}
}
}
E estes são os resultados:
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
Links relacionados
$null
[void]
- Out-Null
List<T>
- método
Add(T)
[String]
[Int]
[Object]
- método
ToArray()
[ArrayList]
[StringBuilder]
[StreamReader]
- método
[File]::ReadLines()
- de host de gravação
- de membro do suplemento