Help understanding memory use


#1

I’m working on a library to make callbacks for webhooks a bit more friendly. In this library, I create a buffer for callback objects with fixed length response topics. The library is being set up to allow users to change the buffer size and response topic length. I was doing some memory use testing and am having trouble understanding the amount of memory used for the response topic char[]

Test code looks like this:

#include "Particle.h"
#include <functional>

class P_Promise{
public:
  P_Promise(uint8_t charLen = 20){
    responseTopic = new char[charLen];
  }
  bool inUse;
  char* responseTopic;
  uint32_t timeoutTime;
  std::function<void(const char*, const char*)> successFunc;
  std::function<void(const char*, const char*)> errorFunc;
  std::function<void(void)> timeoutFunc;
  std::function<void(void)> finalFunc;
};


class ParticlePromise{
public:
   ParticlePromise(const uint8_t _containerSize = 5, const uint8_t _maxTopicLength = 20){
     PromiseContainer = new P_Promise[_containerSize]{_maxTopicLength};
   }

private:
  P_Promise* PromiseContainer;
};

Here’s a link to my memory use data:

I’ve found that if I hold the buffer size steady at 5 objects, and increase the char[] by 10, I only get an 8 byte increase in memory use. In my mind, it should be 1 byte per character, which would mean a 50 byte increase in memory use when increasing the char[] by 10.


#2

One thing to keep in mind is that the STM32 processor is word aligned - every allocation begins at a 4-byte boundary. So an 10 byte buffer will use 10 bytes, however there will be 2 filler bytes after it that won’t be able to be allocated since the next allocation needs to start on a word boundary.

Global variables, structures, and classes, variables smaller than 4 bytes (bool, uint8_t, uint16_t, etc.) are generally allocated on 4-byte boundaries as well.

Arrays are packed within the array, however, so uint16_t[4] (2 bytes values x 4 array elements) takes 8 bytes.


#3

So specifying uint8_t vs int or uint32_t doesn’t actually save any memory space unless it’s an array?


#4

So specifying uint8_t vs int or uint32_t doesn’t actually save any memory space unless it’s an array?

Correct. The reason is that the processor is optimized for reading at word boundaries and if you read a non-aligned value it needs to do so using a much slower method. It’s possible to create a packed structure, but it’s generally not good to this because it makes accessing the data inefficient.


#5

Awesome! Do you have any good resources on memory use/management so I can hopefully start understanding this better?


#6

This page has a number of tips for optimizing flash and memory usage:


#7

I still don’t quite understand the small increase in memory use. I get that going from a 10 char buffer to 20 is actually only an 8 byte increase, but I’m doing this 5 times, so I should be seeing a 40 byte increase.


#8

How are you “going from a 10 char buffer to 20”?
Are you changing the hardcoded default topic length or are you doing it via the parameter at runtime?

I might be completely off there, but when you construct an array of objects the individual objects in the array are constructed by means of the default constructor. Since you are not providing one but a “custom” constructor with an optional paramter, I’d suspect the default to be used on all but the first element for which you provide an initial value.


#9

I have a simple .ino file:

#include "ParticlePromise.h"

ParticlePromise promise(5,20);

void setup(){
    Serial.begin(9600);
    Serial.printlnf("Free Memory: %u", System.freeMemory());
}

void loop(){

}

I thought new P_Promise[_containerSize]{_maxTopicLength} would work similar to the array syntax for setting all values like int arr[10] = {42} I’ll try removing the optional parameter on the constructor for P_Promise when I get home.


#10

I did find this on stackoverflow that might be relevant

Looks like I may need to use std::vector to hold my “array” of objects. As long as I don’t try to resize the vector, it should be fragment-safe.


#11

what array of objects are you referring to? Do you intend to create an array of these callback objects?

I’m curious to understand why are you using functional instead of plain function pointers?


#12

PromiseContainer = new P_Promise[_containerSize]{_maxTopicLength} is the array. It’s not a true array, but it can be accessed similarly. ie PromiseContainer[1].timeoutTime = 2000

I’m using functional because I want to be able to point to class member functions as well as standard functions so I’ll need to use std::bind and std::function plays nicer with bind.


#13

This does not initialise all elements with 42 but only element 0


#14

I see now that I misunderstood this:

int z[3] = {0}; // z has type int[3] and holds all zeroes

from http://en.cppreference.com/w/c/language/array_initialization


#15

This works because most compliers initialise global variables to 0 by default
as the previous sample of the same link shows

int y[5] = {1,2,3}; // y has type int[5] and holds 1,2,3,0,0
int z[3] = {0}; // z has type int[3] and holds all zeroes

The latter would also be true with this

int z[3]; // z holds all zeroes this way too