Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Measuring CPU load #81

Closed
moritz89 opened this issue Jan 5, 2020 · 12 comments
Closed

Measuring CPU load #81

moritz89 opened this issue Jan 5, 2020 · 12 comments

Comments

@moritz89
Copy link

moritz89 commented Jan 5, 2020

Hi, I wanted to know if there is a method to measure the CPU load? i.e., time spent in the IDLE mode

@arkhipenko
Copy link
Owner

Hi.
Currently, there is no such method as I have never needed one, nor been asked to implement one.
Also, since this needs to happen inside the scheduling loop I am trying to avoid putting any non-essential processing inside there.
Could you please elaborate on the use case? Why is it necessary?
Cheers,
Anatoli

@moritz89
Copy link
Author

moritz89 commented Jan 5, 2020

Hi, as the scope of my program on the ESP32 grows I would like to know how much headroom I still have. A system monitor task is currently able to periodically report free RAM and heap fragmentation, but I currently have no idea how fast I can clock other tasks and what difference a more frequent execution of those tasks will have on the CPU load.

Since the loop() function is always being called, using the system CPU load reporting will be of little use as TaskScheduler is not using the system idle thread, as far as I know (for the ESP32).

@TD-er
Copy link

TD-er commented Jan 5, 2020

Just as a disclaimer. I do not use this scheduler, but am using something I wrote myself. (as I didn't know about this one...)

What I do in my implementation is checking if there is something to be done and if not, I do log the current time stamp. (also keep track if that timestamp is 0 before setting it)
As soon as there is something to be done, I compute the current time elapsed and add that to a counter.
Periodically I reset this counter and the timestamp since the last reset.
Before resetting it, I store the last load value (idle time counter / time since last reset).

But if you have some priority tasks possible, then you could also schedule a lowest priority task with its only purpose to keep track of how long it was running. (and maybe calling delay() to keep the CPU power consumption low)

@chrisalbertson
Copy link

chrisalbertson commented Jan 5, 2020 via email

@arkhipenko
Copy link
Owner

arkhipenko commented Jan 5, 2020

All good comments, guys, thank you.

However, since we are talking about ESP32, the situation is a little complicated:
There is no real IDLE sleep on ESP chips. Or at least I could not find the one that puts CPU in a similar state to that of the Arduino Idle Sleep. ESP sleep function requires a reset of the chip, which is not usable for task scheduling.
So IDLE sleep for ESP8266 and ESP32 is simulated by just calling esp.delay(1) if one pass through the task chain takes less then a threshold value (currently 200 microseconds). This is very unscientific and I don't even use _TASK_SLEEP_ON_IDLE_RUN compile option with ESPs.

Additionally on ESP32 loop() and therefore TS tasks run on CORE 1 (Application) inside the FreeRTOS idle task. Since tasks from CORE1 could potentially call system routines from CORE0 and then receive callbacks (especially in the WiFi methods), there is a way to create race condition on the callbacks leading to unpredictable results and crashes.

I have (successfully) experimented combining FreeRTOS and TS in one program (multiple TSs actially, since you can have a separate scheduler per RTOS task), but this proved to be not trivial and caused a lot of tripping of RTOS watchdog timers.

I have some ideas how we can measure what you are trying to measure - let me noodle on those.

Meanwhile, there is something you can already do:
Use _TASK_TIMECRITICAL compile option:
Compile parameters

You can use these two methods to assess whether you still have CPU capacity:
getStartDelay()
getOverrun()

If you start getting consistent and growing negative values on getOverrun, your CPU is not handling the load.

@arkhipenko
Copy link
Owner

arkhipenko commented Jan 8, 2020

OK. I pushed version 3.1.0 into the testing branch.
Please refer to Example #24 for the CPU load measuring methods.

Below is example 24 output for ESP32:

WITH IDLE SLEEP
==================
CPU Time Measurement
Start
Loop counts c1=11001
Task counts c2=1001
Total CPU time=9999976 micros
Scheduling overhead CPU time=45013 micros
Idle Sleep CPU time=9789919 micros
CPU Callback time=165044 micros

CPU Idle Sleep 97.90 % of time.
CPU Callback processing 1.65 % of time.



WITHOUT IDLE SLEEP
==================
CPU Time Measurement
Start
Loop counts c1=548161
Task counts c2=1001
Total CPU time=9999927 micros
Scheduling overhead CPU time=2306636 micros
Idle Sleep CPU time=0 micros
CPU Callback time=7693291 micros

CPU Idle Sleep 0.00 % of time.
CPU Callback processing 76.93 % of time.

I think 'CPU Callback processing' is a bit of a misnomer. It should be "CPU callback processing and other stuff the ESP OS does outlside of loop() like running WiFi stack, etc.)

@arkhipenko
Copy link
Owner

@moritz89 : would be helpful if you could test and comment.

@moritz89
Copy link
Author

moritz89 commented Jan 8, 2020

First off, the added functionality is exactly what I am looking for! I agree that the "CPU Callback processing" would rather be "Time not spent in TaskScheduler" which effectively translates to "Doing effictive work".

While testing the changes I noticed two issues:

  1. Due to changes in commit 44240bf, including <TaskSchedulerDeclarations.h> will cause the build to fail due to redeclaration of the millis() and micros() function.
  2. The warning that _TASK_SLEEP_ON_IDLE_RUN is not supported on the ESP32 might be removed. It actually yields the current task as is implemented by FreeRTOS.

My results while running the example code showed:

# Without _TASK_SLEEP_ON_IDLE_RUN
Start
Loop counts c1=547483
Task counts c2=1001
Total CPU time=9999778 micros
Scheduling overhead CPU time=2265702 micros
Idle Sleep CPU time=0 micros
CPU Callback time=7734076 micros

CPU Idle Sleep 0.00 % of time.
CPU Callback processing 77.34 % of time.

# With _TASK_SLEEP_ON_IDLE_RUN
CPU Time Measurement
Start
Loop counts c1=11001
Task counts c2=1001
Total CPU time=9999949 micros
Scheduling overhead CPU time=45025 micros
Idle Sleep CPU time=9789831 micros
CPU Callback time=165093 micros

CPU Idle Sleep 97.90 % of time.
CPU Callback processing 1.65 % of time.

@arkhipenko
Copy link
Owner

arkhipenko commented Jan 9, 2020

Due to changes in commit 44240bf, including <TaskSchedulerDeclarations.h> will cause the build to fail due to redeclaration of the millis() and micros() function.

Could you please send me the files? I did test example 24 on ESP32 and it did not have any issues
Just making sure: you include <TaskScheulder.h> in one file and <TaskSchedulerDeclarations.h> in all other files. Not both!

The warning that _TASK_SLEEP_ON_IDLE_RUN is not supported on the ESP32 might be removed. It actually yields the current task as is implemented by FreeRTOS.

Well... Idle sleep supposed to put the chip to sleep, which ESPs just does not do... just delays, so no power savings actually take place.

Thanks for testing!

@arkhipenko
Copy link
Owner

arkhipenko commented Jan 9, 2020

Also:
micros() rolls over every ~72 minutes. I have not tested the stats running continuously for such a long time, (pretty sure the results would be completely incorrect since total scheduling time will suddenly go down dramatically while accumulated idle time will remain high) but if you plan to do so I would recommend calling ts.cpuLoadReset(); every hour or so if you plan to use it beyond testing.

@moritz89
Copy link
Author

@arkhipenko I reordered my includes and how the defines are set and it works now. Thanks again for the quick turn-around for the CPU load functionality!

@arkhipenko
Copy link
Owner

Enjoy! And thank you for the suggestion. I think this is useful and I could have used it in my projects before for sure...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants