embeddedartistry / arduino-printf

Add printf support to the Arduino SDK
MIT License
91 stars 15 forks source link

Library uses substantially more storage than PrintEx? #1

Closed scottchiefbaker closed 4 years ago

scottchiefbaker commented 4 years ago

I use PrintEx in a lot of my projects, but it's not maintained any more (last update was 2016). Researching your printf() implementation looks promising but it uses a significant amount of storage.

Any idea why this implementation is so much larger?

PrintEx

Sketch uses 5486 bytes (17%) of program storage space. Maximum is 32256 bytes.
Global variables use 264 bytes (12%) of dynamic memory, leaving 1784 bytes for local variables. Maximum is 2048 bytes.

arduino-printf

Sketch uses 9876 bytes (30%) of program storage space. Maximum is 32256 bytes.
Global variables use 254 bytes (12%) of dynamic memory, leaving 1794 bytes for local variables. Maximum is 2048 bytes.
phillipjohnston commented 4 years ago

Hello Scott,

Thanks for writing in and sending a size comparison. This library is merely a wrapper around mpaland/printf, so the sizing is determined by that library's implementation.

I notice a few things in my testing and reading:

  1. Exponential formatting is enabled in this library by default, but doesn't seem to be used in PrintEx. Disabling this support by defining PRINTF_DISABLE_SUPPORT_EXPONENTIAL saves 1764 bytes in my test case.
  2. PrintEx tops out at 32 bit integers, while "long long" support is enabled in this library by default. Disabling support by defining PRINTF_DISABLE_SUPPORT_LONG_LONG saves 1266 bytes in my test case
  3. Disabling these two options together gets us closer to the PrintEx numbers, although we're still +~1400 bytes
  4. If you're not using floats, disabling floating-point support by defining PRINTF_DISABLE_SUPPORT_FLOAT saves a whopping 4,044 byes. Disabling all three of the above options saves 5220 bytes.

The PrintEx library is a custom wrapper around the Print class, while mpaland/printf is written to the C standard. From PrintEx:

The printf implementation found in this library is unique. It has been built directly on top of the Arduino Print library rather than as a separate code base simply called from within. All the features found in printf use a feature already implemented in Print.

It could very well be that there are differences in the two implementations accounting for the remaining size difference, such as additional format flags implemented in mpaland/printf that aren't used in PrintEx.

It could also be that the optimizer is better able to work with the PrintEx inheritance structure. This printf library is standalone and is only tied to the Print class through the default implementation of _putchar.

phillipjohnston commented 4 years ago

I'll also add this information to the README, seems useful to note in general.

scottchiefbaker commented 4 years ago

Thank you for the the very detailed write up. This was by no means meant to disparage this library, I'm just trying to find a good replacement for PrintEx. I love the idea that this has no dependencies and is super simple. On an MCU that's pretty important.

I think the options you mentioned above sound promising. I definitely need floating point support as that was the main reason I need a printf() implementation. The other two are certainly less common. I'm not sure how I disable them though. If I include

#define PRINTF_DISABLE_SUPPORT_EXPONENTIAL

The output size of my sketch doesn't change. Am I missing something here?

scottchiefbaker commented 4 years ago

Just for posterity here are my initial findings. Using vanilla options for both libraries on my PrintfTest.ino I get:

Type Bytes
No Serial 1606
PrintEx 5056
arduino-printf 9476
phillipjohnston commented 4 years ago

No disparagement perceived! It's a reasonable question worth exploring.

Due to the design of the mpaland/printf library, these definitions must come from the compiler, rather than defining that before the header. I'll work with the library author on that, as it would be convenient to define them before including the header to control behavior.

If you're using a Makefile or other build system, you'd use the -D flag (e.g., -D PRINTF_DISABLE_SUPPORT_EXPONENTIAL) to add the necessary variables/commands.

For Arduino IDE, the flags need to be added to the compiler.extra_flags property in platform.txt or platform.local.txt. You would need to restart the IDE for the changes to take effect.

I believe that you can also use the Arduino CLI to control preferences. Some notes: https://forum.arduino.cc/index.php?topic=537500.0

(Will also update the README with better notes on this)

scottchiefbaker commented 4 years ago

If I disable long long support, and exponential I get:

Type Bytes
No Serial 1606
PrintEx 5056
arduino-printf 6328

Which is a lot more reasonable. It would be great if you could disable these at compile time with a #define instead of having to use -D which is a pain.

Or perhaps better yet, they could default to disabled on an AVR based MCUs, and enabled on bigger more capable boards? I feel like the use case for needing 64 bit ints, and exponents on AVR boards is going to be pretty minimal.

My compile command was:

arduino --verify --pref build.extra_flags="-DPRINTF_DISABLE_SUPPORT_EXPONENTIAL -DPRINTF_DISABLE_SUPPORT_LONG_LONG" --pref build.path=/tmp/arduino-build-PrintfTest/ --port /dev/ttyUSB0 --board arduino:avr:uno /home/bakers/Arduino/PrintfTest/PrintfTest.ino

phillipjohnston commented 4 years ago

That's a reasonable request. I'll modify the library source here and work on improvements with the primary project separately.

scottchiefbaker commented 4 years ago

Sounds good. I look forward to testing it.

scottchiefbaker commented 4 years ago

I just found out (accidentally) that the ESP8266 and ESP32 Arduino implementations both implement printf()/sprintf() natively now. I'm not sure how well documented/publicized that is.

From my testing the ESP implementation of printf() supports: floating point, long longs, but not exponents.

It might be worth putting in your README.md. If you think it's worthy of mention I can submit a pull request for it.

phillipjohnston commented 4 years ago

Definitely worth a note - happy to accept PRs.

phillipjohnston commented 4 years ago

Do you know what header ESPxxx defines printf in? I'd like to take a look at what they're doing in the context of another library (arduino-logger)

scottchiefbaker commented 4 years ago

@phillipjohnston I'm not a C++ expert, it's hard to tell where it gets defined. I think it's here:

https://github.com/esp8266/Arduino/blob/master/cores/esp8266/Print.h#L78

scottchiefbaker commented 4 years ago

Digging a little deeper I think it's here actually:

https://github.com/esp8266/Arduino/blob/master/cores/esp8266/Print.cpp#L58