Consideraciones sobre el rendimiento del scripting de PowerShell
Los scripts de PowerShell que aprovechan .NET directamente y evitan la canalización tienden a ser más rápidos que PowerShell idiomático. PowerShell idiomático usa cmdlets y funciones de PowerShell, a menudo aprovechando la canalización y recurriendo a .NET solo cuando sea necesario.
Nota
Muchas de las técnicas que se describen aquí no son de PowerShell idiomática y pueden reducir la legibilidad de un script de PowerShell. Se recomienda a los autores de scripts usar PowerShell idiomático a menos que el rendimiento lo determine de otro modo.
Supresión de la salida
Hay muchas maneras de evitar escribir objetos en la canalización.
- Asignación o redirección de archivos a
$null
- Conversión a
[void]
- Canalización a
Out-Null
Las velocidades de asignación a $null
, la conversión a [void]
y la redirección de archivos a $null
son casi idénticas. Sin embargo, llamar a Out-Null
en un bucle grande puede ser significativamente más lento, especialmente en 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'
}
}
}
Estas pruebas se ejecutaron en una máquina Windows 11 en PowerShell 7.3.4. A continuación se muestran los resultados:
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
Los tiempos y velocidades relativas pueden variar en función del hardware, la versión de PowerShell y la carga de trabajo actual del sistema.
Adición de matrices
La generación de una lista de elementos a menudo se realiza mediante una matriz con el operador de suma:
$results = @()
$results += Get-Something
$results += Get-SomethingElse
$results
La adición de matrices es ineficaz porque las matrices tienen un tamaño fijo. Cada adición a la matriz crea una nueva matriz lo suficientemente grande como para contener todos los elementos de los operandos izquierdo y derecho. Los elementos de ambos operandos se copian en la nueva matriz. En el caso de las colecciones pequeñas, esta sobrecarga puede no importar. El rendimiento puede verse afectado por colecciones grandes.
Hay un par de alternativas. Si realmente no necesita una matriz, considere la posibilidad de usar una lista genérica con tipo ([List<T>]
):
$results = [System.Collections.Generic.List[object]]::new()
$results.AddRange((Get-Something))
$results.AddRange((Get-SomethingElse))
$results
El impacto en el rendimiento del uso de la adición de matrices aumenta exponencialmente con el tamaño de la colección y las adiciones numéricas. Este código compara explícitamente la asignación de valores a una matriz mediante la adición de matrices y el uso del método Add(T)
en un objeto [List<T>]
. Define la asignación explícita como línea base para el rendimiento.
$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'
}
}
}
Estas pruebas se ejecutaron en una máquina Windows 11 en 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
Cuando se trabaja con colecciones grandes, la adición de matrices es considerablemente más lenta que agregar a un List<T>
.
Al usar un objeto [List<T>]
, debe crear la lista con un tipo específico, como [String]
o [Int]
. Al agregar objetos de un tipo diferente a la lista, se convierten al tipo especificado. Si no se pueden convertir al tipo especificado, el método genera una excepción.
$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
Cuando necesite que la lista sea una colección de diferentes tipos de objetos, créela con [Object]
como tipo de lista. Puede enumerar la colección inspeccionar los tipos de los objetos en él.
$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
Si necesita una matriz, puede llamar al método ToArray()
en la lista o permitir que PowerShell cree la matriz automáticamente:
$results = @(
Get-Something
Get-SomethingElse
)
En este ejemplo, PowerShell crea un [ArrayList]
para contener los resultados escritos en la canalización dentro de la expresión de matriz. Justo antes de asignar a $results
, PowerShell convierte el [ArrayList]
en un [Object[]]
.
Adición de cadenas
Las cadenas son inmutables. Cada adición a la cadena crea realmente una nueva cadena lo suficientemente grande como para contener el contenido de los operandos izquierdo y derecho, luego copia los elementos de ambos operandos en la nueva cadena. En el caso de cadenas pequeñas, esta sobrecarga puede no importar. En el caso de cadenas grandes, esto puede afectar al rendimiento y al consumo de memoria.
Hay al menos dos alternativas:
- El operador
-join
concatena cadenas - La clase
[StringBuilder]
.NET proporciona una cadena mutable.
En el ejemplo siguiente se compara el rendimiento de estos tres métodos de creación de una cadena.
$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'
}
}
}
Estas pruebas se ejecutaron en una máquina Windows 11 en PowerShell 7.4.2. La salida muestra que el operador -join
es el más rápido, seguido de la clase [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
Los tiempos y velocidades relativas pueden variar en función del hardware, la versión de PowerShell y la carga de trabajo actual del sistema.
Procesamiento de archivos grandes
La forma idiomática de procesar un archivo en PowerShell podría tener un aspecto similar al siguiente:
Get-Content $path | Where-Object Length -GT 10
Puede ser un orden de magnitud más lento que el uso de las API de .NET directamente. Por ejemplo, puede usar la clase [StreamReader]
de .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()
}
}
También puede usar el método ReadLines
de [System.IO.File]
, que encapsula StreamReader
, simplifica el proceso de lectura:
foreach ($line in [System.IO.File]::ReadLines($path)) {
if ($line.Length -gt 10) {
$line
}
}
Búsqueda de entradas por propiedad en colecciones grandes
Es habitual usar una propiedad compartida para identificar el mismo registro en colecciones diferentes, como usar un nombre para recuperar un identificador de una lista y un correo electrónico de otro. La iteración en la primera lista para encontrar el registro coincidente en la segunda colección es lento. En concreto, el filtrado repetido de la segunda colección tiene una gran sobrecarga.
Dadas dos colecciones, una con un identificador de
$Employees = 1..10000 | ForEach-Object {
[PSCustomObject]@{
Id = $_
Name = "Name$_"
}
}
$Accounts = 2500..7500 | ForEach-Object {
[PSCustomObject]@{
Name = "Name$_"
Email = "Name$_@fabrikam.com"
}
}
La manera habitual de conciliar estas colecciones para devolver una lista de objetos con el identificador de , Nombrey Propiedades de Correo electrónico podría tener este aspecto:
$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
}
}
Sin embargo, esa implementación tiene que filtrar todos los 5000 elementos de la colección $Accounts
una vez por cada elemento de la colección $Employee
. Esto puede tardar minutos, incluso para esta búsqueda de valor único.
En su lugar, puede crear un tabla hash de
$LookupHash = @{}
foreach ($Account in $Accounts) {
$LookupHash[$Account.Name] = $Account
}
Buscar claves en una tabla hash es mucho más rápida que filtrar una colección por valores de propiedad. En lugar de comprobar todos los elementos de la colección, PowerShell puede comprobar si la clave está definida y usar su valor.
$Results = $Employees | ForEach-Object -Process {
$Email = $LookupHash[$_.Name].Email
[pscustomobject]@{
Id = $_.Id
Name = $_.Name
Email = $Email
}
}
Esto es mucho más rápido. Mientras el filtro de bucle tarda minutos en completarse, la búsqueda hash tarda menos de un segundo.
Usar Write-Host cuidadosamente
El comando
Write-Host
puede ser un orden de magnitud más lento que [Console]::WriteLine()
para hosts específicos, como pwsh.exe
, powershell.exe
o powershell_ise.exe
. Sin embargo, no se garantiza que [Console]::WriteLine()
funcionen en todos los hosts. Además, la salida escrita mediante [Console]::WriteLine()
no se escribe en transcripciones iniciadas por Start-Transcript
.
Compilación JIT
PowerShell compila el código de script en el código de bytes que se interpreta. A partir de PowerShell 3, para el código que se ejecuta repetidamente en un bucle, PowerShell puede mejorar el rendimiento mediante la compilación just-in-time (JIT) del código en código nativo.
Los bucles que tienen menos de 300 instrucciones son aptos para la compilación JIT. Los bucles más grandes que son demasiado costosos de compilar. Cuando el bucle se ha ejecutado 16 veces, el script se compila JIT en segundo plano. Cuando se completa la compilación JIT, la ejecución se transfiere al código compilado.
Evitar llamadas repetidas a una función
Llamar a una función puede ser una operación costosa. Si llama a una función en un bucle ajustado de larga duración, considere la posibilidad de mover el bucle dentro de la función.
Tenga en cuenta los ejemplos siguientes:
$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'
}
}
}
El de bucle básico de
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 el ajuste de canalizaciones de cmdlets
La mayoría de los cmdlets se implementan para la canalización, que es una sintaxis secuencial y un proceso. Por ejemplo:
cmdlet1 | cmdlet2 | cmdlet3
La inicialización de una nueva canalización puede ser costosa, por lo que debe evitar encapsular una canalización de cmdlet en otra canalización existente.
Considere el ejemplo siguiente. El archivo Input.csv
contiene 2100 líneas. El comando Export-Csv
se ajusta dentro de la canalización de ForEach-Object
. El cmdlet Export-Csv
se invoca para cada iteración del bucle 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
En el ejemplo siguiente, el comando Export-Csv
se movió fuera de la canalización de ForEach-Object
.
En este caso, se invoca Export-Csv
solo una vez, pero sigue procesando todos los objetos pasados 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
El ejemplo desencapsulado se 372 veces más rápido. Además, tenga en cuenta que la primera implementación requiere el parámetro Append, que no es necesario para la implementación posterior.
Creación de objetos
La creación de objetos mediante el cmdlet New-Object
puede ser lenta. El código siguiente compara el rendimiento de la creación de objetos mediante el cmdlet New-Object
con el acelerador de tipos [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 agregó el método estático new()
para todos los tipos de .NET. El código siguiente compara el rendimiento de la creación de objetos mediante el cmdlet New-Object
con el 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
Uso de OrderedDictionary para crear objetos nuevos de forma dinámica
Hay situaciones en las que es posible que necesitemos crear objetos dinámicamente en función de alguna entrada, la forma más común de crear una nueva PSObject y, a continuación, agregar nuevas propiedades mediante el cmdlet Add-Member
. El costo de rendimiento de las colecciones pequeñas que usan esta técnica puede ser insignificante, pero puede ser muy notable para las grandes colecciones. En ese caso, el enfoque recomendado es usar un [OrderedDictionary]
y, a continuación, convertirlo en un PSObject mediante el acelerador de tipos [pscustomobject]
. Para obtener más información, vea la sección Creación de diccionarios ordenados de about_Hash_Tables.
Supongamos que tiene la siguiente respuesta de API almacenada en la variable $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" ]
]
}
]
}
Supongamos que quiere exportar estos datos a un CSV. En primer lugar, debe crear nuevos objetos y agregar las propiedades y los valores mediante el 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
}
Con un OrderedDictionary
, el código se puede traducir a:
$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
}
En ambos casos, la salida $result
sería la misma:
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
Este último enfoque se vuelve exponencialmente más eficaz a medida que aumenta el número de objetos y propiedades de miembro.
Esta es una comparación de rendimiento de tres técnicas para crear objetos con 5 propiedades:
$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'
}
}
}
Y estos son los 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
Vínculos relacionados
$null
[void]
- Out-Null
List<T>
-
Add(T)
método [String]
[Int]
[Object]
-
ToArray()
método [ArrayList]
[StringBuilder]
[StreamReader]
-
[File]::ReadLines()
método - de host de escritura
- de miembros de complemento