Filtro de diferencia de rendimiento de PowerShell vs. función

11

Actualmente estoy leyendo el libro paso a paso de Windows PowerShell 3.0 para obtener más información sobre PowerShell.

En la página 201, el autor demuestra que un filtro es más rápido que la función con la misma funcionalidad.

Este script toma 2.6 segundos en su computadora:

MeasureAddOneFilter.ps1
Filter AddOne
{ 
 "add one filter"
  $_ + 1
}

Measure-Command { 1..50000 | addOne }

y este 4.6 segundos

MeasureAddOneFunction.ps1
Function AddOne
{  
  "Add One Function"
  While ($input.moveNext())
   {
     $input.current + 1
   }
}

Measure-Command { 1..50000 | addOne }

Si ejecuto este código es obtener exactamente lo contrario de su resultado:

.\MeasureAddOneFilter.ps1
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 226
Ticks             : 2266171
TotalDays         : 2,62288310185185E-06
TotalHours        : 6,29491944444444E-05
TotalMinutes      : 0,00377695166666667
TotalSeconds      : 0,2266171
TotalMilliseconds : 226,6171

.\MeasureAddOneFunction.ps1

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 93
Ticks             : 933649
TotalDays         : 1,08061226851852E-06
TotalHours        : 2,59346944444444E-05
TotalMinutes      : 0,00155608166666667
TotalSeconds      : 0,0933649
TotalMilliseconds : 93,3649

¿Alguien puede explicarme esto?

Marcel Janus
fuente

Respuestas:

13

A menos que el autor haya dado más pruebas de apoyo, tal vez solo estaba lleno de aire caliente. Ejecutó la prueba y obtuvo el resultado y demostró que estaba equivocado.

Editar: Desde el blog de Jeffrey Snover:

Un filtro es una función que solo tiene un bloque de script de proceso

Eso por sí solo no es suficiente para convencerme de que un filtro tendrá una ventaja de velocidad sobre una función, dado que ambos tienen bloques de proceso idénticos.

Además, ¿en qué tipo de equipo de la década de 1950 está ese tipo en el que toma 4.6 segundos agregar uno a un número?

PS C:\Users\Ryan> Measure-Command { Filter AddOne { $_ + 1 }; AddOne 1 }

TotalMilliseconds : 7.7266


PS C:\Users\Ryan> Measure-Command { Function AddOne { $_ + 1 }; AddOne 1 }    

TotalMilliseconds : 0.4108

4.6 segundos es un golpe. Tal vez el autor estaba usando algún tipo de versión CTP de Powershell antes de que se crearan los binarios. :PAGS

Finalmente, intente su prueba en una nueva sesión de Powershell, pero en orden inverso. Pruebe la función primero y el filtro segundo, o viceversa:

PS C:\Users\Ryan> Measure-Command { Function AddOne { $_ + 1 }; AddOne 1 }    

TotalMilliseconds : 6.597    


PS C:\Users\Ryan> Measure-Command { Filter AddOne { $_ + 1 }; AddOne 1 }

TotalMilliseconds : 0.4055

¿Ver? El primero que ejecutes siempre será más lento. Se trataba solo de lo interno de .NET de haber cargado cosas en la memoria que hace que la segunda operación sea más rápida, independientemente de si es una función o un filtro.

Sin embargo, admitiré que la función todavía parece ser consistentemente más rápida que el filtro, independientemente de cuántas veces se ejecute.

Measure-Command { Function AddOne($Num) { Return $Num += 1 }; 1..50000 | AddOne $_ }

TotalMilliseconds : 13.9813

Measure-Command { Filter AddOne($Num) { Return $Num += 1 }; 1..50000 | AddOne $_ }

TotalMilliseconds : 69.5301

Entonces el autor estaba equivocado ... y ahora no me siento mal por nunca haber usado un Filtro en lugar de una Función antes.

Ryan Ries
fuente
4

En realidad, la diferencia es mucho menor si usa los mismos $ _ en ambas pruebas. No investigé la causa, pero supongo que es porque el autor no está usando el mismo enfoque en ambas pruebas. Además, la salida de la consola puede interferir en los resultados. Si corta estas partes, los números son muy similares. Ver:

Function AddOneFunction
{  
    process {
        $_ + 1
    }
}

Filter AddOneFilter
{ 
    $_ + 1
}

write-host "First"
Measure-Command { 1..50000 | AddOneFilter } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFilter } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFilter } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFilter } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFilter } | select totalMilliseconds

write-host "Second"
Measure-Command { 1..50000 | AddOneFunction } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFunction } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFunction } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFunction } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFunction } | select totalMilliseconds

Los resultados serán muy cercanos, incluso si cambia el orden de los comandos.

First

TotalMilliseconds
-----------------
        84.6742
        84.7646
        89.8603
        82.3399
        83.8195
Second
        86.8978
        87.4064
        89.304
        94.4334
        87.0135

La documentación también dice que los filtros son básicamente accesos directos a funciones con solo el bloque de proceso. Las funciones, a menos que se especifiquen con un bloque de proceso (o alguna otra técnica como el uso de variables automáticas como $ input), se ejecutan una vez, no utilizan input y no pasan al siguiente comando en la tubería.

Más información en https://technet.microsoft.com/en-us/library/hh847829.aspx y https://technet.microsoft.com/en-us/library/hh847781.aspx

Vinicius Xavier
fuente