Server Fault is a question and answer site for system and network administrators. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm reading currently the Windows PowerShell 3.0 Step by Step book to get some more insights to PowerShell.

On page 201 the author demonstrates that a filter is faster than the function with the same functionally.

This script takes 2.6 seconds on his computer:

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

Measure-Command { 1..50000 | addOne }

and this one 4.6 seconds

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

Measure-Command { 1..50000 | addOne }

If I run this code is get the exact opposite of his result:

.\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

Can someone explain this to me?

share|improve this question
up vote 7 down vote accepted

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.

share|improve this answer

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

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.