Recent

Author Topic: Sleep(1) in thread's loop can reduce CPU usage much  (Read 3187 times)

kinlion

  • New Member
  • *
  • Posts: 45
  • I Love Lazarus
Sleep(1) in thread's loop can reduce CPU usage much
« on: May 17, 2019, 09:31:22 am »
I create a thread to listen to a pipe.
I found that if I put "sleep(1);" in thread.Execute's while-loop, the CPU usage of the application is nearly 0%, but when I put "yield;" instead, the CPU usage if about 25%.
It looks that let the thread sleep a minest while, say 1ms, is better than yield it.
BTW, I have tested that sleep(0) is similar to yield.

So, I think if a thread is NOT very time-critical, one should use sleep(1) to reduce the CPU usage.

Code: Pascal  [Select]
  1. procedure TMyThread.Execute;
  2. begin
  3.   while not Terminated do
  4.   begin
  5.     // do something
  6.     ...
  7.  
  8.     sleep(1); // To sleep 1ms will reduce CPU usage much.
  9.   end;
  10. end;
  11.  
Lazarus 1.8.4 / FPC 3.0.4 / SVN 57972
On Win10 X64, have to compile for Win32

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7507
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #1 on: May 17, 2019, 09:37:30 am »
If it is for user interface, sleep(50) or even sleep (150) are good enough.

Rule of thumb is that humans notice interface delays only starting from about 250ms.   

devEric69

  • Full Member
  • ***
  • Posts: 150
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #2 on: May 17, 2019, 10:33:03 am »
Quote
Rule of thumb is that humans notice interface delays only starting from about 250ms.

250ms is indeed for the most awake.
To get an overview of your reflexes (reaction time), you can test it here ^^: https://www.humanbenchmark.com/tests/reactiontime/index.php
« Last Edit: May 17, 2019, 10:37:02 am by devEric69 »
use: Ubuntu 18.04 + Laz. 1.8.5 + FPC 3.0.5 (64 bits).

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 5711
    • wiki
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #3 on: May 17, 2019, 02:04:27 pm »
It depends exactly what you do.
I assume you do a none-blocking check on the pipe? You keep calling that, until you get some data. Then yes that will drive CPU usage up.

If you wait for a single piece of data, then sleep(150) will not hurt.

If you do back and forward communication (i.e. each time you receive something, you send the next bit, which will trigger the next response....) then even Sleep(10) can accumulate. It depends how fast the other side responds.


marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7507
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #4 on: May 17, 2019, 02:20:44 pm »
Yeah. And if you are just polling for a signal, see if you can use a TEvent.

I use industrial cameras on windows, and many of their SDK have a grabmethod that you can give a custom TEvent.handle to signal abort. So you can blok on the grab forever, but still interrupt if you need to stop acquisition or do something else.

WaitforMultiple() are some of the nicest win32 api calls :-)

devEric69

  • Full Member
  • ***
  • Posts: 150
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #5 on: May 17, 2019, 04:44:51 pm »
For  information, the equivalent of the Windows TEvent's methods are (if we need to read or write a string, or bitwise mask of [0, 1] - forming various communication tokens, or something else, on a comm. port, for example):
Code: Pascal  [Select]
  1. cthreads.pp . IntBasicEventCreate
  2. cthreads.pp . IntBasicEventDestroy
  3. cthreads.pp . IntBasicEventWaitFor
  4. .../...

(knowing that today, we can *name* a USB port "comm1" :) )
« Last Edit: May 17, 2019, 04:55:19 pm by devEric69 »
use: Ubuntu 18.04 + Laz. 1.8.5 + FPC 3.0.5 (64 bits).

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7507
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #6 on: May 17, 2019, 04:56:52 pm »
Afaik tevent is perfectly supported on Linux. Just not multiple waits  (waitmultiple), though there is sem_timedwait now.

The procedures that you name are simply the OS dependent interface (and starting with INT as in INTERNAL) for a reason.

devEric69

  • Full Member
  • ***
  • Posts: 150
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #7 on: May 17, 2019, 05:08:41 pm »
For what I know, I just saw TEvent for Windows targeted cross-compilation.

As for the search for CreateEvent, ResetEvent, etc., methods encapsulated in a Windows TEvent component, I didn't see it for a Linux target: I only found equivalent APIs in cthreads.pp.

Is there a TEvent for Linux in Lazarus?
use: Ubuntu 18.04 + Laz. 1.8.5 + FPC 3.0.5 (64 bits).

furious programming

  • Sr. Member
  • ****
  • Posts: 354
  • I click a little.
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #8 on: May 17, 2019, 06:36:31 pm »
I found that if I put "sleep(1);" in thread.Execute's while-loop, the CPU usage of the application is nearly 0%, but when I put "yield;" instead, the CPU usage if about 25%.

Yep, but your Sleep(1) does not freeze the program for 1ms, and for much longer. It probably stops the program for about 16ms. The clock's resolution is not so high, so if you need to increase it, use the TimeBeginPeriod and TimeEndPeriod functions from the MMSystem unit.

But even if you call TimeBeginPeriod(1), the Sleep(1) will pause the program for 2ms — this procedure is not very precise for such small values.

If you want to know what is the possible timer lowest period, use TimeGetDevCaps and use the value from the wPeriodMin field in the TimeBeginPeriod call. Example below:

Code: Pascal  [Select]
  1. uses
  2.   MMSystem;
  3.  
  4.   // field to store the period
  5.   FTimerPeriod: Integer;
  6.  
  7. // somewhere at the beginning of the program code
  8. var
  9.   Periods: TTimeCaps;
  10. begin
  11.   if TimeGetDevCaps(@Periods, SizeOf(Periods)) = TIMERR_NOERROR then
  12.   begin
  13.     FTimerPeriod := Periods.wPeriodMin;
  14.     TimeBeginPeriod(FTimerPeriod);
  15.   end
  16.   else
  17.     FTimerPeriod := -1;
  18. end;
  19.  
  20. // somewhere at the end of the program code
  21. begin
  22.   if FTimerPeriod <> -1 then
  23.     TimeEndPeriod(FTimerPeriod);
  24. end;

I'm using such code in my platformer — changing system timer period is mandatory, works excellent.

Quote
BTW, I have tested that sleep(0) is similar to yield.

Sleep(0) does nothing, does not stop the program, so the processor's power consumption will still be full. The smallest actually working value for Sleep procedure is 1, and the real shortest break is that lasting 2ms.
« Last Edit: May 17, 2019, 06:47:44 pm by furious programming »
Lazarus 2.0.4 with FPC 3.0.4, Windows XP (all 32-bit)

440bx

  • Hero Member
  • *****
  • Posts: 1202
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #9 on: May 17, 2019, 06:47:06 pm »
@kinlion

I found that if I put "sleep(1);" in thread.Execute's while-loop, the CPU usage of the application is nearly 0%, but when I put "yield;" instead, the CPU usage if about 25%.
There is a lot of fun to be had with Sleep(0).  From what you typed, you have a computer with a quad-core.  If your computer had 8 cores, the CPU usage would have been slightly above 12.5%. (presuming the machine is not very busy with other processes.)  Keep digging into Sleep(0) and you'll learn a lot about how Windows works, particularly about how it schedules CPU time to processes.

So, I think if a thread is NOT very time-critical, one should use sleep(1) to reduce the CPU usage.
You got that right :)

using FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7507
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #10 on: May 17, 2019, 08:03:56 pm »
I found that if I put "sleep(1);" in thread.Execute's while-loop, the CPU usage of the application is nearly 0%, but when I put "yield;" instead, the CPU usage if about 25%.

Yep, but your Sleep(1) does not freeze the program for 1ms, and for much longer. It probably stops the program for about 16ms. The clock's resolution is not so high, so if you need to increase it, use the TimeBeginPeriod and TimeEndPeriod functions from the MMSystem unit.

But sleep works using the scheduler timer, probably some HPET thing, and not the RTC.

Quote
Sleep(0) does nothing, does not stop the program, so the processor's power consumption will still be full. The smallest actually working value for Sleep procedure is 1, and the real shortest break is that lasting 2ms.

Please inform Microsoft https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-sleep

Cyrax

  • Hero Member
  • *****
  • Posts: 758
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #11 on: May 18, 2019, 12:48:50 am »
For what I know, I just saw TEvent for Windows targeted cross-compilation.

As for the search for CreateEvent, ResetEvent, etc., methods encapsulated in a Windows TEvent component, I didn't see it for a Linux target: I only found equivalent APIs in cthreads.pp.

Is there a TEvent for Linux in Lazarus?

Yes, Use SyncObjs unit.

furious programming

  • Sr. Member
  • ****
  • Posts: 354
  • I click a little.
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #12 on: May 18, 2019, 01:18:58 am »
But sleep works using the scheduler timer, probably some HPET thing, and not the RTC.

Explain one thing to me. My platformer uses Sleep between frames to make delays (few ms). I checked how it works on Win10. Without using the TimeBeginPeriod function, the game maintains 41fps, and if I call it with a minimum period, it maintains 60fps.

Results of some quick research on timing in Win32 — see the penultimate section. Old article, but helped me a lot.

I know it's not like Sleep(0) actually does anything. My point was that it does not freeze the program/thread, so 25% of the power consumption (100% of the core) is the expected value.

Quote
Please inform Microsoft https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-sleep

You don't have to believe me, just check it:

Code: Pascal  [Select]
  1. uses
  2.   Crt, Windows, MMSystem;
  3. var
  4.   TicksBegin: Int64 = 0;
  5.   TicksEnd: Int64 = 0;
  6.   TicksPerSecond: Int64 = 0;
  7. var
  8.   Time: Double;
  9. begin
  10.   QueryPerformanceFrequency(TicksPerSecond);
  11.  
  12.   repeat
  13.     QueryPerformanceCounter(TicksBegin);
  14.     Sleep(1);
  15.     QueryPerformanceCounter(TicksEnd);
  16.  
  17.     Time := (TicksEnd - TicksBegin) / (TicksPerSecond / 1000);
  18.     WriteLn(Time:2:2, 'ms');
  19.   until ReadKey() = 'e';
  20. end.
Code: Pascal  [Select]
  1. 1.39ms
  2. 1.90ms
  3. 1.73ms
  4. 1.88ms
  5. 1.96ms
  6. 1.85ms
  7. 1.88ms
  8. 1.88ms
  9. 1.87ms
  10. 1.92ms
  11. 1.90ms
  12. 1.88ms
  13. {..}

Tomorrow I will check it on Win10.
« Last Edit: May 18, 2019, 01:52:31 am by furious programming »
Lazarus 2.0.4 with FPC 3.0.4, Windows XP (all 32-bit)

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7507
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #13 on: May 18, 2019, 11:36:18 am »
But sleep works using the scheduler timer, probably some HPET thing, and not the RTC.

Explain one thing to me. My platformer uses Sleep between frames to make delays (few ms). I checked how it works on Win10. Without using the TimeBeginPeriod function, the game maintains 41fps, and if I call it with a minimum period, it maintains 60fps.

The scheduler's timer is accurate, the preemptive scheduler is not :-)

I reacted to your

Quote
It probably stops the program for about 16ms.

Based on old 18.2 clock timer resolutions, which is simply not true.  Since Nehalem, hpet timers are in uncore and constant in frequency and quite precise. Windows will use whatever resources it have to try to keep it short.

Of course, reaction to the timer (polling, interrupt latency, or simply fair scheduling) does not.

If you have some precise timing requirements you can use realtime extensions, at the expense of the efficiency of scheduling.

Quote
Results of some quick research on timing in Win32 — see the penultimate section. Old article, but helped me a lot.

Too old, one of the two test systems is win98 for christ sake!

Quote
I know it's not like Sleep(0) actually does anything. My point was that it does not freeze the program/thread, so 25% of the power consumption (100% of the core) is the expected value.

Quote
Please inform Microsoft https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-sleep

And with that I meant not the deviation, but the sleep(0) explicitely being mentioned there.
Read it.

furious programming

  • Sr. Member
  • ****
  • Posts: 354
  • I click a little.
Re: Sleep(1) in thread's loop can reduce CPU usage much
« Reply #14 on: May 18, 2019, 02:36:43 pm »
The scheduler's timer is accurate, the preemptive scheduler is not :-)

Yes. 8)

Quote
I reacted to your

Quote
It probably stops the program for about 16ms.

Based on old 18.2 clock timer resolutions, which is simply not true.  Since Nehalem, hpet timers are in uncore and constant in frequency and quite precise. Windows will use whatever resources it have to try to keep it short.

[…]

Too old, one of the two test systems is win98 for christ sake!

I know that it is old, but is was halpful for me. So below you have a tests on modern systems. The low means that the test uses default time period, and the high additionally calls TimeBeginPeriod(1) before loop. The tester code is in my previous post.

Code: Pascal  [Select]
  1.      WinXP                 Win7                Win10
  2.  
  3.  low       high       low       high       low       high
  4.  
  5. 1.84ms    1.69ms     8.41ms    2.88ms    11.79ms    1.17ms
  6. 1.88ms    1.79ms    15.13ms    0.61ms     9.46ms    1.80ms
  7. 1.93ms    1.94ms    15.36ms    0.70ms    14.31ms    1.80ms
  8. 1.88ms    1.93ms    15.52ms    0.64ms    14.89ms    1.88ms
  9. 1.88ms    1.93ms    15.19ms    0.62ms    14.99ms    1.89ms
  10. 1.88ms    1.92ms    15.35ms    0.44ms    14.68ms    1.87ms
  11. 1.88ms    1.93ms    15.54ms    0.59ms    15.08ms    1.72ms
  12. 1.88ms    1.88ms    15.54ms    0.55ms    15.47ms    1.87ms
  13. 1.92ms    1.89ms    15.53ms    0.57ms    15.37ms    1.87ms
  14. 1.88ms    1.89ms    15.45ms    0.58ms    15.01ms    1.88ms
  15. 1.88ms    1.88ms    15.15ms    0.59ms    14.87ms    1.86ms
  16. 1.88ms    1.93ms    15.43ms    0.82ms    15.03ms    1.90ms
  17. 1.88ms    1.93ms    15.17ms    0.58ms    14.99ms    1.84ms
  18. 1.93ms    1.88ms    15.52ms    0.73ms    14.70ms    1.88ms
  19. 1.91ms    1.81ms    15.19ms    0.59ms    15.19ms    1.88ms

There is something strange with the results of the high test on Win7 — the Sleep(1) freezes the thread for less than millisecond, on other systems, close to the 2ms. But the default period is about 15ms on both modern systems, so I was so close to the truth.

But this is not important. No matter who is right — if we use the Sleep procedure, we must take into account its inaccuracy.
« Last Edit: May 18, 2019, 02:42:21 pm by furious programming »
Lazarus 2.0.4 with FPC 3.0.4, Windows XP (all 32-bit)