Optimizing code for SPEED / STABILITY, ways to write, stepper example

Hy,
I noticed that as you start writing bigger and bigger programs with if statements, state machines, i2c, other libraries, etc etc... it really matters that you do need to pay attention on how you write your code or things will get slow.
I don`t use delay()s at all, I would only do it when it would make sense to.

Alot of times I have trouble with "if logic" when there is alot of things to take in account , something in that style:
"if this and this, but not this, do this, but also if you do this then dont do this, but also do this if first 3 things are ok" and it goes on and on.

How do you deal with situations like that? I like to look at the whole picture from time to time, to see what I can merge, sometimes there is also something I dont need anymore...

I would also like to ask few things with examples, some may look stupidly obvious, but those are simplified for the sake of it:

Does it matter if you use 2 if statements when you could get away with just 1:

  if (x == 1)
  {
    y++;
  }

  if (x == 1)
  {
    y++;
  }

instead of:

`  if (x == 1)
  {
    y++;
    y++;
  }`

Also how bad is it to put a x++; statement to run every loop?

Or if there is a situation that something would execute every 200th loop (follow the loop speed) rather than every x miliseconds, should the millis() method be used anyway?
I like to do this:

  x = digitalRead(3);

  if (x == 1 && y <= 200)
  {
    y++;
    if(y == 200)
    {
      a = 1;
      y = 0;
      }
  }

  //NOW I HAVE OUTPUT "a" EVERY 200TH LOOP.

Could I take different approach to make it faster?

I know that there is alot of times that For loop could be used, but I don`t like to use it much for some reason, I just dont. Now the question here is, does it even matter? Can a For loop be faster / better / consume less memory and RAM or is it just to tidy up the code?
Does it even make a difference when compiler is done with it?

And one real-life part of the code that will be used actually:

Im currently at the beginning of modification old radio from the 70s.

One of the mods are digital fm tuner and motorised (with stepper motor) frequency scale; I need this because I want to make auto-seek and in order to make this happen I created the "frequecy sweeper", basicly a float that sweeps between 80 and 108 (MHz) when "Seek-" or "Seek+" is pressed, based on that value, program will send DATA to the FM tuner. Also this value gets maped map() to an int that counts steps from 0 to 5700 for the stepper motor.

Here is the very last part of the code:

  if (StepRequest > 0)
  {
    MoveSteps = ScalePos - CurrentStepPosition;
    StepRequest = 0;
  }
  
  
  if (MoveSteps != 0)
  {
    CurrentStepPosition = CurrentStepPosition + MoveSteps;

    stepper.step(MoveSteps);
    MoveSteps = CurrentStepPosition - ScalePos;
  }

StepRequest changes to 1 earlier in the loop when the "Seek" starts, ScalePos is NEW position of where the scale should be (at step 1000 for example) and CurrentStepPosition is where scale is right now.

I had it written this way because Frequency must sweep fast, only a few seconds from 80 to 108MHz (there are also halfs (88.5 MHz)) and stepper must follow. I cant just tell a stepper to go x steps together, because I dont know when auto seek will stop, but I can tell it in the every loop how far behind it is. This results in smooth, fast and accurate motion of the scale, also it stops right when it needs to.

This is all good, but because I`m feeding about 35turns/loop to the stepper, the loop must be fast or motor starts to jitter.

Is there a better way to do it?

Right now it is ok -after I excluded Serial.print(); commands that were ment for debugging purposes, but once I add tuner, bluetooth & usb module, source selector, buttons... Well at that point of learning curve I can say that I know for a fact that loop will slow down enough to cause issues down the road, if not else, constant I2C communication with FM module while auto seek will.

Last thing that I want is a stepper humming to the speed of my program.

If not else I can keep the tempo with millis() and instruct a program to check if steps should be sent earlier in the loop, or is it maybee too fast and it should be sent in the middle of the loop or at the end of it.

Help of speed control also is on an option as motor is really quiet, but at certain RPM.

Thank you all for reading my looong "essey", anwser what part you would like to, have a great day.

Sorry if I answer in chunks, but I am an old tube radio guy AM, we barely looked at FM as it wasn't very popular then. I even owned at one time half a dozen 01A hand blown glass tubes.
The compiler is set to optimize space since the majority of boards it runs on are space restricted. The lowly CPU on some of these boards are 48 times faster than the fastest mainframe I worked on when I was at IBM.
YES you can change the optimization, I think even with #pragma inserted in the code at particular points but that is a bit of a guess.

That is correct (other than it wouldn't be two y's). The compiler would likely have optimized your code to that in any case.

As far as your concerns re complex if (decision) logic, sometimes that is just what it is, other times we are blind to some obvious optimizations. When I encounter something like that I will rewite the code without looking at the old code. I sometimes re-write 2 to 4 times. Also learn to use all the looping constructs, not just for, while and do while. Sometimes I have created a bit map of a bunch of conditions then the if tests can be for all zero, all 1's, even a value less than or greater than or between. Unfortunatly the gap between seasoned professionals and the very best amateurs is very wide and they don;t know that. It sounds like you will be one of the better coders at least in a few years.

No, if it's every 200, no millis is needed.

If it's meaningful to increment every loop then x++ is valid. x should likely be unsigned long in order for it to not overflow too quickly, and you might consider checking it occasionally (based on millis) to reset the counter back to 0 if that makes sense.

Back to the x's for a minute, why would anyone code two identical if's in a code block let alone in a row? For instance, if the light turns green do ALL the following, look left and right for light runners, check for kids, animals, citizens caught in the crosswalk, press the gas slowly until you see the way ahead is safe for all, then proceed to accelerate to your intended speed. END OF ALL
Why would you ever break that up? You might even make that chunk a function that is in a separate file (that is another lesson, it is on the forum somewhere)

One last observation. Steppers are a study by themselves and I am a rookie, but I know people who are experts and I think there are ways to control those motors that avoids your concerns. To start, check out the library AccelStepper. Study ALL the examples.
Good luck
Screenshot 2024-09-11 at 16.58.02

If you need answers to this depth of detail, you pretty much MUST look at the object code that the compiler produces (and be aware that that can change with compiler versions and targets, without obvious other changes in behavior.)
(You don't really need to fully UNDERSTAND the assembly language to be able to glean information from an assembly listing. but some level of assembly experience would help.)

So, let's see...

#include <avr/io.h>

int main() {

  char x, y=0;
  x = PINB;   // get some "real" number
  if (x == 1) {
    y++;
  }
  if (x == 1) {
    y++;
  }
  PORTB = y;  // do something with the result.
  
  x = PIND;
  if (x == 1) {
    y++;
    y++;
  }
  PORTD = y;
}

Look for the double-semicolons (";;") for my comments...

 avr-gcc -Os -g -mmcu=atmega328p bar.c
 avr-objdump -S a.out

int main() {

  char x, y=0;
  x = PINB;   // get some "real" number
  80:	83 b1       	in	r24, 0x03	; 3
  if (x == 1) {
  82:	81 30       	cpi	r24, 0x01	;;  compare with 1
  84:	51 f4       	brne	.+20     	; 0x9a 
    y++;
  }
  if (x == 1) {
    y++;
  86:	82 e0       	ldi	r24, 0x02	;;  Load 2 into "y". 
                                        ;; Note that setting y=0 was skipped as unncessasry,
                                        ;; as was doing actual subtraction
  }
  PORTB = y;  // do something with the result.
  88:	85 b9       	out	0x05, r24	; 5
  
  x = PIND;
  8a:	99 b1       	in	r25, 0x09	; 9
  if (x == 1) {
  8c:	91 30       	cpi	r25, 0x01	; 1
  8e:	09 f4       	brne	.+2      	; 0x92
    y++;
    y++;
  90:	8e 5f       	subi	r24, 0xFE	;; actually increment y by 2,
                                            ;;  (by subtracting -2) (there is no "add constant" in AVR.)
  }
  PORTD = y;
  92:	8b b9       	out	0x0b, r24	; 11
}

Thank you both for your anwsers, now I have some clue.
Will study it in depth a bit later as I just got home from work.

The switch-case structure, commonly used in FiniteStateMachines is a good alternative to of complicated nests of if statements.

Have you seen these:

Hy, thank you for anwser...

I got carried away with hardware part for my fm tuner project and also my code works as it should.

I will finish this to get it of the table and return here to study things out.

Have a great day

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.