Function takes more memory than discrete code implementation?

#1

So… I am nearly finished my first Arduboy game, but my game is becoming too big for the flash memory to store. At that point in the program, every flash memory byte counts, so I am trying to reduce the size of my sketch by doing stuff like removing unnecessary code, improving code so that it takes up less flash while doing the same thing as before, defining repeated code as void functions, y’know, all the normal procedures you’d perform to reduce the size of your sketch.
But then I came across a problem with defining repeated chunks of code as void functions. So… I had two chunks of code which did the same thing, so I put a copy of that chunk of code in a void declaration, and replaced the two chunks of code with the void function defining that whole thing. But when I verified to see how much flash the program took up, it took WAY more (like, about 334 bytes) flash than the previous version of the program (the one that had repeated code instead of function definitions).

I guess to solve that problem, I could just hold down ctrl and z to go back to before, but the code is going to look pretty messy with repeated chunks of code. Plus I am now pretty confused… I thought calling functions that have been defined save memory, not take up more. Why did it take up more memory?? What if I spent a whole load of time replacing repeated code with defined functions only to have not saved flash, but used up more??! I’m so confused someone please explain to me why it took up more flash!!

Because the program of my game is so big and so confusing, the codes below are just examples and segments taken from my original sketch. The example with the defined void functions takes up only 74 more bytes than the one with repeated code, but like I said earlier, in my game, it took up about 334 bytes of flash. Don’t know why it takes up a different amount of flash in the examples than in my game, but the concept in the examples is the same as in my game; using void functions takes up more flash than repeating codes.

Here’s the code that uses repeated code (uses 10,022 bytes of 28,672 bytes):

#include <Arduboy.h>

Arduboy ard;
//Just a variable that never gets changed in this example.
byte PowerUpType = 1;

//Also two variables that never get changed.
byte Xpos = 60;
byte Ypos = 20;

//A bunch of bitmaps.
static const unsigned char InvulPower[] PROGMEM = {
0x7E, 0x91, 0x99, 0xBF, 0xFD, 0x99, 0x89, 0x7E, 
};

static const unsigned char FixbarrierPower[] PROGMEM = {
0x7E, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x7E, 
};

static const unsigned char UpPower[] PROGMEM = {
0x7E, 0x81, 0xC5, 0xFF, 0xFF, 0xC1, 0x81, 0x7E, 
};

static const unsigned char MegashotPower[] PROGMEM = {
0x7E, 0x81, 0xDD, 0x81, 0x81, 0xDD, 0x81, 0x7E, 
};

static const unsigned char StopPower[] PROGMEM = {
0x3C, 0x42, 0xBD, 0xBD, 0xBD, 0xBD, 0x42, 0x3C, 
};

//Initialize Arduboy
void setup() {
  ard.begin();
  ard.setFrameRate(30);
  ard.clear();
}

//So, here it is. Two chunks of code, you'd expect it to take up
//less flash if you were to declare one as a void function, and
//replace those large chunks of code with the functions, but NO!
//That takes up MORE flash!
void loop() {
    //One large chunk of code...
    ard.fillRect(Xpos, Ypos, Xpos + 8, Ypos + 8, BLACK);
    switch (PowerUpType) {
      case 1:
      ard.drawBitmap(Xpos, Ypos, FixbarrierPower, 8, 8, WHITE);
      break;
      case 2:
      ard.drawBitmap(Xpos, Ypos, UpPower, 8, 8, WHITE);
      break;
      case 3:
      ard.drawBitmap(Xpos, Ypos, InvulPower, 8, 8, WHITE);
      case 4:
      break;
      ard.drawBitmap(Xpos, Ypos, MegashotPower, 8, 8, WHITE);
      break;
      case 5:
      ard.drawBitmap(Xpos, Ypos, StopPower, 8, 8, WHITE);
      break;
    }
    //Two large chunks of code, both the same
    //(apart from the Xpos - 11 and Ypos - 9 thing)
    ard.fillRect(Xpos, Ypos, Xpos + 8, Ypos + 8, BLACK);
    switch (PowerUpType) {
      case 1:
      ard.drawBitmap(Xpos - 11, Ypos - 9, FixbarrierPower, 8, 8, WHITE);
      break;
      case 2:
      ard.drawBitmap(Xpos - 11, Ypos - 9, UpPower, 8, 8, WHITE);
      break;
      case 3:
      ard.drawBitmap(Xpos - 11, Ypos - 9, InvulPower, 8, 8, WHITE);
      case 4:
      break;
      ard.drawBitmap(Xpos - 11, Ypos - 9, MegashotPower, 8, 8, WHITE);
      break;
      case 5:
      ard.drawBitmap(Xpos - 11, Ypos - 9, StopPower, 8, 8, WHITE);
      break;
    }
  //Wait until the next frame, even though nothing will change in this example.
  while (!(ard.nextFrame())) {
  }
}

And here’s the code that uses void functions, the one that takes up more flash (uses 10,096 bytes of 28,672 bytes):
#include <Arduboy.h>

Arduboy ard;

//Example variables that never change.
//I should probably add 'const' at the start of these, but this is
//just a quick example. Plus, I cannot be bothered to do so.
byte PowerUpType = 1;

byte Xpos = 60;
byte Ypos = 20;

//A bunch of bitmaps.
static const unsigned char InvulPower[] PROGMEM = {
0x7E, 0x91, 0x99, 0xBF, 0xFD, 0x99, 0x89, 0x7E, 
};

static const unsigned char FixbarrierPower[] PROGMEM = {
0x7E, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x7E, 
};

static const unsigned char UpPower[] PROGMEM = {
0x7E, 0x81, 0xC5, 0xFF, 0xFF, 0xC1, 0x81, 0x7E, 
};

static const unsigned char MegashotPower[] PROGMEM = {
0x7E, 0x81, 0xDD, 0x81, 0x81, 0xDD, 0x81, 0x7E, 
};

static const unsigned char StopPower[] PROGMEM = {
0x3C, 0x42, 0xBD, 0xBD, 0xBD, 0xBD, 0x42, 0x3C, 
};

//The void function. Does the same as the two chunks
//from the previous example.
void RenderPowerUp(int PXpos, byte PYpos, boolean PBW) {
    ard.fillRect(PXpos, PYpos, PXpos + 8, PYpos + 8, BLACK);
    switch (PowerUpType) {
      case 1:
      ard.drawBitmap(PXpos, PYpos, FixbarrierPower, 8, 8, PBW);
      break;
      case 2:
      ard.drawBitmap(PXpos, PYpos, UpPower, 8, 8, PBW);
      break;
      case 3:
      ard.drawBitmap(PXpos, PYpos, InvulPower, 8, 8, PBW);
      case 4:
      break;
      ard.drawBitmap(PXpos, PYpos, MegashotPower, 8, 8, PBW);
      break;
      case 5:
      ard.drawBitmap(PXpos, PYpos, StopPower, 8, 8, PBW);
      break;
    }
}
//Initialize Arduboy.
void setup() {
  ard.begin();
  ard.setFrameRate(30);
  ard.clear();
}

void loop() {
  //Now that's where the problem is. Normally it would
  //take up less memory to define a chunk of code as a
  //function than repeating it, but doing all this takes
  //up more flash memory!
  RenderPowerUp(Xpos, Ypos, WHITE);
  RenderPowerUp(Xpos - 11, Ypos - 9, WHITE);

  //Wait for next frame, even though nothing new will be rendered.
  while (!(ard.nextFrame())) {
  }
}

Thank you for any help and advice provided, I can’t wait to receive an answer as to why it takes up more flash!

(Kevin) #2

I do know that running code discretely will be faster. If what you are describing is the case is because there will be some memory overhead for creating the objective links to the function?

(Pharap) #3

They’re not equivalent.

You’re using boolean (i.e. bool) in the function, and the conversion to and from bool is what’s eating up the progmem.

Change PBW in the function to uint8_t (or byte if you’d rather) and you’ll see it suddenly drops in size significantly.

drawBitmap expects a uint8_t, not a bool, and the rules of conversion state that when a bool is converted to an integer type, it takes a value of 0 if it’s false and 1 if it’s true and that conversion uses progmem.

Likewise, converting a uint8_t (or other integer type) to a bool involves producing false if the value is 0 and true for any other value. Doing so will use extra code depending on how the compiler implements bool.