Search code examples
powershellcpu-usage

High CPU usage & "Very High" Power usage with PowerShell Start-Sleep


I created a PowerShell script in Windows 10 to display an icon in the system tray and change the icon depending on whether or not Caps Lock is on. (I have a keyboard that has no indicator for Caps Lock). The script is quite simple (like me) but involves an infinite loop so it will continuously check.

When I use the following line to determine the loop interval the PowerShell instance uses 16% CPU and "Very High" Power usage according to Task Manager:

Start-Sleep -Seconds 0.5

However when I use the following line to determine the loop interval the PowerShell instance uses ~0% CPU and "Very Low" Power usage according to Task Manager:

Start-Sleep -Milliseconds 500

This, in spite of the fact that the time intervals are the same. Needless to say, I am going with the second option (I even reduced it to 200ms, better response and still no issues).

I was wondering, does anyone know why using fractions of "Seconds" causes such a radical increase in CPU usage? (I am guessing that the relatively high CPU usage is where the "Very High" power using is coming from).

I thought maybe it is because the CPU is converting data types, although it still seems a little extreme to me, so I tried:

Start-Sleep -Seconds 1.5 

This resulted in ~0% CPU and "Very Low" Power usage according to Task Manager....

Any ideas?


Solution

  • Preface:

    • The answer below assumes that you're using PowerShell (Core) 7+, because the legacy PowerShell edition, Windows PowerShell, doesn't support fractional seconds with Start-Sleep's -Seconds parameter (it quietly converts to [int], which, in the case of 0.5, means that 0 is effectively used, and, in the case of 1.5, due to implicit half-to-even rounding, it is 2); it does, however, support the -Milliseconds parameter.

    • In other words:

      • In Windows PowerShell, if you want sub-second granularity, you must use -Milliseconds, not -Seconds, e.g. -Milliseconds 500 rather than -Seconds 0.5

        • As noted, in Windows PowerShell Start-Sleep -Seconds 0.5 is the same as Start-Sleep -Seconds 0, which means that you're not sleeping at all, and creating only unnecessary overhead - this would explain your high CPU utilization.
      • In PowerShell (Core) v7+, you're free to use either parameter, because the -Seconds parameter is now [double]-typed and therefore accepts fractional values (see below).


    If you're observing an unusual CPU spike in PowerShell (Core) 7+, you're most likely seeing a momentary measuring artifact: there should be no appreciable difference in performance between -Seconds 0.5 and -Milliseconds 0.5:

    As the source code shows, the only extra work that use of -Seconds incurs (vs. -Milliseconds) is the following single statement:

    (int)(Seconds * 1000);
    

    That is, the -Seconds value, which is of type [double], is converted to milliseconds (as an [int] value), and it is that millisecond value that is ultimately passed to the underlying .NET API, the System.Threading.Thread.Sleep method.

    Note that PowerShell, especially shortly after startup, may be performing background operations that impact its overall CPU utilization.