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

"default_lib": "small" no longer supported!? #2823

Closed
RobMeades opened this issue Sep 27, 2016 · 5 comments
Closed

"default_lib": "small" no longer supported!? #2823

RobMeades opened this issue Sep 27, 2016 · 5 comments

Comments

@RobMeades
Copy link
Contributor

RobMeades commented Sep 27, 2016

With the current version of mbed-cli, running mbed compile -s tells me that the target I am in the process of integrating into mbed cannot be supported officially because it is using nano.spec libraries. When did using std libraries become a requirement!? We have very little flash/RAM to play with (128 kbytes/20 kbytes) and we NEED nano.spec to make useful applications fit in it. The difference in flash consumption is huge (blinky plus printf() under GCC_ARM):

"default_lib": "small" 29035 bytes
"default_lib": "std" 72756 bytes

...i.e. it takes up 34% more of our flash for no net gain. There's also a 3 kbyte RAM increase, which is even worse (as we have less left in the first place), but I hope there's some way of configuring that out; please tell me there is.

So, PLEASE explain why this is a requirement, 'cos it's making mbed too heavy weight for us and being so light and nimble was one of mbed's key differentiators.

@bridadan
Copy link
Contributor

@RobMeades I believe the motivation behind this change was that small libs are not necessarily thread safe (GCC's small lib for instance). To deal with this, when using a small library we configure mbed OS to be in single threaded mode by default. Since one of the main features of mbed OS 5 is multi-threading, it was decided to enforce multi-threading capability on the default configuration for all mbed OS 5 targets. @sg- may be able to clarify a bit more on the specifics if you need more info.

You're right this comes with increased memory usage, and @pan- has had a number of PRs that work on reducing the memory requirement of mbed OS in general as well as with config options. Maybe you can benefit from this work?

@RobMeades
Copy link
Contributor Author

RobMeades commented Sep 27, 2016

I'm pretty sure we do not have enough RAM on this particular platform to run any multithreading but I would be interested to hear if that is not the case. My numbers for static RAM are (for blinky plus printf() under GCC_ARM):

"default_lib": "small" 5868 bytes
"default_lib": "std" 8888 bytes

With a 3 kbyte stack (seems sufficient), we're well into our 20 kbytes allocation.

@RobMeades
Copy link
Contributor Author

RobMeades commented Sep 29, 2016

@bridadan, @sg- , @pan- In order to check whether mbed is at all viable for our platform with the flash sizes of std libs, I have tried GCC_ARM builds using K64F and our M0 platform against latest mbed-os (58c12f1) with mbed-os-example-blinky as a base and modified as follows:

  1. unmodified ('Base'),
  2. use a printf(),
  3. use a Serial object and echo received characters with a .readable()/.writeable() getc()/putc() loop,
  4. (2) and (3) together,
  5. (4) plus malloc() (for good measure).

For each build I did both NDEBUG=1 and default. We really need to be at 30 kbytes to 40 kbytes for a sensible configuration, which I think is build (3) for our case; this is where we were with small libs. That's currently showing as more like 70 kbytes for any platform with std libs.

The detailed results are below. They raise a few specific questions:

  1. Build (3) (use a Serial object) adds 20 kbytes of code for both platforms? Why does a simple Serial object attract so much code?
  2. Build (2) (use a printf()) on our M0 platform, best case, is 5 kbytes larger (all in 'misc' code) than the K64F. In general, it seems that NDEBUG=1 produces much less benefit on our platform than the K64F platform (2 kbytes versus 10 kbytes in this case). What could I be doing to cause this? FYI, I applied NDEBUG=1 in targets.json and all builds were -c.
  3. Looking at the underlying numbers, K64F generally has 10% to 20% less code in the 'misc' category then our M0 platform (and we have less target code and, as far as I can tell, no strange header inclusions, just boring drivers for CMSIS, timers, GPIO and serial). Any ideas as to how I can close the gap?

k64f_flash_size

m0_platform_flash_size

@pan-
Copy link
Member

pan- commented Sep 29, 2016

@RobMeades newlib is significantly larger than newlib-nano or IAR and ARMCC C library because it is a more general purpose C library.
Regarding to the numbers and the primitives used, most of the space occupy by your application will be taken by the C library.

There is few ways to reduce the size of a binary:

  • If you're application doesn't needs multiple threads then you can safely use nano-lib by passing the option -o small-lib when you compile with mbed-cli.
  • Avoid the IO and file subsystems of C library, this cost a lot of memory especially on newlib. That is one of the reason the ROM consumed explode when you start using the Serial class. Indeed, it pull the formatting library (vfprintf) and the file subsystem of newlib. Instead you can use RawSerial which doesn't pull any code of the file susbsystem and pull the format library only if you use printf.
    You can also completely shutdown the IO subsystem by applying this configuration: Add configuration for IO flushing during exit. #2741
  • Tune the RTOS configuration to fit your application: Basically you can overide all the configuration macros defined here in you mbed_app.json.

With all this advice, the binary size is greatly reduced:

main.cpp

#include <mbed.h>

int main() {
    RawSerial serial(USBTX, USBRX);
    while(true) { 
        serial.putc(serial.getc());
    }

    return 0;
}

mbed_app.json:

{
    "macros": [
        "NDEBUG=1",
        "OS_TASKCNT=1",
        "OS_IDLESTKSIZE=32",
        "OS_STKSIZE=1",
        "OS_TIMERS=0",
        "OS_FIFOSZ=4",
        "OS_MUTEXCNT=1"
    ],
    "target_overrides": {
        "*": {
            "core.stdio-flush-at-exit": false
        }
    }
}

Results:

+---------------------+-------+-------+------+
| Module              | .text | .data | .bss |
+---------------------+-------+-------+------+
| Fill                |    43 |     4 | 2349 |
| Misc                |  1454 |  2116 |   76 |
| features/frameworks |    56 |     0 |    0 |
| features/storage    |    42 |     0 |  184 |
| hal/common          |  1108 |     4 |   21 |
| hal/targets         |  4838 |    12 |  216 |
| rtos/rtos           |    38 |     4 |    4 |
| rtos/rtx            |  5321 |    20 | 4642 |
| Subtotals           | 12900 |  2160 | 7492 |
+---------------------+-------+-------+------+
Allocated Heap: 65540 bytes
Allocated Stack: unknown
Total Static RAM memory (data + bss): 9652 bytes
Total RAM memory (data + bss + heap + stack): 75192 bytes
Total Flash memory (text + data + misc): 16100 bytes

It is still a big executable but it is much more reasonable, we keep investigating on potential memory optimizations.

@RobMeades
Copy link
Contributor Author

RobMeades commented Sep 29, 2016

That is brilliant advice @pan-, very much appreciated. Leaving std libs in place, applying the flush tweak and using RawSerial instead of Serial brings my "sensible configuration" (mbed-os-example-blinky with a printf() and, now, a RawSerial object) down [with NDEBUG=1] to:

My M0 platform:

+---------------------+-------+-------+------+
| Module              | .text | .data | .bss |
+---------------------+-------+-------+------+
| Fill                |    50 |     0 |   31 |
| Misc                | 29139 |  2216 |  108 |
| features/frameworks |    56 |     0 |    0 |
| hal/common          |  1400 |     4 |  285 |
| hal/targets         |  3436 |     4 |  114 |
| rtos/rtos           |    46 |     4 |    4 |
| rtos/rtx            |  5937 |    20 | 5986 |
| Subtotals           | 40064 |  2248 | 6528 |
+---------------------+-------+-------+------+
Allocated Heap: 4280 bytes
Allocated Stack: 3072 bytes
Total Static RAM memory (data + bss): 8776 bytes
Total RAM memory (data + bss + heap + stack): 16128 bytes
Total Flash memory (text + data + misc): 42312 bytes

K64F:

+---------------------+-------+-------+-------+
| Module              | .text | .data |  .bss |
+---------------------+-------+-------+-------+
| Fill                |    83 |     4 |  2501 |
| Misc                | 31998 |  2212 |    88 |
| features/frameworks |    56 |     0 |     0 |
| features/storage    |    42 |     0 |   184 |
| hal/common          |  2056 |     4 |   277 |
| hal/targets         | 10028 |    12 |   216 |
| rtos/rtos           |    38 |     4 |     4 |
| rtos/rtx            |  5907 |    20 |  6870 |
| Subtotals           | 50208 |  2256 | 10140 |
+---------------------+-------+-------+-------+
Allocated Heap: 65540 bytes
Allocated Stack: unknown
Total Static RAM memory (data + bss): 12396 bytes
Total RAM memory (data + bss + heap + stack): 77936 bytes
Total Flash memory (text + data + misc): 53504 bytes

Absolutely spot-on.

Someone asked me yesterday how I was doing with mbed. I said I have a love hate relationship and that I was currently in hate but I expected to be in love again by the end of the week. I am now in love again 👍 .

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

3 participants