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
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.
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
}
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
}
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.You can check for yourself:
Measure-Command {
1..100000 | ForEach-Object $_
}
1.17s
Measure-Command {
foreach ($i in (1..100000))
{
$i
}
}
0.15s