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

Example sketches always report the same values? #3

Closed
philj404 opened this issue May 29, 2021 · 7 comments
Closed

Example sketches always report the same values? #3

philj404 opened this issue May 29, 2021 · 7 comments

Comments

@philj404
Copy link

philj404 commented May 29, 2021

Hi. Thank you for publishing this library. I wanted to make something similar, but it was good to see you had a library already available!

Unfortunately, I have not been able to get MemoryUsage to report any changing values for the example sketches (or for my own sketches). I expected the examples to report changing values as storage was allocated and released. Am I using the library incorrectly?

My environment:

  • Windows 10 build computer
  • Arduino IDE 1.8.13
  • board family: "Arduino AVR Boards" v1.8.3
  • Board: "Arduino Uno"
  • Library: MemoryUsage v2.21.0 (fresh today)

I tried to run the example sketches "FreeRam" and "Stack".

NOTE 1:
The MemoryUsage examples are under File/Examples/INCOMPATIBLE/MemoryUsage...

  • Are these examples really incompatible with "Arduino Uno"?
  • I have other libraries compatible with the AVR architecture -- in these other libraries their library.properties files declare "architectures=avr" -- they declared "avr" in lower case. Should "avr" be declared lower case for MemoryUsage too?

The examples seemed to compile anyway.

NOTE 2: Stack.ino

Stack.ino builds with the following warning:

WARNING: library MemoryUsage claims to run on AVR architecture(s) and may be incompatible with your current board which runs on avr architecture(s).
C:\Users\philk\OneDrive\Documents\Arduino\libraries\MemoryUsage\src\MemoryUsage.cpp: In function 'uint16_t mu_StackCount()':
C:\Users\philk\OneDrive\Documents\Arduino\libraries\MemoryUsage\src\MemoryUsage.cpp:76:39: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  while (*p == STACK_CANARY && (int) p <= SP)
                                       ^
Sketch uses 5262 bytes (16%) of program storage space. Maximum is 32256 bytes.
Global variables use 286 bytes (13%) of dynamic memory, leaving 1762 bytes for local variables. Maximum is 2048 bytes.

The sketch output gives values that never change. For example stack size is always 1256 bytes:

Starting state of the memory:

Data start:256
Heap start:542
Heap end:542
Stack start:1047
Stack end:2303
Stack size:1256
Stack Maximum Size (Painting method): 1761


subPointer
Test string
Stack start:1047
Stack end:2303
Stack size:1256
Stack Maximum Size (Instrumentation method): 1256

subSmartPointer
Test string
Stack start:1047
Stack end:2303
Stack size:1256
Stack Maximum Size (Instrumentation method): 1256

subConstSmartPointer
Test string
Stack start:1047
Stack end:2303
Stack size:1256
Stack Maximum Size (Instrumentation method): 1256

subFull
Test string
Stack start:1047
Stack end:2303
Stack size:1256
Stack Maximum Size (Instrumentation method): 1256

subLocalData
10.00
Stack start:1047
Stack end:2303
Stack size:1256
Stack Maximum Size (Instrumentation method): 1256

Stack Maximum Size (Painting method): 1761


Ending state of the memory:

Data start:256
Heap start:542
Heap end:542
Stack start:1047
Stack end:2303
Stack size:1256

These values were supposed to change (at least temporarily), correct?

NOTE 3: FreeRam.ino

FreeRam.ino builds with the following warning:

WARNING: library MemoryUsage claims to run on AVR architecture(s) and may be incompatible with your current board which runs on avr architecture(s).
C:\Users\philk\OneDrive\Documents\Arduino\libraries\MemoryUsage\examples\FreeRam\FreeRam.ino: In function 'void setup()':
C:\Users\philk\OneDrive\Documents\Arduino\libraries\MemoryUsage\examples\FreeRam\FreeRam.ino:21:11: warning: unused variable 'p' [-Wunused-variable]
     byte *p = new byte[3000];
           ^
C:\Users\philk\OneDrive\Documents\Arduino\libraries\MemoryUsage\src\MemoryUsage.cpp: In function 'uint16_t mu_StackCount()':
C:\Users\philk\OneDrive\Documents\Arduino\libraries\MemoryUsage\src\MemoryUsage.cpp:76:39: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  while (*p == STACK_CANARY && (int) p <= SP)
                                       ^
Sketch uses 2458 bytes (7%) of program storage space. Maximum is 32256 bytes.
Global variables use 192 bytes (9%) of dynamic memory, leaving 1856 bytes for local variables. Maximum is 2048 bytes.

The sketch output gives values that never change. For example the free RAM size is always 1850 bytes:

Starting state of the memory:

Data start:256
Heap start:448
Heap end:448
Stack start:2297
Stack end:2303
Heap size:0


Free Ram Size: 1850


Ending state of the memory:

Data start:256
Heap start:448
Heap end:448
Stack start:2297
Stack end:2303
Heap size:0


Free Ram Size: 1850

Again, these values were supposed to change (at least temporarily), correct?

@philj404
Copy link
Author

FYI I tried using the previous release of MemoryUsage (v2.20.0).

  • The examples were found under MemoryUsage as expected (not under INCOMPATIBLE/MemoryUsage/...)
  • Otherwise Stack.ino and FreeRam.ino generated the same output was as for the examples in v2.21.0.

@Trusty77
Copy link
Member

For the problem of architecture, its fixed with the new lib 2.21.1.
The INCOMPATIBLE folder has been probably caused by this problem ('AVR' instead of 'avr')...
For the incorrect values, the way heap and stack pointers and their exposure to code has probably changed since the development of this in 2015. This code needs probably a refresh....

@philj404
Copy link
Author

(Argh... I just lost the longer comment I was typing up. I will break it up to smaller topics.)

Your v2.21.1 release now has its examples listed for Arduino Uno. Thanks.

I will do some more investigation into why the examples have results which do not seem to change.

@philj404
Copy link
Author

I cloned the MemoryUsage repository and instrumented the code to learn more about what the examples are doing.

The FreeRam.ino example never calls STACK_COMPUTE. Does this make sense? I think the amount of unused RAM available should change depending on function calls and local variables going into and out of scope.

@philj404
Copy link
Author

philj404 commented Jun 1, 2021

The Stack.ino example calls STACK_COMPUTE 5 times.

Unfortunately since STACK_COMPUTE only saves the largest value in mu_stack_size, it is hard to tell which calls (if any) change the value. This also can be a bit misleading as the reported "Stack size" is actually the MAXIMUM seen so far, not the current stack size.

I added an extra STACK_COMPUTE call at the beginning of setup(). This should help catch a change that increases stack size.

I think the SP macro might return a good value for the stack pointer, but I may not be interpreting the value correctly. I got type conversion warnings when doing the math between SP, RAMEND and mu_stack_size. It is hard to tell if ANY of them start with the same type! :-)

On my branch I am turning the STACK_COMPUTE macro into an inline function (at least temporarily). This lets me print intermediate values for each call.

@philj404
Copy link
Author

philj404 commented Jun 5, 2021

I think the library and examples are technically behaving correctly (except for minor issues #6 and #5).

However the compiler optimizer passes make the examples much less interesting. After optimization many expected side effects disappear:

  • aggressive inlining of functions means the stack pointer may never change. This can be useful if RAM space is small, and there is plenty of ROM for code -- like the AVR architecture.
  • the compiler may keep variables in registers, so again the stack pointer may never need to change
  • the compiler seems to detect "unused/unchanged" values and not bother generating code for them.

All good in principle. But the optimizations make it difficult to predict how much stack space is needed, just by inspection. Even for a simple example. Often nothing seems to change, especially for simple examples.

I was able to get more predictable results by:

  • declaring functions noinline, for example void __attribute__ ((noinline)) STACK_COMPUTE_function(void) {...};. This ensures the function call will exercise the stack so this library can observe it.
  • declaring variables volatile, which hints to the compiler it should manipulate the variable (and put it on the stack), even if it seems unnecessary.

I found it useful to modify the MemoryUsage library to support new (inline) functions to return numeric values for stack size, heap size etc.. This lets me numerically compare measured values against my expectations, measure deltas, print values in hex, etc.. I wrote a unit test to help me ensure my expectations matched what the compiler actually does.
(I will submit a pull request with these changes and my example sketch. It is a significant change, but it should be backwards-compatible. I hope it is useful for you!)

In all, I am VERY happy with how MemoryUsage is working for me. It is a good tool for understanding how much work the optimizers have done to improve performance on your code, and how much memory your code still needs. Thank you for providing these abstractions!

@philj404
Copy link
Author

I am going to close this issue out, as I think it is now covered by other, simpler issues (#5, #6, #8, and #9).
The simpler issues should be easier to address one by one.

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

2 participants