FastLED runs slower when adding unreachable code

I am doing a project with FastLED that involves a cube with 7x7x20 LEDs distributed over 25 strings. I can share more code and details if needed, but I'll start with what I think is necessary for understanding the issue.

The project has several functions that set the lights of the cube in different manners. I have added a state variable that decides which function to run.
The problem is that the update rate slows down when I add the possibility of running the other functions by changing the state variable, even though the state variable is for now always set to 0.

Below I have added the part of the code that sets the state variable and the loop function.

const uint8_t SPACE_TRAVEL = 0;
const uint8_t OCEAN = 1;
const uint8_t GLOBE = 2;
const uint8_t TREE = 3;

uint8_t state = 0;

void loop() {
   c++;
   t = millis() / 5;
   x1 = 0;
   scale = 50;
   
   switch (state)
   {
   case SPACE_TRAVEL:
      spaceTravel2();
      break;
   case OCEAN:
      landscape(startColorIndex);
      break;
   case GLOBE:
      globe();
      break;
   case TREE:
      playWithTrees();
      break;
   }

   FastLED.show();
}

Why is the code under the other case statements resulting in FastLED updating more slowly?
Simply changing uint8_t state = 0 to const uint8_t state = 0 makes the code run fast again.
Is FastLED calculating render speed based on possible updates of the lights?

None of the functions called in the switch expression calls FastLED.show() either.

Thanks for any help!

Do.

There’s no hint of why what you observing is.

Complete code that compiles, runs and supports your claim. As small as you can while it still malfunctions.

TIA

a7

It's because if state is not a constant it could be any value and the compiler has to generate code to get to the right case for any possible state. Also, when state is not a constant the compiler has to generate code for all of the functions because they might be called.

Still, I'm a bit surprised that the switch/case statement takes a noticable amount of time.

I understand that when state is not a constant the compiler will include more of the code (which is what I want because ultimately I would want to change state based on conditions in the code).

As with you, what is puzzling is why the code slows down during execution. Programatically nothing underneath the case blocks, where case is not 0 should execute (and of course doesn't, since my program runs as expected, just slower).

My only theory is that FastLED does some calculation of the timing to set the LEDs and this results in a long delay. The code in itself shouldn't take long to run, and the bottleneck is the FastLEDs timing schedule.
From my understanding, the exact same code is executed during runtime, just FastLED slows it down by reserving more time for the LEDs to be se.

I'll try to make a minimum full case that shows the issue. For now its almost 1000 lines of code due to different scenes.

FastLED is designed to be, um, fast.

I do know that another smart LED library messes with millis() timing mechanism and makes an attempt to compensate for that, so that millis() is not totally wrong… but the idea that FastLED itself ever takes more time than it should to "help" is, IMO, not what you are going to discover.

My only evidence is that this would have turnt up long ago and upset a very many apple carts.

a7

Wild guess here, odd behavior like you are describing is sometimes caused by writing past the end of an array. Any change to the code can result in a different variable being stored immediately after the array, changing what is affected by overwriting that variable.

That might very well be the issue. There are quite a few arrays keeping track of lights and positions. I think I have found the one function that slows things down even though its case condition doesn't hold.

I was trying to simplify the code down to the bare minimum as requested by @alto777, and found that one single function was responsible for slowing things down. I'll investigate more, and come back here with what I find, but yeah, I think writing past the end of an array might be the issue.

I won't tell you how many times the process of preparing a proper case for presentation to the heavies that might help in these fora has led me to the discovery of an error, sometimes subtle and sometimes the Doh! forehead slapping kind.

I won't tell you, and I won't say what fraction do turn out to be pretty dumb… :wink:

a7

I don't have much experience with C++ or Arduino programming, so these memory issues throw me off a little. I wish it was the Doh! kind here, but I have found where the issue lies, but I'm not quite sure why it results in problems.

int8_t treeHeight = 0;
int8_t treeX = 3;
int8_t treeY = 3;
int8_t leafLength = 0;
int8_t leafHeight = 1;
bool treePlanted = false;

void plantTree(int x, int y) {
   treeHeight = 0;
   treeX = x;
   treeY = y;
   treePlanted = true;
}

void growTree(int maxTreeHeight, int interval) {
   if (treePlanted && c % interval == 0){
      treeHeight += 1;
      treeHeight = min(maxTreeHeight, treeHeight);
   }
}

void growLeaves(int maxTreeHeight, int maxLeafLength, int interval) {
   if (leafLength == maxLeafLength - 1 && c % interval == 0){
      leafHeight++;
      leafHeight = min(leafHeight, 10);
   } 
   if (treeHeight == maxTreeHeight - 1 && c % interval == 0){
      leafLength++;
      leafLength = min(leafLength, maxLeafLength);
   }
   
}

void renderTree(int maxTreeHeight) {
   // -------------------Problematic (treeX and treeY) ----------------
   for (int z = 0; z <= treeHeight; z++) {
      setLight(CRGB::DarkOrange, treeX, treeY, NUM_Z - 1 - z);
   }
   // -----------------------------------------------------------------
   if (leafLength > 0) {
      int zTop = NUM_Z - 1 - maxTreeHeight;
      int _xMin = max(treeX - leafLength, 0);
      int _yMin = max(treeY - leafLength, 0);
      int _xMax = min(treeX + leafLength, NUM_X - 1);
      int _yMax = min(treeY + leafLength, NUM_Y - 1);
      for (int x = _xMin; x <= _xMax; x++) {       // int x = _xMin is problematic
         for (int y = _yMin; y <= _yMax; y++) {    // int y = _yMin is problematic
            for (int z = zTop; z < zTop + leafHeight; z++){
               if (z == zTop || x == _xMin || x == _xMax || y == _yMin || y == _yMax) {
                  setLight(CRGB(30, 175, 30), x, y, z);
               }
            }
         }
      }
   }
}

void setLight(CRGB color, int x, int y, int z) {
   const int stripIndex = (y + y % 2) / 2 * NUM_X + (1 - 2 * (y % 2)) * ((x - x % 2)/ 2) - (y % 2);
   const int ledIndex = ((x + (y % 2)) % 2 ) * NUM_Z + z;
   leds[stripIndex][ledIndex] = color;
}

int treeCount = 0;
void playWithTrees() {
   if (treeCount == 0) {
      plantTree(3,3);
      treeCount++;
   }
   if (treePlanted){
      growTree(NUM_Z - 1, 2);
      growLeaves(NUM_Z - 1, 2, 2);
      renderTree(NUM_Z - 1);
   }
}

Here I have the code that grows and renders a "tree". The problem lies in renderTree, where the problematic lines are commented. It seems like the theoretical possibility of x or y being set to something out of range creates the issue. Has this something to do with memory allocation?

void renderTree(int maxTreeHeight) {

   const int trunkX = min(NUM_X, max(0, treeX));
   const int trunkY = min(NUM_Y, max(0, treeY));
   for (int z = 0; z <= treeHeight; z++) {
      setLight(CRGB::DarkOrange, trunkX, trunkY, NUM_Z - 1 - z);
   }

   if (leafLength > 0) {
      int zTop = NUM_Z - 1 - maxTreeHeight;
      int _xMin = max(treeX - leafLength, 0);
      int _yMin = max(treeY - leafLength, 0);
      int _xMax = min(treeX + leafLength, NUM_X - 1);
      int _yMax = min(treeY + leafLength, NUM_Y - 1);
      for (int x = 0; x <= _xMax; x++) {
         for (int y = 0; y <= _yMax; y++) {
            for (int z = zTop; z < zTop + leafHeight; z++){
               if (x >= _xMin && y >= _yMin && (z == zTop || x == _xMin || x == _xMax || y == _yMin || y == _yMax)) {
                  setLight(CRGB(30, 175, 30), x, y, z);
               }
            }
         }
      }
   }
}

This seems to have solved it. Using a conditional instead of initialization of the variable in the for loop, and making sure that treeX and treeY is not out of range seems to satisfy the program in terms of trusting that the coordinates are within the limits of the array.

Okey, it doesn't seem like that was the only problem. There is a lot of weird things that impact the refresh rate.

void globe() {
   if (state != GLOBE) {
      return;
   }
   int radius0 = 0;
   int radius1 = 1;
   int radius2 = 2;
   int radius3 = 3;
   int z = 10;
   int dir = 1;
   double m = min(c / 100, 2);
   double rad = t * DEG_TO_RAD * m;
   
   int xPoint1 = round(3 + cos(rad) * radius1);
   int yPoint1 = round(3 + sin(rad) * radius1);
   int xPoint2 = round(3 + cos(rad) * radius2);
   int yPoint2 = round(3 + sin(rad) * radius2);
   int xPoint3 = round(3 + cos(rad) * radius3);
   int yPoint3 = round(3 + sin(rad) * radius3);

   CRGB color = CRGB(100, 100, 100);

   for (int x = 0; x < NUM_X; x++) {
      for (int y = 0; y < NUM_Y; y++) {
         for (int z = 0; z < NUM_Z; z++) {
            if (x == xPoint3 && y == yPoint3 && 5 < z && z < 14) {
               setLight(color, x, y, z);
            } else if (x == xPoint2 && y == yPoint2 && (z == 4 || z == 16)) {
               setLight(color, x, y, z);
            } else if (x == xPoint1 && y == yPoint1 && (z == 1 || z == 18)) {
               setLight(color, x, y, z);
            } else if (x == 3 && y == 3 && (z == 0 || z == 19)) {
               setLight(color, x, y, z);
            } 
         }
      }
   }
}

Here, the use of sin() or cos() makes the refresh rate slower. If I change sin(rad) to 1 for example, the speed is faster. This is without running the code, just having globe() in a case block that is not run, in the same way as mentioned before.

That eliminates a call to the sin function, which may be slow. To get faster and constant trig functions, you should use a lookup table close enough.

But if you mean even if you aren't calling sin() or using 1 as a substitute for it in any code that gets executed, you still looking for something you are doing wrong in one place that rises up to bite you on the door in a tots different place in your code...

a7