Powershell: Slow module load caused by inefficient PSUtils.GetParentProcess

Created on 1 Nov 2016  路  17Comments  路  Source: PowerShell/PowerShell

The current implementation for getting the parent id of the current process if extremely slow:
The parent Id is not even cached, but is retrieved over and over again. On several systems I see module load times of 5+ seconds, where almost all of the time is spent blocking on the wmi call, waiting for WmiPrvSe to enumerate all processes.
The Id of the process that created us will not change, so cache it and/or use a more efficient method of getting the parent id.

The check for process start time would still have to be done (not in the example).

/// suggested alternative (about 100 times faster)
public static class ProcessInfoUtil
{
    public static System.Diagnostics.Process GetParentProcess() { return ParentProcessId == 0 ? null : System.Diagnostics.Process.GetProcessById(ParentProcessId); }

    public static readonly int ParentProcessId = GetParentProcessId();

    private static int GetParentProcessId()
    {
        var pi = new PROCESS_BASIC_INFORMATION();
        int actual;
        if (0 == NativeMethods.NtQueryInformationProcess(new IntPtr(-1), 0/*processbasicInformation*/, ref pi, pi.Size, out actual))
        {
            return (int)pi.InheritedFromUniqueProcessId;
        }
        else 
        {
            return 0;
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct PROCESS_BASIC_INFORMATION
    {
        public IntPtr ExitStatus;
        public IntPtr PebBaseAddress;
        public IntPtr AffinityMask;
        public IntPtr BasePriority;
        public UIntPtr UniqueProcessId;
        public IntPtr InheritedFromUniqueProcessId;

        public int Size { get { return Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION));}}
    }

    static class NativeMethods
    {
    [DllImport("NtDll", SetLastError=true)]
    public static extern int NtQueryInformationProcess(IntPtr ProcessHandle, int processInformationClass, ref PROCESS_BASIC_INFORMATION ProcessInformation, int processInformationLength, out int returnLength);
    }
}
/// this is the current implementation
 string wmiQuery = String.Format(CultureInfo.CurrentCulture,
                                            "Select * From Win32_Process Where Handle='{0}'",
                                            current.Id);

            using (CimSession cimSession = CimSession.Create(null))
            {
                IEnumerable<CimInstance> processCollection =
                    cimSession.QueryInstances("root/cimv2", "WQL", wmiQuery);

                int parentPid =
                    processCollection.Select(
                        cimProcess =>
                        Convert.ToInt32(cimProcess.CimInstanceProperties["ParentProcessId"].Value,
                                        CultureInfo.CurrentCulture)).FirstOrDefault();

                if (parentPid == 0)
                    return null;
...
Resolution-Fixed WG-Engine

Most helpful comment

Since upgrading to PSVersion 5.1.14409.1005 launching powershell.exe seems to launch almost immediately. It looks promising that the worst manifestations of this problem that I was seeing are resolved. Thanks to whomever made that happen. 馃憤

All 17 comments

Just synced head and saw that a faster implementation is committed instead of the WMI one.

NtQueryInformationProcess will still be much faster, but it is perhaps fast enough already.

@SteveL-MSFT Please comment

The WMI Query was replaced. If @powercode believes NtQueryInformationProcess is faster, I would suggest prototyping it and measuring the perf. I do agree that caching it would be something we should do eventually.

MSDN say:

NtQueryInformationProcess may be altered or unavailable in future versions of Windows.

So perhaps it's enough to make caching.

I am working on a pull request for the caching. Promising results so far.

@powercode should I assign this to you since you are working on it?

@SteveL-MSFT I submitted a pull request that referenced this issue. #2588

From my point of view, this is done. Are there any remaining issues?

@powercode once the PR is merged, this issue should be closed

We're just waiting on final sign-off from @LeeHolmes.

How long does it take for a fix like this to reach a FullClr release?

I have a startup time of PowerShell that often exceeds half a minute, since our IT department added ExecutionPolicy ByPass in a GPO.

Loading personal and system profiles took 32682ms.

It really is painful!

@powercode the change to replace the WMI query was accepted into servicing already so that should be available in Dec (I believe early flights can get it in Nov as preview). The caching code you provided is not planned to be taken back for PS5.

Well, at least there will be an improvement!

@powercode Thank you very much for bringing this to my attention.

@powercode the change to replace the WMI query was accepted into servicing already so that should be available in Dec (I believe early flights can get it in Nov as preview).

@SteveL-MSFT Can you tell me how I can tell whether this change has been applied to a computer?

ccmexec.exe (the System Center Configuration Manager client) currently suffers terribly from the slow loading of PowerShell.exe. ccmexec.exe can use powershell scripts as detection methods for Applications. ccmexec.exe launches a new powershell.exe for each script. Some of our clients run about 300 hundred of these powershell detection scripts on each detection run. That means that ccmexec.exe spends approximately 120 minutes executing 300 detection scripts on an i7 Surface Pro 3. Because powershell.exe (or something it invokes) pins one of the CPUs that represents about 70% of the battery life of the device just to detect which applications are installed. I'm really hopeful that this fix will solve it, and I'd like to test it (and the caching), if possible.

@alx9r I believe it's rolled into the patch Tuesday this month, so if you get the latest updates, I believe you should have it (powershell.exe will have a more recent date).

Since upgrading to PSVersion 5.1.14409.1005 launching powershell.exe seems to launch almost immediately. It looks promising that the worst manifestations of this problem that I was seeing are resolved. Thanks to whomever made that happen. 馃憤

I came across this issue while troubleshooting slow script execution (powershell -NoProfile -ExecutionPolicy Bypass -file path\to\script.ps1) in PowerShell (5.1.17134.165). I narrowed it down to Get-ExecutionPolicy being slow (~5 sec); and it got resolved by restarting the computer.

Judging from the commit here it seems that if one has the GPO setting for ExecutionPolicy (i.e. Get-ExecutionPolicy -List displays MachinePolicy as something other than Undefined), powershell still does the parent process check when first executing a script, which can take a few seconds (with many processes running?).

Was this page helpful?
0 / 5 - 0 ratings