Scripting Games 2012 comments: #5 using WHERE

 

I have seen a few interesting variations on using where-object during the games. This is normally aliased to where. You can use ? if you are fanatical about aliases but to me your PowerShell starts to become unreadable if its heavily aliased.

Consider get-process.  It returns a set of process objects. Where-Object is used to put a filter on those objects to cut down the amount of data on the pipeline -

PS> get-process | where {$_.Handles -gt 500}

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    558       9     1576       3424    46             384 csrss
    660      11     1768       6816   102             452 csrss
   1396      38    45412      58896   262    40.69   2776 explorer
    622      23    43196      63204   309    30.76   2164 iexplore
   1000      17     4988      10440    36             508 lsass
    509      14    37992      43308   202     4.06   4688 powershell
    938      35    42864      27516   131            3620 SearchIndexer
    628      16    16392      15968    90             824 svchost
    803      18    65548      73660   160             864 svchost
   1369      34    22884      34012   200             900 svchost
    504      17     6616      11640    52            1056 svchost
    784       0       48        968     3               4 System
    669      50   109832      78000   400    17.99   1700 WindowsLiveWriter
    526      18    17084      24020   150     4.06    788 wlcomm
   1951     178   222312     217488   689    96.39   3872 wlmail

or

PS> get-process | where {$_.CPU -gt 50}

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    124       8    28828      24996   120   131.10   2744 dwm
    485      23    14940      37444   202    52.49   3272 iexplore
    363      26    25180      60088   335    78.14   5336 WINWORD
   1951     178   222312     217488   689    96.42   3872 wlmail

We often need to combine filters. These can apply both filters or either filter.

Both filters are applied using the –and operator

PS> get-process | where {$_.CPU -gt 50 -and $_.Handles -gt 500 }

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
   1956     179   222332     217508   689    96.47   3872 wlmail

In the situation where you want either the CPU or the Handles filter to apply you use the –or operator

PS> get-process | where {$_.CPU -gt 50 -or $_.Handles -gt 500 }

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    532       9     1576       3368    46             384 csrss
    651      11     1768       6816   103             452 csrss
    124       8    27804      23964   119   132.18   2744 dwm
   1403      38    46744      60284   263    41.53   2776 explorer
    619      23    43128      63196   308    30.76   2164 iexplore
    488      23    14940      37444   202    52.51   3272 iexplore
    992      17     4988      10440    36             508 lsass
    566      14    34704      40004   202     4.68   4688 powershell
    918      35    42860      27436   131            3620 SearchIndexer
    617      16    16364      15952    89             824 svchost
    807      18    65548      73684   160             864 svchost
   1371      34    22932      34048   201             900 svchost
    508      17     6616      11632    52            1056 svchost
    782       0       48        968     3               4 System
    672      50   103560      76708   400    32.85   1700 WindowsLiveWriter
    363      26    25180      60088   335    78.16   5336 WINWORD
    520      18    17060      24000   150     4.09    788 wlcomm
   1953     178   222332     217508   689    96.52   3872 wlmail

 

Notice the very different outputs!

One thing I have seen in the games is the use of cascading where

PS> get-process | where {$_.CPU -gt 50 } | where {$_.Handles -gt 500 }

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
   1953     178   222312     217496   689    96.52   3872 wlmail

logically this is equivalent of where {$_.CPU -gt 50 -and $_.Handles -gt 500 }

Is there any difference in speed

1..100 | foreach {
Measure-Command -Expression {get-process |
where {$_.CPU -gt 50 -and $_.Handles -gt 500 }}
} | Measure-Object -Average TotalMilliseconds

produces an answer of 153.92904 milliseconds

1..100 |
foreach {Measure-Command -Expression {get-process |
  where {$_.CPU -gt 50 } | where {$_.Handles -gt 500 }}} |
Measure-Object -Average TotalMilliseconds

gives 152.905894 milliseconds

Not a massive difference. But does the order of the where statements make a difference?

1..100 | foreach {
Measure-Command -Expression {get-process | 
  where {$_.Handles -gt 500 -and $_.CPU -gt 50 }}} |
Measure-Object -Average TotalMilliseconds

produces an answer of 51.495388

1..100 | foreach {
Measure-Command -Expression {get-process |
  where {$_.Handles -gt 500 | where {$_.CPU -gt 50 }  }}} |
Measure-Object -Average TotalMilliseconds

gives 37.23507 milliseconds

Filtering on Handles first produces a quicker result because it is a heavier filter – it produces less out put so the second filter has less work to do. I wasn’t expecting such a big difference between the use of –and & the cascading where statements though – that was a surprise.

So what have learnt:

  • where filter clauses can be combined with –and OR –or
  • where filter clauses can be cascaded in multiple where statements
  • putting the heaviest filter first speeds the process
  • cascading where statements can be quicker than using –and

Be careful with the last statement and test any code before committing to using cascading wheres Also test the order of your filters very carefully.

Leave a Reply