39

I have slow PowerShell console startup times (always more than 5 second wait) and was hoping for advice on troubleshooting steps to find out where the bottlenecks might be?

I have read that for running scripts, -NoProfile is important to prevent Modules etc loading, but how, in general, should we approach finding out what is slowing things down? I don't have many Modules installed and I know that since PowerShell 3.0, Modules are just referenced at startup and not fully loaded (a Module is only fully loaded when a function from a given Module is invoked) so I just can't understand why it takes 5+ seconds to start a bare console (my $profile also is empty).

Any advice on various steps that I can look at to debug the console startup process would be appreciated? Also, are there maybe some Microsoft or third-party tools that exist to debug the various steps in the console startup process to look for bottlenecks?

3
  • See Powershell slow starting on Windows 10
    – Shivam Jha
    Commented Aug 16, 2021 at 9:37
  • Update: updating to windows 11 fixed startup time from 4+ seconds to ~0.5 seconds
    – Shivam Jha
    Commented Jan 1, 2022 at 14:21
  • That's interesting, most of my machines fail the upgrade compatibility check, think it's all about TPM or something. But a lot of hardware has been made redundant by Microsoft's Win 11 bullying unfortunately :(
    – YorSubs
    Commented Jan 9, 2022 at 19:36

10 Answers 10

37

When PowerShell starts to become slow at startup, an update of the .NET framework might be the cause. To speed up again, use ngen.exe on PowerShell's assemblies. It generate native images for an assembly and its dependencies and install them in the Native Images Cache.

Run this as Administrator

$env:PATH = [Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()
[AppDomain]::CurrentDomain.GetAssemblies() | ForEach-Object {
    $path = $_.Location
    if ($path) { 
        $name = Split-Path $path -Leaf
        Write-Host -ForegroundColor Yellow "`r`nRunning ngen.exe on '$name'"
        ngen.exe install $path /nologo
    }
}

Hope that helps

10
  • 2
    Thanks for that Theo, but that did not affect my startup times at all (I also did a full reboot afterwards in case that might help, but no luck). This is useful as part of steps to take though, maybe it helps other folks. Any other ideas for seeing why console startup is slow?
    – YorSubs
    Commented Dec 15, 2019 at 14:22
  • @YorSubs I found some other things you can try here
    – Theo
    Commented Dec 16, 2019 at 20:25
  • 10
    Thanks Theo, that is a very interesting article and I've gone through them all but no luck. I might have regained 0.5 seconds on startup from following all of these, but it's still remarkably slow (by remarkable, I find it really bad that opening a console on a modern OS with a very fast modern i5 CPU is taking 5+ second open - I mean, if a console is not opening in less than 1 second, that's not a good advertisement for PowerShell to say the least!).
    – YorSubs
    Commented Dec 18, 2019 at 16:13
  • 1
    @Theo It worked for my case, after running the script it loads in 1 sec. Commented May 14, 2021 at 21:55
  • 1
    Awesome, oldi but goldie. I have a .net App, that calls various powershell-scripts, 5-20 every second. CPU during spawning the processes was on average 75% on 16 cores. After optimizing, Startup of powershell is noticeable faster, now keeping the cores chilled at about 8-10% average load.
    – dognose
    Commented Sep 16, 2023 at 20:48
7

My work computer stored the main profile on a remote server. Another minor problem was that it imported duplicate modules from 4 different profile.ps1 files.

Use the following commands to see where your profiles and modules are stored. Delete the unnecessary profile.ps1 and move all your modules into one directory.

echo $env:PSModulePath

$profile | select *

My loading time was reduced from 21000ms to 1300ms.

1
  • It never really occured to me that the reason for the slowness was onedrive. I thought I had disabled Syncing the documents folder ages ago, and your answer pointed me in the right direction. Commented Dec 6, 2023 at 5:16
7

in my case, it was Anaconda that caused the loading of my profile to take long time. After I removed this region:

#region conda initialize
# !! Contents within this block are managed by 'conda init' !!
(& "C:\Users\USERNAME\anaconda3\Scripts\conda.exe" "shell.powershell" "hook") | Out-String | Invoke-Expression
#endregion

from my C:\Users\USERNAME\OneDrive\Documents\WindowsPowerShell[profile.ps1|Microsoft.PowerShell_profile.ps1] paths, the loading became instant.

But, of course, you must know it won't cause any problems in your case.

6

Yes, pwsh tends to be slow.

Check your profiles. It's better for startup time only one to exist. Some apps (like conda) like to add something to a shared profile. Clean if something is not yours.

$profile | Select-Object -Property *

Use Runspaces to make your module imports lazily loaded. We can import a module in a different (background) thread and then use that already loaded module in our session. Look at the code below:

$LazyLoadProfileRunspace = [RunspaceFactory]::CreateRunspace()
$LazyLoadProfile = [PowerShell]::Create()
$LazyLoadProfile.Runspace = $LazyLoadProfileRunspace
$LazyLoadProfileRunspace.Open()
[void]$LazyLoadProfile.AddScript({Import-Module posh-git}) # (1)
[void]$LazyLoadProfile.BeginInvoke()
$null = Register-ObjectEvent -InputObject $LazyLoadProfile -EventName InvocationStateChanged -Action {
    Import-Module -Name posh-git # (2)
    $global:GitPromptSettings.DefaultPromptPrefix.Text = 'PS '
    $global:GitPromptSettings.DefaultPromptBeforeSuffix.Text = '`n'
    $LazyLoadProfile.Dispose()
    $LazyLoadProfileRunspace.Close()
    $LazyLoadProfileRunspace.Dispose()
}

As you can notice we have to describe modules we want to lazy load twice. First time for a background thread that will be actually loading it and the second for our actual thread. Ofc don't forget to clean things up at the end (close & dispose used).

This method is applicable only for modules or global variables - all your background thread-local variables/functions/etc won't come to your console session.

Note: It is a workaround. It's not truly lazy - we just put things with heavy time impact in the background and when it's ready we start using it (It's more of delayed initialization). That's why if you have something you need to use immediately after the shells console pop-up then you shouldn't use this technique.

Optional 1: For further investigation you can use PSProfiler.

Install-Module PSProfiler
Import-Module PSProfiler
Measure-Script -Top 3 $profile

Optional 2. Generate native images. This one didn't help me, but helped others. You need to have Visual Studio installed and run the code below in Developer Powershell for VS 20xx with Admin permissions:

$env:PATH = [Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()
[AppDomain]::CurrentDomain.GetAssemblies() | ForEach-Object {
    $path = $_.Location
    if ($path) { 
        $name = Split-Path $path -Leaf
        Write-Host -ForegroundColor Yellow "`r`nRunning ngen.exe on '$name'"
        ngen.exe install $path /nologo
    }
}

Optional 3. Add powershell.exe and pwsh.exe or both to Windows Defender exclusions. Didn't help me but helped others.

2

Found this solution when I googled having the same problem, but in 2022. Unfortunately this did not fix my issue.

Our systems have a group policy security requirement to "Turn on PowerShell Transcription". The policy requires that we specify "the Transcript output directory to point to a Central Log Server or another secure location". The server name changed and no one updated the policy. As soon as I updated the GPO with the new location, PowerShell opened instantly again.

2

If your machine does not have direct access to the internet, the following solution may help.

This solution is lifted from comments on this PowerShell Issue summarised by eizedev.

I'd recommend just following this comment but for the sake of SO guidelines, I've reproduced some of it here:

Cause

For machines disconnected from the internet, there is still an attempt retrieve a Certificate Revocation list from the internet, and this request must time-out before the PowerShell start-up process completes.

In particular this is during the import of module PSReadLine

Solution

Consider methods to make these lists available to machines without direct internet access. (eg a configure a Certificate Revocation List Distribution Point?)

Workaround

The workaround, is to reduce time-outs from 15 seconds to 1 second for any system certificate checks on the disconnected machines.

WARNING this change is system wide - it doesn't just affect PowerShell.

Please see the linked PowerShell issue thread for complete considerations before executing this.

# Create the keys if missing 
If((Test-Path 'HKLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\ChainEngine') -eq $false ) { New-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\ChainEngine' -Force -ErrorAction SilentlyContinue }
If((Test-Path 'HKLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\ChainEngine\Config') -eq $false ) { New-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\ChainEngine\Config' -Force -ErrorAction SilentlyContinue }

# Set Timeout values to 1 second (1000 ms)
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\ChainEngine\Config" -Name ChainUrlRetrievalTimeoutMilliseconds -Value 1000 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\ChainEngine\Config" -Name ChainRevAccumulativeUrlRetrievalTimeoutMilliseconds -Value 1000 -PropertyType DWORD -Force
2

If it helps anyone, I had an approximate 6.5 seconds to sometimes even peaks of 9 to 12 seconds of loading when opening the terminal.

I usually use the terminal only for SSH connections, winget, node, and few other things.

Doing some research, I found that a solution can be to disable the loading of all modules when you open the terminal and importing only the modules you require. This reduced the loading time from the normal 6/7 seconds to 800ms.

It can be done this way:

  • Open PowerShell and run:

    echo $PROFILE

This will return the path to your PowerShell profile, find that file and open it.

  • Now go back to PowerShell and run:

    Get-Module -ListAvailable

This will return a complete list of absolutely all the modules that are loaded and those that are not. From this list you should take the name of the modules that you require when you are using PowerShell.

  • Now, back inside the PowerShell profile file, add the following line to it:

    $PSModuleAutoLoadingPreference = 'None'

This will disable the automatic loading of all modules when PowerShell is opened.

  • Below this line, you will import the modules you require as follows:

    Import-Module [module-name]

I recommend importing:

Import-Module Microsoft.PowerShell.Utility

Import-Module Microsoft.PowerShell.Management

Since they are the basic modules for PowerShell.

After that simply restart PowerShell and see if it is effective, as I said at the beginning, this was effective for me and everything is still working correctly as I require. You will have to adapt it to your needs.

1

In my case, it was taking ~2 seconds to load and no time at all with -NoProfile option.

What worked for me is to remove from the profile (editing it with vim $PROFILE) file all lines that started with Import-Module, which I guess were not necessary because all the modules work anyway.

I'm pretty new to Powershell, so I'm not sure what that means, but now it loads fast, it doesn't even show the time it takes.

-1

Step 1: Stop using PowerShell.

Now, seriously, something that needs ~13 seconds (YMMV) on an quad-core i7 cpu to launch off an ssd drive is an abomination of software architecture.

But yes, I hear you, "no viable alternative" etc...


... but if forced, bribed or blackmailed to still use it, check if your Windows has DNS cache service enabled.

For me, with DNS cache disabled and powershell executable firewalled, the built-in 5.1.19041.906 version starts quickly, but the new pwsh 7.1.4 would take around 13 seconds to get responsive to keyboard input under the same circumstances. It's so desperate to call home that it would just synchronously wait for some network timeout while blocking all user input, as if threads were a thing for the weak.

My resolution was to stick with the olden powershell 5.

4
  • 9
    Is there any (e.g. third-party) replacement for PowerShell? I got to this article searching for a solution to a specific problem and expect the answers to contain useful solutions.
    – Jaroslav K
    Commented Aug 9, 2021 at 11:53
  • 1
    @JaroslavK good alternatives now exist. I've tried Cmder, Tabby, and Hyper. Another suggestion is to install PowerShell 7 from their GitHub repo; it integrates with other tools more easily. And the neat thing is even when using one of these replacements to call PowerShell underneath, it still starts an order of magnitude faster than PowerShell does by itself. Commented Apr 22, 2022 at 19:49
  • Windows Subsystem for Linux is a good alternative. Commented May 11, 2022 at 18:58
  • 1
    @AdityaWagh that is not a good alternative to automating windows tasks. Powershell unifies a lot of diverse APIs that interact natively with a myriad of services. Also, in terms of performance, powershell may take a while to start up sometimes, but dealing with managed. NET objects without having to serialize and deserializing them between separate command line utilities launched in separate processes is going to be faster than any posix shell.
    – PC Luddite
    Commented Apr 5, 2023 at 2:53
-4

Press Windows+R Type %temp% and hit enter C+A & SHIFT+DEL That should do it

4
  • 1
    Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Jan 31, 2022 at 18:45
  • It is mandatory to empty the trash bin first though!
    – sunny moon
    Commented Apr 1, 2022 at 13:33
  • Your answer implies that temporary files are cause for the slowness to start... which is one of the myriads of potential causes.
    – HiperiX
    Commented Aug 19, 2022 at 14:25
  • This doesn't fix anything. Not sure why deleting temp files has anything to do with slow startup of PowerShell.
    – l1e3e3t7
    Commented Sep 18, 2023 at 15:41

Not the answer you're looking for? Browse other questions tagged or ask your own question.