Cost of Memory (usage) by Libraries

One for the experts.

For the sake of clarity I will ask the question with as basic a scenario as possible.

Lets say I have a library XYZ which has 10 functions.

Lets say I have a sketch A which uses only one of the functions in library XYZ.

Lets say I have a sketch B which uses all ten of the functions in library XYZ.

When I compile the sketches will library XYZ use :

    1. less memory in sketch A than in sketch B?
    1. the same amount of memory in sketch A and sketch B?

My thinking is - if the answer is 2, I guess I am better off placing the needed function directly in a sketch A.

I’ve heard it claimed that once a library is linked in, the entire library is included, but my recent tests don’t really bear that out.

Using this debugging (at the command line on Linux):

 avr-nm -SCn whatever.elf | grep ' [Tt] '

(Turn on verbose compiling to get the pathname to the .elf file).

Test sketch:

#include <Wire.h>
void setup () { }
void loop () 
  {
  }  // end of loop

Sketch size: 1,716 bytes

Symbols (in part):

000000be 00000002 T setup
000000c0 00000002 T loop
000000c2 00000012 T TwoWire::available()
000000d4 0000002a T TwoWire::read()
000000fe 00000022 T TwoWire::peek()
00000120 00000002 T TwoWire::flush()
00000122 0000002e t global constructors keyed to TwoWire::rxBuffer
00000150 00000062 T TwoWire::write(unsigned char const*, unsigned int)
000001b2 00000060 T TwoWire::write(unsigned char)
00000212 00000036 T twi_transmit
00000248 00000322 T __vector_24
0000056c 00000022 T main
0000058e 00000090 T __vector_16
0000061e 00000076 T init

Even without any function calls, we have various constructors and functions included.


Adding in this line:

Wire.begin ();

Sketch size now: 2,064 bytes

Symbols now:

00000118 00000002 T setup
0000011a 0000000a T loop
00000124 00000012 T TwoWire::available()
00000136 0000002a T TwoWire::read()
00000160 00000022 T TwoWire::peek()
00000182 00000002 T TwoWire::flush()
00000184 0000002e t global constructors keyed to TwoWire::rxBuffer
000001b2 00000062 T TwoWire::write(unsigned char const*, unsigned int)
00000214 00000060 T TwoWire::write(unsigned char)
00000274 00000016 T TwoWire::begin()        <-------------- new
0000028a 00000036 T twi_transmit
000002c0 00000322 T __vector_24
000005e2 0000003c T twi_init
0000061e 000000a8 T digitalWrite
000006c8 00000022 T main
000006ea 00000090 T __vector_16
0000077a 00000076 T init

You can see that init() has moved further down in program memory.


Adding in this:

 Wire.beginTransmission (42);
  Wire.requestFrom (66, 5);

Sketch size now: 2,298 bytes

Symbols:

00000118 00000002 T setup
0000011a 0000002c T loop
00000146 00000014 T TwoWire::beginTransmission(int)  <---------- new
0000015a 00000012 T TwoWire::available()
0000016c 0000002a T TwoWire::read()
00000196 00000022 T TwoWire::peek()
000001b8 00000002 T TwoWire::flush()
000001ba 0000002e t global constructors keyed to TwoWire::rxBuffer
000001e8 00000062 T TwoWire::write(unsigned char const*, unsigned int)
0000024a 00000060 T TwoWire::write(unsigned char)
000002aa 0000001a T TwoWire::requestFrom(unsigned char, unsigned char, unsigned char)  <------------ new
000002c4 00000008 T TwoWire::requestFrom(int, int)    <------------ new
000002cc 00000016 T TwoWire::begin()
000002e2 00000092 T twi_readFrom
00000374 00000036 T twi_transmit
000003aa 00000322 T __vector_24
000006cc 0000003c T twi_init
00000708 000000a8 T digitalWrite
000007b2 00000022 T main
000007d4 00000090 T __vector_16
00000864 00000076 T init

So it looks to me that the sketch is getting larger, the more of the library you use.

I tested this for myself a while ago. I used one function out of the large strings.h library and I also tested a version where I wrote my own version of the function without any error checking or even a function call - it was just typed straight into my code. The function was strncpy(), which copies n characters between two memory locations.

The library version was one byte bigger so obviously it doesn't include the entire library and it's even capable of inlining the function where appropriate.

From your post, given we start with a base size of 1,716 bytes - IMHO I would interpret that as the entire library being included i.e. the library overhead.

Adding in this line:

Wire.begin ();

Sketch size now: 2,064 bytes

I would interpret this as the library overhead + usage of 1 function.

Adding in this:

 Wire.beginTransmission (42);

Wire.requestFrom (66, 5);




Sketch size now: 2,298 bytes

I would interpret this as the library overhead + usage of 2 functions.

So it looks to me that the sketch is getting larger, the more of the library you use.

Yes, but it would also seem the entire basic library is always included.

So it still begs the question, if I only need one function and I embed the code for that function directly in my sketch - do I save the library overhead at the slight expense of a larger sketch?

The caveat being the library contains no other bits and pieces need to run my sketch.

I note Morgan_S’s post appears to indicate little/no saving.

What do you mean by the "entire library" if you then add "+ usage of 1 function.".

The functions are in the library.

aisc:
do I save the library overhead at the slight expense of a larger sketch?

What library overhead do you have in mind?

If u include the library but issue no function calls, there is a basic overhead for including the library.

If u then issue a function call (i.e. add usage of 1 function), the size increases from the basic overhead to a larger size.

Hope that clarifies what I am trying to say.

In theory, the memory usage for including the library with no function call.
i.e. 1,716 bytes based on your example.

If u include the library but issue no function calls, there is a basic overhead for including the library.

Some libraries. It depends on what is in the library.

aisc:
If u include the library but issue no function calls, there is a basic overhead for including the library.

Well... As Coding Badly said, it depends.

If the library has a class instance, then you automatically get the constructor and destructor (functions). And if they happen to call other functions, they are necessarily included as well.

Instead of talking about some hypothetical library, how about posting the actual library?

And as I almost posted in my first reply: "Try it and see". Why ask us, when you can just try both techniques and let us know how they go?

I have a sketch running on the equivalent of a Leonardo which receives wireless data and posts it to an online database.
My current sketch uses 433MHz.
I am now working on an nRF24 version as an alternative.
Presently connection to the LAN is via Ethernet, but I would like to add wifi.
I also want to add Bluetooth monitoring with an Android tablet.

Initially the sketch also wrote to an SD card, but I had to drop that due to memory limitations.
I can live without writing to the SD card, but I was at least hoping to read from it.

So I am just trying to find a way to achieve my desired functionality and I see the libraries as one potential area for reducing memory usage.

#include <VirtualWire.h> // RF library
//#include <SD.h> // SD Card library
#include <SPI.h>
#include <Ethernet.h>

Which memory?

Mark

holmes4:
Which memory?

Mark

hmmm...... let's put it another way.
I am trying to avoid the dreaded "your sketch is too big etc etc" when trying to upload.

The AVR's have 3 types of memory SRAM (data) only, Program and EPROM.

Mark

linkers are supposed to only include the functions that they use.

If you are reading elsewhere about "libraries", it's probably good to know
that the terminology "library" in arduino-world is quite different to
the generally understood meaning of "library" in the real world.

IMHO I would interpret that as the entire library being included i.e. the library overhead.

And your opinion would be wrong.

Now when you include the SD card library, you are probably going to get an instance of the class, even if you don't declare one in your sketch code.

The Wire class will do that, too.

And in some cases, the class objects will have quite large arrays in them. The code for the 'ten functions" that you have, may be tiny compared to the size of the data buffers created by the class.

On the other hand, some other class might have very little class data, and a lot of actual code.

The code for SD has been drastically obfuscated by "experts", you have to put together about ten files to figure out what is going on.