Home AMX User Forum NetLinx Studio

For Loop Best Practices

I have several for loops that I use in my program. I've been assigning each one a different variable to use as the counter but I got to thinking, do I need to do that?....Would a for loop ever run while another for loop is running (with the exception of a loop in a loop). How about if I have a wait command that then runs a for loop? As far as I understand netlinx, there's never actually 2 things running at once, one thing ends then the next in the queue goes.

Comments

  • PhreaKPhreaK Posts: 966
    Yep, as you mentioned NetLinx is single threaded so, as far as I am aware, re-using a loop counter variable won't cause any issues.

    That being said, it is good practice however to limit the scope of any variables as much as possible. In the case of your loops if you are just utilizing a variable as a counter this can be achieved with a stack_var:
    {
    	stack_var integer i
    	
    	for (i = /* init statement */; /* condition */; /* operation */) {
    		// do stuff
    	}
    }
    
  • ericmedleyericmedley Posts: 4,177
    Also, it's a good idea to run the loop backwards if you can. It actually goes faster since there is fewer calculations being performed.

    Ex:
    stack_var integer nloop;
    for(nloop=length_array(My_TPs);nloop;nloop--) {
      Update_TP_feedback(nloop);
      }
    
    
  • DHawthorneDHawthorne Posts: 4,584
    I'll typically name my counters differently in a nested loop just to avoid confusion. You can get some weird bugs if you reference the wrong counter by mistake in the op code.
  • amclainamclain Posts: 41
    ericmedley wrote: »
    Also, it's a good idea to run the loop backwards if you can. It actually goes faster since there is fewer calculations being performed.

    Ex:
    stack_var integer nloop;
    for(nloop=length_array(My_TPs);nloop;nloop--) {
      Update_TP_feedback(nloop);
      }
    

    Having worked with several programming languages and embedded devices, I don’t think that’s accurate. Running a loop backwards will only make the program harder to understand if the loop makes more sense when run forward. Just because the condition in a for loop doesn’t have a comparison operator doesn’t mean that fewer calculations are performed. In the code above, the implicit comparison acts like nloop != 0. If you look at the PowerPC instruction set, which I believe is what the NI-X100 masters use, the for loop’s condition statement is going to get compiled down to the cmp instruction. This instruction compares the values in two CPU registers no matter how the code you write may look. If you don’t give the compiler a value to compare, it figures one out when it builds the machine instruction.

    Basically, write code in a way that makes sense when read and let the compiler do the machine-level optimizations.

    Also relevant to this thread is the tech note on why loops in mainline are bad.
  • PhreaKPhreaK Posts: 966
    amclain wrote: »
    Having worked with several programming languages and embedded devices, I don’t think that’s accurate.

    See [thread=5294]this thread[/thread].

    As you mentioned, in the majority of cases making something readable trumps optimizing the crap out of it. That being said though the two are not mutually exclusive.
  • ericmedleyericmedley Posts: 4,177
    amclain wrote: »
    Having worked with several programming languages and embedded devices, I don’t think that’s accurate. Running a loop backwards will only make the program harder to understand if the loop makes more sense when run forward. Just because the condition in a for loop doesn’t have a comparison operator doesn’t mean that fewer calculations are performed. In the code above, the implicit comparison acts like nloop != 0. If you look at the PowerPC instruction set, which I believe is what the NI-X100 masters use, the for loop’s condition statement is going to get compiled down to the cmp instruction. This instruction compares the values in two CPU registers no matter how the code you write may look. If you don’t give the compiler a value to compare, it figures one out when it builds the machine instruction.

    Basically, write code in a way that makes sense when read and let the compiler do the machine-level optimizations.

    Also relevant to this thread is the tech note on why loops in mainline are bad.

    Oh, I write normal when necessary. But I've seen backwards loops in all the languages I've programmed. It's pretty commonly used for exactly the same reasons mentioned in the thread referenced in Phreak's reply. I first ran into it when modifying some C++ code back in 1999. The science behind it is pretty easy to understand and makes total sense. I only use it when the direction of something doesnt matter. For example if you're populating a bunch of text boxes on a TP what difference does the order make? As for the importance or lack thereof of code optimization: I've written code for some big systems that sped up noticably after doing some pretty hardcore optimizing.
  • jjamesjjames Posts: 2,908
    For anyone stumbling across this thread, it's important to throw out that what Phreak did is absolutely okay anywhere in code.

    Take this for example:
    button_event[dvTp,1]
    {
      push:
      {
        send_command dvCable,"'SP',1";
        {
            stack_var integer i;
            for(i = 1; i <= something; i++){ /* Do something */}
        }
      }
    }
    

    You can use brackets anywhere to create an extra scope to define stack_vars if you'd like.
  • Technotes can be helpful

    I'd like to point out in DebugModuleExample from TechNote 875, tokenizer.axi includes this in the file revision comments:
    (*  COMMENTS:                                              *)
    (*  - Speed tweaks                                         *)
    (*    * Reduce FOR and WHILE loop control expressions to   *)
    (*      only the variable name where possible.             *)
    (*    * Avoid use of LENGTH_ARRAY or LENGTH_STRING() in    *)
    (*      FOR or WHILE loop control expressions.             *)
    (*    For example:                                         *)
    (*    Bad  -   FOR (i = 1; i <= LENGTH_STRING(str); i++)   *)
    (*    Good -   len = LENGTH_STRING(str)                    *)
    (*             FOR (i = 1; i <= len; i++)                  *)
    (*    Better - FOR (i = LENGTH_STRING(str); i > 0; i--)    *)
    (*    Best -   FOR (i = LENGTH_STRING(str); i; i--)        *)
    
  • amclainamclain Posts: 41
    Thanks for the Optimize Your Code thread Phreak; definitely interesting. I got curious and ran a benchmark of my own to see how NetLinx handles the different loops. Attached is the source code for anyone who wants to play with it.

    It looks like I was wrong about how NetLinx handles “i > 0” versus plain old “i”. However, it seems that the only loop that causes a drastic difference is when a function is in the evaluation section, like “for (i = 1; i < length_array(data); i++)”. That makes sense because the function will be called during each pass of the loop. Evaluating primitives doesn’t seem to make a big difference as to how the loop is written.
    Line      1 :: Loops will be iterated 100000 times. - 18:08:08
    Line      2 :: Performing benchmarks... - 18:08:08
    Line      3 :: Memory Available = 39788552 <38648> - 18:08:09
    Line      4 :: for (i = 1; i < x; i++) - 18:08:12
    Line      5 :: 4460ms - 18:08:12
    Line      6 :: for (i = x; i > 0; i--) - 18:08:17
    Line      7 :: 4577ms - 18:08:17
    Line      8 :: for (i = x; i; i--) - 18:08:21
    Line      9 :: 4113ms - 18:08:21
    Line     10 :: Memory Available = 39688496 <100056> - 18:08:22
    Line     11 :: for (i = 1; i < length_array(data); i++) - 18:08:28
    Line     12 :: 6823ms - 18:08:28
    Line     13 :: for (i = 1; i <= len; i++) - 18:08:32
    Line     14 :: 4546ms - 18:08:32
    Line     15 :: for (i = length_array(data); i > 0; i--) - 18:08:37
    Line     16 :: 4536ms - 18:08:37
    Line     17 :: for (i = length_array(data); i; i--) - 18:08:41
    Line     18 :: 4111ms - 18:08:41
    Line     19 :: Done. - 18:08:41
    
    ericmedley wrote: »
    As for the importance or lack thereof of code optimization: I've written code for some big systems that sped up noticably after doing some pretty hardcore optimizing.

    If you were able to get a noticeable performance boost then yeah, it sounds like the optimizations completely make sense. I think I’ve been tainted, having spent the last nine months on and off re-architecting another programmer’s various “optimizations” across several different projects that had no visible performance benefits, made future expansion difficult, and had little or no comments.
Sign In or Register to comment.