'Powershell performance : nested foreach vs foreach in series

I am doing some performance profiling on registry search, and I have found a definite performance difference between these two loop structures, and I wonder what is going on under the hood to explain it? From a readability standpoint I prefer the nested foreach (Loops2) over the loops in series (Loops1). However, Loops1 consistently runs in about 55% of the time of Loops2. Now, to be honest I don't know that it REALLY matters, given that I am running each loop 100 times and the slow one is still under 1 second (.88) and in actual use I will likely not run the loop even half as many times as that, often much lower. But I feel like just the fact that I am doing the performance profiling is a real improvement in my approach, and understanding WHY one performs better than the other would be valuable too. I may well use the slower approach because of readability, but perhaps there is something in the why that will convince me otherwise.

$regKeys = @('Registry::HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall', 
             'Registry::HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall')
$regKeyNames = @('SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', 
                 'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall')
$searchPropertyValue = 'Microsoft Visual C++ 2012 Redistributable (x64) - 11.0.61030'

Write-Host "`r`nLoops1 " -noNewLine
(Measure-Command {
    foreach ($i in 1..100) {
        $installedPrograms = Get-ChildItem -Path:$regKeys[0]
        :x64 foreach ($installedProgram in $installedPrograms) {
            if ($installedProgram.GetValue('DisplayName') -eq $searchPropertyValue) {
                $found = $installedProgram
                break x64
            }
        }
        if (-not $found) {
            $installedPrograms = Get-ChildItem -Path:$regKeys[1]
            :x32 foreach ($installedProgram in $installedPrograms) {
                if ($installedProgram.GetValue('DisplayName') -eq $searchPropertyValue) {
                    $found = $installedProgram
                    break x32
                }
            }
        }
    }
}).TotalSeconds
Write-Host "$found"

Write-Host "`r`nLoops2 " -noNewLine 
(Measure-Command {
    foreach ($i in 1..100) {
        :search foreach ($key in $regKeys) {
            $installedPrograms = Get-ChildItem -Path:$key
            foreach ($installedProgram in $installedPrograms) {
                if ($installedProgram.GetValue('DisplayName') -eq $searchPropertyValue) {
                    $found = $installedProgram
                    break search
                }
            }
        }
    }
}).TotalSeconds
Write-Host "$found"

Write-Host "`r`n[Microsoft.Win32.RegistryHive] " -noNewLine 
(Measure-Command {
    $localMachineHive = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, 0)


foreach ($i in 1..100) {
        :search foreach ($keyName in $regKeyNames) {
            if (($installedProgram = $localMachineHive.OpenSubKey("$keyName\$uninstallKeyName")).GetValue('DisplayName') -eq $searchPropertyValue1) {
                $found = $installedProgram
                break search
            }
        }
    }
}).TotalSeconds
Write-Host "$found"


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source