Prerequisites
Steps to reproduce
Сreate a test directory and a tree of subdirectories within it:
.\config
.\release\PowerShell-7.6.4-win-x64
.\release\PowerShell-7.6.4-win-fxdependent
.\release\dotnet-runtime-10.0.10-win-x64
Download and extract the installation packages to the appropriate directories:
https://github.com/PowerShell/PowerShell/releases/download/v7.6.4/PowerShell-7.6.4-win-x64.zip (.\release\PowerShell-7.6.4-win-x64)
https://github.com/PowerShell/PowerShell/releases/download/v7.6.4/PowerShell-7.6.4-win-fxdependent.zip (.\release\PowerShell-7.6.4-win-fxdependent)
https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-10.0.10-windows-x64-binaries-zip (.\release\dotnet-runtime-10.0.10-win-x64)
Create a test script:
.\measure_startup_time.ps1
$IterationNumber = 100
# create common empty powershell.config.json
New-Item -Path "${PSScriptRoot}\config\powershell.config.json" -Force | Out-Null
# create common arg list
$ArgumentList = @(
'-NoLogo'
'-NonInteractive'
'-NoProfile'
'-ExecutionPolicy', 'Bypass'
'-SettingsFile', "${PSScriptRoot}\config\powershell.config.json"
'-Command', '#'
)
# create common env
$env:POWERSHELL_TELEMETRY_OPTOUT = 1
$env:POWERSHELL_DISTRIBUTION_CHANNEL = ''
$env:POWERSHELL_UPDATECHECK = 'Off'
$env:POWERSHELL_DIAGNOSTICS_OPTOUT = 1
$env:PSExecutionPolicyPreference = ''
$env:PSModulePath = ''
$env:PSModuleAnalysisCachePath = 'NUL'
$env:PSDisableModuleAnalysisCacheCleanup = 1
# measure portable zip binary pwsh.exe (Self-Contained) :
# https://github.com/PowerShell/PowerShell/releases/download/v7.6.4/PowerShell-7.6.4-win-x64.zip
# remove profile
Get-Item -Path "${env:LOCALAPPDATA}\Microsoft\PowerShell\StartupProfileData-NonInteractive" -ErrorAction 'SilentlyContinue' | Remove-Item
# run test
$SelfContained = 1..$IterationNumber | ForEach-Object {
Write-Progress -Id 1 -Activity 'Self-Contained ' -PercentComplete ( ( $_ / $IterationNumber ) * 100 )
( Measure-Command { & "${PSScriptRoot}\release\PowerShell-7.6.4-win-x64\pwsh.exe" $ArgumentList } ).TotalMilliseconds
} | Measure-Object -Average
# measure portable zip binary pwsh.exe (Framework-Dependent) :
# https://github.com/PowerShell/PowerShell/releases/download/v7.6.4/PowerShell-7.6.4-win-fxdependent.zip
# on .NET Runtime 10.0.10 :
# https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-10.0.10-windows-x64-binaries-zip
# set path to dotnet runtime
$env:DOTNET_ROOT = "${PSScriptRoot}\release\dotnet-runtime-10.0.10-win-x64"
# remove profile
Get-Item -Path "${env:LOCALAPPDATA}\Microsoft\PowerShell\StartupProfileData-NonInteractive" -ErrorAction 'SilentlyContinue' | Remove-Item
# run test
$FrameworkDependent = 1..$IterationNumber | ForEach-Object {
Write-Progress -Id 2 -Activity 'Framework-Dependent' -PercentComplete ( ( $_ / $IterationNumber ) * 100 )
( Measure-Command { & "${PSScriptRoot}\release\PowerShell-7.6.4-win-fxdependent\pwsh.exe" $ArgumentList } ).TotalMilliseconds
} | Measure-Object -Average
# report
Write-Host ( 'IterationNumber = ' + $IterationNumber.ToString() )
Write-Host ( 'Self-Contained Average Satrtup Time (milliseconds) = ' + $SelfContained.Average.ToString( '0' ) )
Write-Host ( 'Framework-Dependent Average Satrtup Time (milliseconds) = ' + $FrameworkDependent.Average.ToString( '0' ) )
Write-Host ( 'Diff = ' + ( ( ( $FrameworkDependent.Average - $SelfContained.Average ) / $SelfContained.Average ) ).ToString( 'P0' ) )
Run the test script (.\measure_startup_time.ps1) from the PowerShell 7.6 (LTS) command line (do not use PowerShell from the .\release\PowerShell* for this).
Expected behavior
Actual behavior
IterationNumber = 100
Self-Contained Average Satrtup Time (milliseconds) = 190
Framework-Dependent Average Satrtup Time (milliseconds) = 418
Diff = 120 %
Error details
The reason is presumably that the ReadyToRun compilation is not used during the framework dependent release . See the discussion in #14121.
dotnet-trace data in speedscope format; it can be viewed on the web at speedscope.app
Self-Contained:
pwsh_(sc).speedscope.json
Framework-Dependent
pwsh_(fd).speedscope.json
Environment data
Name Value
---- -----
PSVersion 7.6.4
PSEdition Core
GitCommitId 7.6.4
OS Microsoft Windows 10.0.26100
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.4
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Visuals
Trace data screenshots in Speedscope "Left Heavy" mode showing that .NET code executes 2x slowly:
Self-Contained:
Framework-Dependent:

Prerequisites
Steps to reproduce
Сreate a test directory and a tree of subdirectories within it:
.\config
.\release\PowerShell-7.6.4-win-x64
.\release\PowerShell-7.6.4-win-fxdependent
.\release\dotnet-runtime-10.0.10-win-x64
Download and extract the installation packages to the appropriate directories:
https://github.com/PowerShell/PowerShell/releases/download/v7.6.4/PowerShell-7.6.4-win-x64.zip (.\release\PowerShell-7.6.4-win-x64)
https://github.com/PowerShell/PowerShell/releases/download/v7.6.4/PowerShell-7.6.4-win-fxdependent.zip (.\release\PowerShell-7.6.4-win-fxdependent)
https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-10.0.10-windows-x64-binaries-zip (.\release\dotnet-runtime-10.0.10-win-x64)
Create a test script:
.\measure_startup_time.ps1
Run the test script (.\measure_startup_time.ps1) from the PowerShell 7.6 (LTS) command line (do not use PowerShell from the .\release\PowerShell* for this).
Expected behavior
Diff = 0 %Actual behavior
Error details
The reason is presumably that the
ReadyToRuncompilation is not used during the framework dependent release . See the discussion in #14121.dotnet-trace data in speedscope format; it can be viewed on the web at speedscope.app
Self-Contained:
pwsh_(sc).speedscope.json
Framework-Dependent
pwsh_(fd).speedscope.json
Environment data
Visuals
Trace data screenshots in Speedscope "Left Heavy" mode showing that .NET code executes 2x slowly:
Self-Contained:
Framework-Dependent: