Seemingly simple operation increases code size

I have a case where a seemingly simple function is taking up much more program space than I would expect. I have the following function in a much larger program (This obviously doesn’t do anything because I pared it down to try to isolate the source of the problem):

int get_fingers() {

  int n = 0;

 for (byte i = 0; i < 9; i++) {
   bitSet(n, i);
   bitClear(n, i);
 }

  return n;
 
}

Using that version of the function my sketch compiles to 22792 bytes.

However, using this version, which does the same thing, compiles to 21780, considerably smaller:

int get_fingers() {

  int n = 0;


   bitSet(n, 0);
   bitClear(n, 0);
      bitSet(n, 1);
   bitClear(n, 1);
      bitSet(n, 2);
   bitClear(n, 2);
      bitSet(n, 3);
   bitClear(n, 3);
      bitSet(n, 4);
   bitClear(n, 4);
      bitSet(n, 5);
   bitClear(n, 5);
      bitSet(n, 6);
   bitClear(n, 6);
      bitSet(n, 7);
   bitClear(n, 7);
      bitSet(n, 8);
   bitClear(n, 8);


  return n;
 
}

However, if I try these both in a bare minimum sketch, they compile to exactly the same size, as would be expected. I’m not changing anything else in my larger sketch, so do you have any ideas what could be going on with the compiler to make this much difference? Thanks!

Unless your real code is doing something different, the result of calling that function will be 0, every time. So, the function is pointless. In the bare minimum sketch, the compiler figured that out, and optimized the whole function away.

Yes, the function doesn’t do anything as shown, because I removed everything else to try to find which part was increasing the code size so much. I didn’t realize the compiler was smart enough to optimize it away in the bare minimum version. In the version below, I don’t think it optimizes it away (does it?), but the sketch size is still very close to the same no matter which version I uncomment (the for-loop version is a few bytes larger). When I try both versions in my larger sketch the for-loop version is still nearly a kb larger.

 int n = 0;
 
void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

n = 10;
/*
 for (byte i = 0; i < 9; i++) {
   bitSet(n, i);
 }
*/
 
      bitSet(n, 0);
      bitSet(n, 1);
      bitSet(n, 2);
      bitSet(n, 3);
      bitSet(n, 4);
      bitSet(n, 5);
      bitSet(n, 6);
      bitSet(n, 7);
      bitSet(n, 8);
   
}

Trying to narrow it down more (again realizing that these don’t do anything useful), this version still increases the sketch size by about 800 bytes:

int get_fingers() {

  int n = 0;

 for (byte i = 0; i < 9; i++) {
   bitSet(n, i);
 }
    return n;
}

…compared to this version:

int get_fingers() {

  int n = 0;

 for (byte i = 0; i < 9; i++) {
   n = i;
 }
    return n;
}

…and also 800 bytes more than this version:

int get_fingers() {

  int n = 0;

 for (byte i = 0; i < 9; i++) {
   bitSet(n, 2);
 }
    return n;
}

Which makes it seems like using i in the bitSet macro is somehow adding 800 bytes to the sketch, but I’m sure I’m missing something important :wink:

I suspect that it's optimizing out the calls to bitSet where n is known (was just set), and the second argument is a fixed value set at compile time. It's probably optimizing out basically all of that function

What's sort of odd is that it's not recognizing that the same is happening when you call it in the loop...

I suspect if n were declared volatile, the difference would go away.

I think I figured out what was going on-- in my sketch I was writing to a variable for the first time after initializing it, and that increased my sketch size a lot because I was reading that variable in many other places. Presumably the compiler was treating it as a constant until I changed it(?). In my examples above, the sketch size generally increased when I was setting n or one of its bits to a variable rather than a constant.

Presumably the size of the bare minimum sketch didn't change nearly as much because I wasn't reading n after writing to it.

Thank you DrAzzy, I hadn’t seen your post, but I think that’s correct-- I didn’t realize quite how smart the compiler is :wink:

Even un-optimized, it would be odd for that code fragment to produce 800 bytes of binary.
Something else is going on, probably in the code you haven't included.

The only real way to analyze these things is to examine the .elf file with various tools (avr-nm, avr-objdump, etc.) Doing so requires the .elf file, or a full set of sources from which the .elf file can be produced.

If I was reading n in multiple places in my sketch, and the above function is the only place I was writing to n, would that be enough to increase the increase the sketch size that much? The reason I think that is I tried just setting n to the value of another variable instead of calling the function, and that also increased the sketch size by about the same amount (whereas setting n to a constant didn't increase it).

n is local, you can’t refer to that same ‘n’ elsewhere in the code unless your real code declares it as global...?

Sorry, I was actually setting another variable to the value that was returned from the function. That's the variable that is read in many other places but never written to anywhere else.

for (byte i = 0; i < 9; i++)

A byte only has 8 bits, 0 ~ 7.

Yes, but I was settings the bits of n, which is an int.