PowerShell performance difference filter vs. function
Solution 1:
Unless the author gave more supporting evidence, maybe he was just full of hot air. You've run the test and got the result and proven him wrong.
Edit: From Jeffrey Snover's blog:
A filter is a function that just has a process scriptblock
That alone isn't enough to convince me that a filter is going to have a speed advantage over a function, given both have identical process blocks.
Also what sort of 1950's equipment is that guy on where it takes 4.6 seconds to add one to a number?
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 seconds is whack. Maybe the author was using some sort of CTP version of Powershell before the binaries were ngen'ed. :P
Finally, try your test in a new Powershell session, but in reverse order. Try the Function first and the Filter second, or vice versa:
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
See? The first one you run will always be slower. It was just about the .NET internals of having already loaded stuff into memory that makes the second operation faster, regardless of whether it's a function or a filter.
I will admit though that the Function still seems to be consistently faster than the Filter, regardless of how many times it's run.
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
So the author was wrong... and now I don't feel bad for never having ever used a Filter instead of a Function before.
Solution 2:
Actually the difference is much smaller if you use the same $_ in both tests. I didn't investigate the cause, but I suppose it's because the author is not using the same approach in both tests. Also, console output can interfere in the results. If you cut these parts, the numbers are very similar. See:
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
The results will be very close, even if you change the order of the commands.
First
TotalMilliseconds
-----------------
84.6742
84.7646
89.8603
82.3399
83.8195
Second
86.8978
87.4064
89.304
94.4334
87.0135
The documentation also says that Filters are basically shortcuts to functions with only the process block. Functions, unless specified with a process block (or some other technique like using automatic variables such as $input), run once, don't use input and don't pass to the next command in the pipeline.
More info at https://technet.microsoft.com/en-us/library/hh847829.aspx and https://technet.microsoft.com/en-us/library/hh847781.aspx