Jump to content
Welcome to our new Citrix community!
  • 1

Speed up Powershell Script launching


Joe Robinson

Question

Good Morning:

 

Powershell has always been a little slow to start.  For this reason, I've stayed away from publishing applications using powershell as a launcher.  It always seems to add a few seconds to the launch time of the application launch, and in a XenApp environment, I really want my apps to pop right open as fast as possible.  For this reason, I've been sticking with batch and vbscripts for launch scripts.

 

If you're not sure what I mean by launch script -- it's a script used to launch an application instead of publishing the application directly.  It's often used to setup an environment before the application launches, such as configuring an app, before the user launches it the first time.

 

I decided it was time to investigate why it was slower, and what I came up with is two tips to make your apps launch faster.  In my environment, this shaved ~4 seconds off the first launch of an application.  

 

For the purpose of this tip, we're going to assume that we were previously publishing the following:

 

Application:  powershell.exe

Arguments: -file \\server\share\myscript.ps1

 

 

The first thing you'll want to do is add the -noprofile switch to your arguments.  When you start powershell, it looks in certain locations to see if a file exists.  If it does, it executes it.  This is a great way to make your shell roam across different environments.  The file that it looks for is in your documents folder, which is probably redirected out to a network share.  

 

By adding the -noprofile switch to powershell, it won't go looking for these files, and it's going to prevent two calls out to the network (or wherever your documents are stored).  The amount of time this saves you is probably not noticeable, but it could be if a power user created that file and put it in their home directory.   You might even feel a little more secure with this in place.

 

 

The second thing is to add a reference to any module that you are going to use.  To be sure you've got them all, start a fresh powershell session and run your script.  Once it's complete, do a get-module | select name.  Any module that is displayed should be imported at the beginning of your script [import-module <modulename>].  It's *VERY& important that you don't use tab completion, or you'll get a false list of requirements.  Using tab completion will cause any modules you have to be analyzed, and you don't want this to happen!  

 

If you're interested, here's the why;

 

Powershell has an autoload feature that will search any known location for modules.  This gets triggered when you run a command that isn't known to powershell, or use tab completion.  Once this is triggered, you'll see files written to %localappdata%\Microsoft\Windows\Powershell\CommandAnalysis.  It appears that the information is stored in memory for the session, and cached for any sessions of powershell that are launched later.  

 

In my investigation, this is what really slows down launching powershell scripts.  On my machine, if I run a command without importing the module, it takes about 5 seconds and creates 97 files in this folder.

 

If I delete all these files, run powershell, import the module, and run the same exact command, the process takes 12ms and creates two files.

 

Let me repeat that:

without importing the module:  4s 850ms.

with importing the module: 12ms

 

 

 

 

If you skipped down to this part and don't want the why, here is what you need to do:

* Import any module you will be using, even the base modules and any dependencies

* add the -noprofile list to your powershell.exe arguments

 

 

Hope this helps someone!  I'm not entirely ready to switch over to powershell, but this discovery is going to make me re-evaluate the next launch scripts I have to write!

 

 

NOTE:  This behavior appears to go away in WMF 5.1, but it hasn't been released for production yet.

Link to comment

4 answers to this question

Recommended Posts

  • 1

Awesome advice, thank you!

 

Going off of that, I did this to speed up my scripts:

 

Updating native images:
 

Set-Alias ngen (Join-Path ([Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()) ngen.exe)
ngen update

Then adding this to the beginning of all my scripts to block auto-loading modules (so we're only using the ones we're manually importing):

$script:PSModuleAutoLoadingPreference = "None"

 

  • Like 1
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...