• AVR Freaks

Hot!Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size

Page: 12345 > Showing page 1 of 5
Author
trossin
Super Member
  • Total Posts : 67
  • Reward points : 0
  • Joined: 2006/06/02 11:31:50
  • Location: 0
  • Status: offline
2020/11/06 19:35:46 (permalink)
5 (2)

Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size

I noticed that if I changed my code from the conditional operator to if/else, I was able to save code space in a project that was getting near the code space limit (about 1K byte total with 20 or so locations).  I did a little experiment and found that if/else executed faster as well.  I first thought about this when I was looking at the code generated for a conditional operator and saw a great deal of goto instructions jumping to other goto instructions.
 
If you need a little speed up or need to save space.  This seems to be a secret.
 
I'm using XC8 v2.10 on an 18F26K40:  here is the code with the size and speed I detected using the simulator:
//#define COND_OP 1
void main()
{
    unsigned char Value;
    
    TRISB = 0x00;
    
    for(Value=0;;Value++){
#ifdef COND_OP
            // Default: 296 bytes for total program 105 instructions/loop
            // O2 : 226 bytes 89 inst/loop
        LATBbits.LATB7 = (Value & 0x01) ? 1 : 0;
        LATBbits.LATB6 = (Value & 0x02) ? 1 : 0;
        LATBbits.LATB5 = (Value & 0x04) ? 1 : 0;
        LATBbits.LATB4 = (Value & 0x08) ? 1 : 0;
        LATBbits.LATB3 = (Value & 0x10) ? 1 : 0;
        LATBbits.LATB2 = (Value & 0x20) ? 1 : 0;
        LATBbits.LATB1 = (Value & 0x40) ? 1 : 0;
        LATBbits.LATB0 = (Value & 0x80) ? 1 : 0;
#else
            // Default: 204 bytes for total program 51 instructions/loop
            // O2 : 102 bytes 36 inst/loop
        if(Value & 0x01) LATBbits.LATB7 = 1; else LATBbits.LATB7 = 0;
        if(Value & 0x02) LATBbits.LATB6 = 1; else LATBbits.LATB6 = 0;
        if(Value & 0x04) LATBbits.LATB5 = 1; else LATBbits.LATB5 = 0;
        if(Value & 0x08) LATBbits.LATB4 = 1; else LATBbits.LATB4 = 0;
        if(Value & 0x10) LATBbits.LATB3 = 1; else LATBbits.LATB3 = 0;
        if(Value & 0x20) LATBbits.LATB2 = 1; else LATBbits.LATB2 = 0;
        if(Value & 0x40) LATBbits.LATB1 = 1; else LATBbits.LATB1 = 0;
        if(Value & 0x80) LATBbits.LATB0 = 1; else LATBbits.LATB0 = 0;
#endif
    }
}

 
#1

92 Replies Related Threads

    1and0
    Access is Denied
    • Total Posts : 11775
    • Reward points : 0
    • Joined: 2007/05/06 12:03:20
    • Location: Harry's Gray Matter
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/06 19:55:35 (permalink)
    0 (2)
    trossin
    If you need a little speed up or need to save space.  This seems to be a secret.
     
    I'm using XC8 v2.10 on an 18F26K40:  here is the code with the size and speed I detected using the simulator:

            if(Value & 0x01) LATBbits.LATB7 = 1; else LATBbits.LATB7 = 0;


    Replace that with this:
      if (Value & 0x01)
        LATBbits.LATB7 = 1;
      if (!(Value & 0x01))
        LATBbits.LATB7 = 0;

    should shave one more instruction.
     
    #2
    upand_at_them
    Super Member
    • Total Posts : 765
    • Reward points : 0
    • Joined: 2005/05/16 07:02:38
    • Location: Pennsylvania
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/06 19:57:56 (permalink)
    +1 (3)
    Or use assembly. ;)
    #3
    JPortici
    Super Member
    • Total Posts : 1251
    • Reward points : 0
    • Joined: 2012/11/17 06:27:45
    • Location: Grappaland
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/06 20:05:25 (permalink)
    +1 (1)
    or write a routine that will mirror the bits in value (was going to suggest a LUT but you apparently have code space issues. but a sequence of 8 RLF - RRF + save/restore carry should suffice) and assign the new value directly to the port
    or consider a new hardware revision with all the lines swapped so you can assign directly
     
    in any case, ternary operators should be avoided. (Personal opinion, ugly. but is this a rule somewhere? MIRSA? god i hope so)
    #4
    1and0
    Access is Denied
    • Total Posts : 11775
    • Reward points : 0
    • Joined: 2007/05/06 12:03:20
    • Location: Harry's Gray Matter
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/06 20:05:34 (permalink)
    +1 (3)
    upand_at_them
    Or use assembly. ;)

    My C snippet will produce the same assembly. ;)
    #5
    1and0
    Access is Denied
    • Total Posts : 11775
    • Reward points : 0
    • Joined: 2007/05/06 12:03:20
    • Location: Harry's Gray Matter
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/06 20:07:23 (permalink)
    0 (2)
    Jack_M
    or write a routine that will mirror the bits in value (was going to suggest a LUT but you apparently have code space issues. but a sequence of 8 RLF - RRF + save/restore carry should suffice) and assign the new value directly to the port

    I believe the mirror is just an experiment, not his real code.  If I recall correctly, mirroring a byte takes 11 assembly instructions. ;)
     
    #6
    trossin
    Super Member
    • Total Posts : 67
    • Reward points : 0
    • Joined: 2006/06/02 11:31:50
    • Location: 0
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/06 21:52:38 (permalink)
    0
    Yes. This is just a small example to show the bad behavior of the compiler. I just have conditional operators sprinkled through my code. I thought this knowledge might be helpful to others.
    #7
    Hen
    Super Member
    • Total Posts : 122
    • Reward points : 0
    • Joined: 2018/10/24 04:01:44
    • Location: 0
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/07 00:59:57 (permalink)
    -1 (1)
    1and0
      if (Value & 0x01)
        LATBbits.LATB7 = 1;
      if (!(Value & 0x01))
        LATBbits.LATB7 = 0;

    should shave one more instruction.

    Or whenever theres no harm

    LATBbits.LATB7 = 0;
    if (Value & 0x01)  LATBbits.LATB7 = 1;

     
    #8
    Hen
    Super Member
    • Total Posts : 122
    • Reward points : 0
    • Joined: 2018/10/24 04:01:44
    • Location: 0
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/07 05:09:57 (permalink)
    0
    vexorg
    1and0
    My C snippet will produce the same assembly. ;)

    Your code produces about a dozen lines of assembly, for what could be done in 4 lines


    Nope, 1and0 is on the money here, but it may come to the compiler.
    For me his piece gives two BTFSX and two BXF.
     
    edit: that is Value is in the selected bank or in access...
    post edited by Hen - 2020/11/07 05:11:21
    #9
    1and0
    Access is Denied
    • Total Posts : 11775
    • Reward points : 0
    • Joined: 2007/05/06 12:03:20
    • Location: Harry's Gray Matter
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/07 07:32:47 (permalink)
    +1 (5)
    vexorg
    Your code produces about a dozen lines of assembly, for what could be done in 4 lines

    vexorg
    The fact that the same code on xc8 gives different results is reason enough to say assembly is better.

    vexorg
    No wonder C is slow by default.

    XC8 gives you bad code because you haven't learned to use the tools properly ;), just like there are bad assemblers and good assemblers. While it may give you different assembly code but the result of the operation is the same. Try again with XC8 in optimization level 2, which is available even in Free mode.


    post edited by 1and0 - 2020/11/07 08:38:41
    #10
    NorthGuy
    Super Member
    • Total Posts : 6471
    • Reward points : 0
    • Joined: 2014/02/23 14:23:23
    • Location: Northern Canada
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/07 08:27:22 (permalink)
    +5 (5)
    vexorg
    The fact that the same code on xc8 gives different results is reason enough to say assembly is better.



    When you write in C, you typically don't worry about assembler instructions. By using C compiler you agree to trade some inefficiency for convenience - moderate efficiency loss for moderate convenience.
     
    When you try to coerce C to produce specific assembler instructions, you actually make things harder compared to what you would get if you wrote directly in assembler. So, you in fact get minor efficiency loss coupled with added inconvenience.
     
    When I write in C, I never look at the assembler code (unless I'm interested in inner working of the compiler). Otherwise I lose what I wanted to get from C in the first place - my convenience.
     
    For example:
     
    x = a*b - c*d;

     
    It's just one line. This is very little effort and it is easily editable. I saved lots of time. Thanks to the C compiler - it did all the work for me.
     
    It will take a while to re-write in assembler, especially in PIC16 where there's no built-in multiplication. Although your code may run 2 times faster. I invested my effort and I get an efficient program which meets my timing. Thanks to my hard work.
     
    If you try to re-write this in C so that it replicates the assembler you would write, it'll take much much longer to write and still there will be some inefficiencies left. The benefits of C are lost, you spent lots of time and effort doing this, and the result is still mediocre. Why the hell would you do this?
     
    #11
    crosland
    Super Member
    • Total Posts : 2145
    • Reward points : 0
    • Joined: 2005/05/10 10:55:05
    • Location: Warks, UK
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/07 12:12:39 (permalink)
    +1 (5)
    vexorg
    The fact that the same code on xc8 gives different results is reason enough to say assembly is better.



    Same code as what? Different results to what?
     
    Your statement makes no sense.
    #12
    1and0
    Access is Denied
    • Total Posts : 11775
    • Reward points : 0
    • Joined: 2007/05/06 12:03:20
    • Location: Harry's Gray Matter
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/07 16:20:08 (permalink)
    -1 (1)
    vexorg
    ... my default xc8 setting was more than 4 lines.

    It’s fairly obvious to convert to 4 lines, why would default C make such a hash of it.

    I don't remember what is the default settings for XC8, but its optimization level 0 is dumb as a rock. It has to do with compiler designs. If you like to know more, search this forum for several long discussions on this. ;) Optimization level 1 or 2 should result in 4 lines of code for my snippet.



    #13
    crosland
    Super Member
    • Total Posts : 2145
    • Reward points : 0
    • Joined: 2005/05/10 10:55:05
    • Location: Warks, UK
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/08 06:39:54 (permalink)
    +1 (3)
    vexorg
    crosland
    vexorg
    The fact that the same code on xc8 gives different results is reason enough to say assembly is better.

     Same code as what? Different results to what? Your statement makes no sense.

    Same code as 1and0 posted up, and different outcome as posted up. If you are struggling to count, my default xc8 setting was more than 4 lines.

    It’s fairly obvious to convert to 4 lines, why would default C make such a hash of it.

     
    The same code is the same code and will produce the same results. The "same code" cannot produce "different" result, unless you use a different compiler or different compiler settings.
     
    Your statement made no sense as you did not say HOW you used the compiler.
     
    Not using the available optimization is no reason to say assembly is better.
    #14
    1and0
    Access is Denied
    • Total Posts : 11775
    • Reward points : 0
    • Joined: 2007/05/06 12:03:20
    • Location: Harry's Gray Matter
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/08 13:14:13 (permalink)
    +1 (3)
    vexorg
    Optimisation 1 or 2 doesn't give 4 lines, still about 6.

    In Post #1, OP stated he's using a 18F26K40, where LATB is located in Access RAM. If banking is required for the variable "Value" my snippet should take only 5 instructions (4 plus a MOVLB). So, what are the 6 instructions that you get in optimization level 2? Post it.
     
    vexorg
    Due to the app only have those few lines it, it changes it so the the logic test on the last 'if' skips a "goto start" rather than set the bit.

    Not quite understand you're saying here.
     
    vexorg
    Cant believe optimisation 0 is so bad.Seeing that, I'd choose assembly over C for any 8 bit pic. Assembly just fits naturally to how the pic runs.

    Like I said earlier, it has to do with compiler designs. It starts with generic code templates to reserve places for all possibilities for a construct. Then later the optimizer cleans it up. In optimization level 0 the optimizer is not invoked; hence the bad code.
     
    vexorg
    32 bit pics are a different story, their the assembly list code bears no relation to anything microchip since it's MIPS. Might stick with C for that.

    Have you written pure assembly code for a PIC32?
     
    #15
    Hen
    Super Member
    • Total Posts : 122
    • Reward points : 0
    • Joined: 2018/10/24 04:01:44
    • Location: 0
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/08 21:46:51 (permalink)
    0
    1and0
    In Post #1, OP stated he's using a 18F26K40, where LATB is located in Access RAM. If banking is required for the variable "Value" my snippet should take only 5 instructions (4 plus a MOVLB). So, what are the 6 instructions that you get in optimization level 2? Post it.

    Maybe the compiler (because of this) has to restore BSR afterwards..
     
    Apart from that, we are entering the religious domain and nobody will convince anybody anything anymore.
     
    #16
    1and0
    Access is Denied
    • Total Posts : 11775
    • Reward points : 0
    • Joined: 2007/05/06 12:03:20
    • Location: Harry's Gray Matter
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/08 22:51:05 (permalink)
    -1 (1)
    Hen
    Maybe the compiler (because of this) has to restore BSR afterwards..
     
    Apart from that, we are entering the religious domain and nobody will convince anybody anything anymore.

    No, it should not "restore" BSR afterward; however, it will set BSR before accessing the next register in another bank.

    I think I see now what vexorg means by this
    vexorg
    Due to the app only have those few lines it, it changes it so the the logic test on the last 'if' skips a "goto start" rather than set the bit.

    This
      while (1) {
        if (Value & 0x01)
          LATBbits.LATB7 = 1;
        if (!(Value & 0x01))
          LATBbits.LATB7 = 0;
      }

    generates this
    start:  btfsc   Value,0
            bsf     LATB,7
            btfsc   Value,0
            bra     start
            bcf     LATB,7
            bra     start

    because it mingles with the while() loop.

    So I suggest to him to give this a try
      while (1) {
        if (Value & 0x01)       //  btfsc   Value,0
          LATBbits.LATB7 = 1;   //  bsf     LATB,7
        if (!(Value & 0x01))    //  btfss   Value,0
          LATBbits.LATB7 = 0;   //  bcf     LATB,7
        NOP();
      }

    #17
    Hen
    Super Member
    • Total Posts : 122
    • Reward points : 0
    • Joined: 2018/10/24 04:01:44
    • Location: 0
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/09 02:24:46 (permalink)
    0
    1and0
    No, it should not "restore" BSR afterward; however, it will set BSR before accessing the next register in another bank.

    Ofcourse, but still you might add two MOVLB if you insert this amongst other statements...
     
    Back to the original post, to mirror port bits without loop shifting:
      Value = 0x00;
     
      if (PORTBbits.LATB0) Value |= (1<<7);
     
      if (PORTBbits.LATB1) Value |= (1<<6);
     
      if (PORTBbits.LATB2) Value |= (1<<5);
     
      if (PORTBbits.LATB3) Value |= (1<<4);
     
      if (PORTBbits.LATB4) Value |= (1<<3);
     
      if (PORTBbits.LATB5) Value |= (1<<2);
     
      if (PORTBbits.LATB6) Value |= (1<<1);
     
      if (PORTBbits.LATB7) Value |= (1);
     
      LATB = Value;
     

    That would be 19 instructions excluding BSR overhead.
     
    You coud even do a translation table (set-up and aligned to one ragisterbank):
      LATB = Mirror[PORTB];
     

    That may be 3 instructions (LFSR+MOVFF+MOVFF) excluding BSR and FSR overhead.
     
    edit: PORTBbits should not be LATBX but RBX...
    post edited by Hen - 2020/11/09 02:26:08
    #18
    1and0
    Access is Denied
    • Total Posts : 11775
    • Reward points : 0
    • Joined: 2007/05/06 12:03:20
    • Location: Harry's Gray Matter
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/09 06:48:24 (permalink)
    -1 (1)
    vexorg
    1and0
    because it mingles with the while() loop.

    That's it exactly.
     
    Although this was the entire main:

     
    void main()
    {
        unsigned char value =PORTAbits.RA0;
        
     if (value & 0x01)
        LATBbits.LATB7 = 1;
      if (!(value & 0x01))
        LATBbits.LATB7 = 0;    
    }

    Didn't expect that to go back to the start, thought it would just stop at the end.

    We all know that you shouldn't allow main() to fall through in an embedded application program. It may branch to the beginning of main(), the reset vector, or other places dependent on the compiler. You should always have an infinite loop in your main() for an embedded application.
     
    #19
    trossin
    Super Member
    • Total Posts : 67
    • Reward points : 0
    • Joined: 2006/06/02 11:31:50
    • Location: 0
    • Status: offline
    Re: Don't use a = (blah) ? 1 : 0; instead use if(blah) a=1; else a=0; for speed and size 2020/11/09 09:07:48 (permalink)
    0 (2)
    The post is not about mirroring bits. It is about a simple change to improve code size and speed. I just created a simple example instead of dumping my 62K byte program which would not be very helpful.

    Please create a new thread if you want to discuss ways to flip bits
    #20
    Page: 12345 > Showing page 1 of 5
    Jump to:
    © 2021 APG vNext Commercial Version 4.5