1

I expected the following code to complete in about one second. It executes in about 20 seconds:

$i = 0; do{sleep -Milliseconds 1; $i=$i+1}while($i -lt 1000)

Could you please suggest why? I'm not able to find any clues in docs.
Thanks in advance!

Andrew
  • 71
  • 6

1 Answers1

3

Calling a cmdlet comes at a cost. Just because you use Start-Sleep -Milliseconds 1, doesn't mean it's going to take 1ms. This is because that cmdlet has overhead it needs to take care of behind the scenes, like setting up the timer, instantiating objects, etc.

Measure-Command { Start-Sleep -Milliseconds 1 }

# TotalMilliseconds : 25.1157

See the above...even though I told it to only run for 1ms, it still took 25ms because of the overhead. This overhead won't be exactly the same every time, but you should always expect there to be some.

On my computer, it seems to average about 16ms of overhead per call. So if you run that 1000 times, then on average, it's going to take 16 seconds to run, just for the sleep alone.

I obtained the average by running this a few times:

Measure-Command { 1..100 | % { Start-Sleep -Milliseconds 1 } }

It's like driving a car. You don't just hop in a car and go, you need to start it up first, and there's things going on behind the scenes the car needs to do in order to start. And that takes a little bit of time.

Chad Baldwin
  • 2,239
  • 18
  • 32
  • Can you suggest a way to sleep a precise time in milliseconds in powershell? – Andrew Dec 14 '20 at 19:08
  • 2
    @Andrew the short answer is pretty much a no. Please see this answer for a more in-depth response: https://stackoverflow.com/a/6254753/3474677 – Chad Baldwin Dec 14 '20 at 19:29
  • 1
    I guess my question is, if you need something so precise to the point where 10ms makes or breaks your code, why use a scripting language like powershell? You may be able to find some other commands like @mklement0 recommended which has less overhead...but it still won't be perfect. – Chad Baldwin Dec 14 '20 at 19:35
  • Interestingly, with both PS 5 and PS 7.2 preview I get nearly same avg. times between `Start-Sleep -milliseconds 1` and `[System.Threading.Thread]::Sleep( 1 )`, executed 1000 times. Start-Sleep: 15,6 ms, Sleep: 15,5 ms. Test code: `1..1000 | %{ (Measure-Command { Start-Sleep -Milliseconds 1 }).TotalMilliseconds } | Measure -Average` – zett42 Dec 14 '20 at 19:36
  • The 16 ms look to me more like the inherent precision of the underlying timing mechanism, not the overhead of calling a PS cmdlet. Otherwise there should be much greater difference between the cmdlet and the .NET API. – zett42 Dec 14 '20 at 19:44
  • @mklement0 Getting almost the same results with `Time-Command` on both PS 5 and PS 7.2 preview, on Win 10. It shows a factor of 1.01 for `Start-Sleep`. – zett42 Dec 14 '20 at 19:56
  • 1
    Thanks for checking, @zett42. The short of it: On Windows, you cannot sleep shorter than around 12 msecs., consistent with the answer Chad linked to. On Unix (Linux, macOS) sleeping for 1 msecs. _is_ possible, but the relatively lower overhead of `[System.Threading.Thread]::Sleep` becomes irrelevant at around 10 msecs. Curiously, the absolute overhead for the calls is much higher on Windows. Ergo: In most cases `[System.Threading.Thread]::Sleep` won't help. – mklement0 Dec 14 '20 at 22:46