PowerShell performance difference filter vs. function

powershell

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?

Best Answer

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.