Runtime of Foreach-Object vs Foreach loop

Tags: powershell
Question!

I want to do a progress bar of my script but then I need a total amount of folders.

Is there a significant runtime difference between:

Get-ChildItem $path -Directory | ForEach-Object {
    #do work
}

and

$folders = Get-ChildItem $path -Directory
foreach($folder in $folders){
    #do work
}

Then I can use $folders.Count as my total amount of folders. I don't know how to do it with a foreach-object loop.



Answers

Piping is designed to process items immediately as they appear so the entire length of the list is not known while it's being piped.

Get-ChildItem $path -Directory | ForEach {
    # PROCESSING STARTS IMMEDIATELY
    # LENGTH IS NOT KNOWN
}
  • Advantage: processing starts immediately, no delay to build the list.
  • Disadvantage: the list length is not known until it's fully processed

On the other hand, assigning the list to a variable builds the entire list at this point, which can take an extremely large amount of time if the list contains lots of items or it's slow to build, for example, if it's a directory with lots of nested subdirectories, or a slow network directory.

# BUILD THE ENTIRE LIST AND ASSIGN IT TO A VARIABLE
$folders = Get-ChildItem $path -Directory
# A FEW MOMENTS/SECONDS/MINUTES/HOURS LATER WE CAN PROCESS IT
ForEach ($folder in $folders) {
    # LENGTH IS KNOWN: $folders.count
}
  • Advantage of building the list + ForEach statement: overall time spent is less because processing { } block is not invoked on each item whereas with piping it is invoked like a function or scriptblock, and this invocation overhead is very big in PowerShell.
  • Disadvantage: the initial delay in the list assignment statement can be extremely huge
By : wOxxOm


You can check for yourself:

Measure-Command {
    1..100000 | ForEach-Object $_
}

1.17s

Measure-Command {
    foreach ($i in (1..100000))
    {
    $i
    }
}

0.15s
By : Avshalom


Yes, there is a performance difference. foreach is faster than ForEach-Object, but requires more memory, because all items ($folders) must be in memory. ForEach-Object processes one item at a time as they're passed through the pipeline, so it has a smaller memory footprint, but isn't as fast as foreach.

See also.



This video can help you solving your question :)
By: admin