Alternatives to if/else jump conditions - Page 1
Forum section: electronics / general

author: sangar
author_rank: Regular Contributor
timestamp: July 01, 2017, 05:34:16 am
content: Hello all,I have a 16- bit array of 5 locations.I will write these 5 values one by one when certain condition met. After writing 5 th location value, I have to go to first location.Here, instead using if/else condition, I want to use some other conditions which is better than if/else.code:Code:uint16_t output_current={0x0400,0x0403,0x0408,0x0409,0x040C};Code:chnum++;if(chnum==5)chnum=0;ad7124_regs.value=output_current;Please suggest a solution.Thanks,Sangar

author: Psi
author_rank: Super Contributor
timestamp: July 01, 2017, 05:45:31 am
content: Have a look at case/switch statement, i'm not sure if it will help what you're doing but maybe of some use.We need more information about what you are trying to do.Can you write the code as if/else, then we can see if there is a simpler way to do it.

author: Nusa
author_rank: Super Contributor
timestamp: July 01, 2017, 05:51:43 am
content: Is it the modulo function that you're after?chnum = (chnum + 1) % 5;

author: orin
author_rank: Frequent Contributor
timestamp: July 01, 2017, 06:14:55 am
content: Quote from: Nusa on July 01, 2017, 05:51:43 amYou could do that, but I'd guess it wouldn't be faster.It depends on the CPU and compiler/assembler. If there is no hardware divide instruction, it's definitely slower.Some CPUs have conditional instructions, so the sequence:Code:if(chnum==5)chnum=0;becomes something like:Code:cmp reg, 5ldreq reg, 0The second instruction only executes if reg == 5.I read that the branch prediction in modern CPUs is so good that such code has no benefit and you might as well use the traditional if-then-else format.So I say write the code as in the original post and let the compiler take care of the details.

author: hamster_nz
author_rank: Super Contributor
timestamp: July 01, 2017, 06:30:28 am
content: Have a 'next' array with values of 1,2,3,4 and 0.Update chnum from that. (E.g chnum = next; )

author: JPortici
author_rank: Super Contributor
timestamp: July 01, 2017, 06:36:04 am
content: if chnum is not inizialized it may start from let's say 6.best case: compiler adds boundary check so it automatically wraps around, but doesn't change variablenot so best case: array becomes a pointer to whole memoryi think i saw this one on earlier version of mikroC for pic16 many years agocommon case: address error.this situation will stay the same until chnum overflows and restarts from zero, which could be many many many cyclesthis is why i always useCode:chnum++;if (chnum>=5)chnum = 0orCode:chnum = (chnum+1) & xwhere the number of elements is a power of two, x is (2^n - 1)i don't think you can really do it faster than this.

author: John Coloccia
author_rank: Super Contributor
timestamp: July 01, 2017, 08:04:16 am
content: What's wrong with the if statement? What are you trying to accomplish that the if statement doesn't accomplish?

author: hamster_nz
author_rank: Super Contributor
timestamp: July 01, 2017, 08:06:51 am
content: Quote from: John Coloccia on July 01, 2017, 08:04:16 amMy money is on that they are counting CPU cycles for an interface, and trying to avoid hand coded assembler.

author: sangar
author_rank: Regular Contributor
timestamp: July 01, 2017, 09:03:52 am
content: Hello All,Thanks. Let me take it into the detailed explanation.Exp:I am using ADC which have 5 channels to be connected to RTD. Limitation is only one source of output current available to feed to RTD. Default, It is connected to Channel 0. After channel 0 conversion completed, I switch the output current to channel 1 by writing location 1 value in the array. Like this way, I do the same for the remaining channels. After channel-4 conversion completed, I have to switch the output current to channel-0. Chnum is automatic variable.Here is the code:Code:void ad7124_handler(void){int32_t ret;uint8_t chnum;ret=AD7124_ReadData(ad7124_handler, &sample); //reading sample from ADCif (ret==0){return; //error occurs}chnum=sample & 0x0f; //Extracting channel number from Sample.It repeat like 0,1,2,3,4.. 0,1,2,3,4...0,1,2,3,4chnum++;if(chnum==5)chnum=0;ad7124_regs.value=output_current;ret=AD7124_WriteRegister(ad7124_handler,ad7124_regs);//writing output current to ADCif (ret==0){return; //error occurs}}Thanks,Sangar

author: hamster_nz
author_rank: Super Contributor
timestamp: July 01, 2017, 09:37:39 am
content: Not because it is practical, just because it looks nice (well, at least to me)...Code:#include <stdio.h>int main(int argc, char *argv[]){int a = 0;while(1) {a += ((1-a) * (a-0) * (a-1) * (a-2) * (a-3))>>4;a ++;printf("%i\n",a);}}

author: Kjelt
author_rank: Super Contributor
timestamp: July 01, 2017, 09:39:50 am
content: Nothing wrong with using the if/else in this situation.Just a small improvement for defensive programming, since you can not guarantee the value of sample (glitch or error or whatever), I would recommend doing the check over every value larger or equal to 5. And the brackets help reading improvement although this will start an entire different discussion I don't want to go into right nowSoCode:if(chnum >= 5){chnum=0;}

author: hamster_nz
author_rank: Super Contributor
timestamp: July 01, 2017, 09:51:41 am
content: Quote from: Kjelt on July 01, 2017, 09:39:50 amTo make it truly defensive you need to pick up negative values too:Code:if((unsigned)chnum >= 5){chnum=0;}However, if you can't guarantee the value of 'chnum', you ay have bigger problems to worry about...If just the 'shape' of the code offends the original poster they can use the '?' operator:Code:int main(int argc, char *argv[]){int a = 0;while(1) {a = (a >= 4) ? 0 : a+1;printf("%i\n",a);}}

author: nctnico
author_rank: Super Contributor
timestamp: July 01, 2017, 10:46:46 am
content: Quote from: hamster_nz on July 01, 2017, 09:51:41 amDeclaring chnum as unsigned takes care of negative numbers because an unsigned cannot become negative. Besides that you can never really guarantee values of variables are right so when used for an array index or driving hardware you need to check the range. If you don't then subtile bugs can linger forever because they are impossible to track down.

author: hamster_nz
author_rank: Super Contributor
timestamp: July 01, 2017, 11:07:56 am
content: Quote from: nctnico on July 01, 2017, 10:46:46 amOh, I see it is uint8_t in the later code snippet... yeah, no cast needed.

author: edgelog
author_rank: Regular Contributor
timestamp: July 01, 2017, 11:21:36 am
content: Consider counting down instead of up. A comparison with 0 is much faster than a comparison to any other number and your compiler will optimize that better.

author: John Coloccia
author_rank: Super Contributor
timestamp: July 01, 2017, 02:35:42 pm
content: FWIW, on code review I would reject anything but a simple if statement or mod operator...including counting down. I would also reject the number '5'. Personally, I would lean towards using the mod operator because it's a very common pattern and anyone looking at it will immediately understand exactly what's going on.Optimize your code for readability, simplicity and maintainability. Optimize for size and performance only if you absolutely have to, and even then don't worry about individual instructions unless there's really a need to.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 01, 2017, 02:49:29 pm
content: Quote from: John Coloccia on July 01, 2017, 02:35:42 pmAre you implying that most people looking at the "if" statement wouldn't immediately understand exactly what's going on?

author: Kjelt
author_rank: Super Contributor
timestamp: July 01, 2017, 03:01:18 pm
content: Quote from: John Coloccia on July 01, 2017, 02:35:42 pmI agree that magical numbers are a no go.With a for statement or simple iteration i personally feel it is a grey area but better in this case would be to define the arraysize with a #define and use the same define in the if statement. At least it is clear that hey belong together.

author: NivagSwerdna
author_rank: Super Contributor
timestamp: July 01, 2017, 03:07:44 pm
content: Quote from: sangar on July 01, 2017, 05:34:16 amStick with your original. It's a simple example of a circular buffer type thing. Perfectly reasonable.If you want to change the pattern then you could introduce a for loop or ifs or case but you don't gain anything really.Comparison with literal 5 will be one instruction; perfectly reasonable from a performance point of view.Keep it simple.Quote from: sangar on July 01, 2017, 05:34:16 amThat will potentially give you a result between 0 and 0x0f.... i.e. 0 and 15. Beware overflow

author: John Coloccia
author_rank: Super Contributor
timestamp: July 01, 2017, 03:13:04 pm
content: Quote from: NorthGuy on July 01, 2017, 02:49:29 pmI think the mod operator is most obvious and requires the least amount of thought. Just like when you're writing a loop, you can write it:for (i = 0; i < num_things; i++)orfor (i = 0; i <= last_index; i++)It's the same end result, but unless there was a good reason for doing it the second way I would reject it 100% of the time on code review, simply because it adds a little cognitive friction where there could actually be zero cognitive friction. My general approach to software is it's hard enough under the best of conditions, so I try and drive things to be as simple and standard as I can possibly make it. I feel like I did a good job if my software looks boring and easy.Anyway, the mod operator also guarantees that no matter what chnum comes in as, you can't possibly get a result that's ever out of range. That also reduces a little friction when you're code reviewing or looking for an unexplained crash, for example.Hope that helps explain my reasoning a bit. It's sort of minutia and doesn't really matter either way in this case, but I think if you're anal about things like this at every step, the overall affect on a code base can be pretty significant.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 01, 2017, 03:36:56 pm
content: Quote from: hamster_nz on July 01, 2017, 06:30:28 amThat's an FPGA approach

author: madires
author_rank: Super Contributor
timestamp: July 01, 2017, 03:37:10 pm
content: Instead of addressing the array by an index number you could address it via a pointer. Based on the MCU that could save a few bytes and cycles.

author: Kalvin
author_rank: Super Contributor
timestamp: July 01, 2017, 03:42:32 pm
content: Quote from: madires on July 01, 2017, 03:37:10 pmAnd the table could also contain the pointer to the next table element. In that way there won't be any need for checking array bounds at all, and no need for if-statement either.Edit: Alternatively the table element could contain an entry for the next table index, if one doesn't want to use pointers. For more robust code, on could create an array of 8 elements, and initialize the unused entries so that they will lead to the first entry to the table. Then one could access the table as [index & 0x07].

author: NorthGuy
author_rank: Super Contributor
timestamp: July 01, 2017, 03:58:51 pm
content: Quote from: sangar on July 01, 2017, 09:03:52 amI don't think you need any optimizations here. Looks like a slow process to me anyway. Also, it might not be a good idea to rely on the sample to supply a channel number. But if you want to make your current version quicker, you can do this:Code:uint16_t next_output_current={0x0403,0x0408,0x0409,0x040C,0x0400,0x0400,0x0400,0x0400};ad7124_regs.value=next_output_current[sample & 0x07];Also, it will be faster if, instead of using "ad7124_regs" you create a variable holding a single element of it. I think it might be possible to simplify it further if we knew what the elements of "ad7124_regs" look like.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 01, 2017, 04:09:44 pm
content: Quote from: John Coloccia on July 01, 2017, 03:13:04 pmEvery programmer has OCD. Me tooHowever, I don't think it makes the code better. You think of better algorithms, better data structures. This makes your code simpler, more efficient, and at the same time easier to understand (and maintain if you wish).

author: Nusa
author_rank: Super Contributor
timestamp: July 01, 2017, 05:28:54 pm
content: Quote from: NorthGuy on July 01, 2017, 03:58:51 pmhttps://github.com/analogdevicesinc/no-OS/blob/master/drivers/ad7124/AD7124.hAD7124_IOCon1 is a constant declared in an enum statement, so it'll be evaluated at compile time. No speed advantage to making it look different.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 01, 2017, 05:58:02 pm
content: Quote from: Nusa on July 01, 2017, 05:28:54 pmThis is true. Besides, even if it wasn't, the function being called does so much stuff that saving few cycles while calling it simply wouldn't matter.

author: sokoloff
author_rank: Super Contributor
timestamp: July 01, 2017, 07:22:00 pm
content: Quote from: Kjelt on July 01, 2017, 03:01:18 pmAt a prior company, we had a fairly hopeless programmer. At one code review, we suggested that he not use a magic constant, but should rather make a #define and use that instead.His code came back, and I kid you not at all, with this:Code:#define SEVENTEEN 17and dutifully used SEVENTEEN in the subsequent comparisons.

author: John Coloccia
author_rank: Super Contributor
timestamp: July 01, 2017, 07:43:16 pm
content: Quote from: sokoloff on July 01, 2017, 07:22:00 pmAh...one of the gifted engineers that can write FORTRAN in any language.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 01, 2017, 08:23:52 pm
content: Quote from: John Coloccia on July 01, 2017, 07:43:16 pmCode:#define NUMBER_OF_CHANNELS 5#define CURRENT_ARRAY_SIZE 8#define CURRENT_ARRAY_MASK (CURRENT_ARRAY_SIZE-1)#define CURRENT_FOR_CHANNEL_0 0x400#define CURRENT_FOR_CHANNEL_1 0x403#define CURRENT_FOR_CHANNEL_2 0x408#define CURRENT_FOR_CHANNEL_3 0x409#define CURRENT_FOR_CHANNEL_4 0x40C#define CURRENT_FOR_UNKNOWN_CHANNEL (CURRENT_FOR_CHANNEL_0)uint16_t next_output_current= {CURRENT_FOR_CHANNEL_1,CURRENT_FOR_CHANNEL_2,CURRENT_FOR_CHANNEL_3,CURRENT_FOR_CHANNEL_4,CURRENT_FOR_CHANNEL_0,CURRENT_FOR_UNKNOWN_CHANNEL,CURRENT_FOR_UNKNOWN_CHANNEL,CURRENT_FOR_UNKNOWN_CHANNEL};ad7124_regs.value=next_output_current[sample & CURRENT_ARRAY_MASK];Better?

author: John Coloccia
author_rank: Super Contributor
timestamp: July 01, 2017, 09:18:42 pm
content: Quote from: NorthGuy on July 01, 2017, 08:23:52 pmActually, if you do it like that you need to be very careful how you set your current array size or the mask will fail. Honestly, I'd be happy withCode:uint16_t channel[] = { 0x400, 0x403, 0x408, 0x409, 0x40C };#define NUM_CHANNELS (sizeof(channel)/sizeof(channel))...chnum = (sample & 0xF) + 1;chnum %= NUM_CHANNELS;I'm really not big on #defining everything, and obfuscating the world. In fact, in our new embedded coding standards I've even eliminated typedefs nearly altogether, after seeing the mess that less than careful typedefs and inconsistent usage can do on a project we just inherited. After thinking about it for a minute, it became clear that the easiest way to prevent us from ever doing the same thing is to just simply eliminate the damn things.Code:enum power_states curr_state;will never make you wonder just what the heck curr_state actually is.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 01, 2017, 09:49:52 pm
content: Quote from: John Coloccia on July 01, 2017, 09:18:42 pmVery tidy.A single extra division certainly won't hurt when you call a function which calculates CRC and communicates through SPI.I wouldn't use the division myself, but I wouldn't have such wasteful functions neither.

author: sokoloff
author_rank: Super Contributor
timestamp: July 01, 2017, 10:24:42 pm
content: Both operands of the division are known at compile time. Most compilers will optimize away the division and just place a constant. (Not in the define per-se, but in the usage of the define.)Code:#include <iostream>using namespace std;int array;int a = 0;#define a_l sizeof(array)/sizeof(array)int main(){cout << "a_l = " << a_l << endl;return a_l;}Code:0000000000400880 <main>:400880:55push rbp400881:48 89 e5mov rbp,rsp400884:53push rbx400885:48 83 ec 08sub rsp,0x8400889:bb 2d 00 00 00mov ebx,0x2d40088e:be b0 09 40 00mov esi,0x4009b0400893:bf e0 0d 60 00mov edi,0x600de0400898:e8 b3 fe ff ffcall 400750 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@plt>40089d:48 89 demov rsi,rbx4008a0:48 89 c7mov rdi,rax4008a3:e8 b8 fe ff ffcall 400760 <std::ostream::operator<<(unsigned long)@plt>4008a8:be 80 07 40 00mov esi,0x4007804008ad:48 89 c7mov rdi,rax4008b0:e8 bb fe ff ffcall 400770 <std::ostream::operator<<(std::ostream& (*)(std::ostream&))@plt>4008b5:b8 2d 00 00 00mov eax,0x2d4008ba:48 83 c4 08add rsp,0x84008be:5bpop rbx4008bf:5dpop rbp4008c0:c3ret45 is 0x2d. You'll see that is directly loaded into ebx prior to the call to cout the unsigned long and into eax prior to returning from main. There is no division happening at runtime.

author: sokoloff
author_rank: Super Contributor
timestamp: July 01, 2017, 10:27:18 pm
content: That was with stock g++, no options passed to the compiler and then objdump to disassemble.Code:[ec2-user@]$ g++ --versiong++ (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9)[ec2-user@]$ objdump -drwC -Mintel ./a.outI'd wager most compilers will doconstant foldinglike this.

author: John Coloccia
author_rank: Super Contributor
timestamp: July 01, 2017, 10:37:45 pm
content: Yeah, it actually never even crossed my mind that the sizeof/sizeof might be implemented as a division. I just assumed it would become a constant. I guess some compiler might actually do a division, but I just assume that things like this become constants.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 01, 2017, 10:47:12 pm
content: Quote from: sokoloff on July 01, 2017, 10:24:42 pmThe division happens at this line:Code:chnum %= NUM_CHANNELS;<edit>To clarify: "chnum" is being divided by "NUM_CHANNELS"</edit>Even though NUM_CHANNELS is known at compile time, I don't think the compiler will use this to eliminate the division.

author: sokoloff
author_rank: Super Contributor
timestamp: July 01, 2017, 11:26:14 pm
content: Ah, your objection is to the modulus, not the sizeof/sizeof division. That's a fair point in a performance critical application.

author: John Coloccia
author_rank: Super Contributor
timestamp: July 01, 2017, 11:35:51 pm
content: Quote from: sokoloff on July 01, 2017, 11:26:14 pmI suspect that implementing it as a division is generally not optimal and many compilers will implemented more cleverly depending on the specific platform.

author: Mechatrommer
author_rank: Super Contributor
timestamp: July 02, 2017, 12:02:58 am
content: Quote from: hamster_nz on July 01, 2017, 06:30:28 am+1, if you really anal of not using if... but the drawback to this is its requires memory. and i bet fetching values from this memory (assign pointer into register and read memory from assigned register) will take more time, but the good thing is the code will run at uniform clock cycle at each chnum value. "if" statement if optimized correctly will take only 2-3 cycles to run, the shortest path you can get, but the drawback it will take more cycle when it reaches the boundary, hence not uniform execution resulting in jitter (depending on what you want), but since you are doing this in C, i dont see much point in uniform code execution...

author: Nusa
author_rank: Super Contributor
timestamp: July 02, 2017, 12:42:44 am
content: Quote from: John Coloccia on July 01, 2017, 11:35:51 pmI went and tried this code snippet onhttps://godbolt.org/, which is a fun little tool for cases like this:int test(int num, int div) {num = num % div;num = 10 % div;num = num % 10;return num;}The results varied depending with compiler selection, but x86-64 gcc 7.1 used division for the first two cases, but switched to a customized implementation using multiply for the 3rd case when the divisor is a constant. I think I understand what it's doing, but it's more fun if you look at it yourself.

author: sokoloff
author_rank: Super Contributor
timestamp: July 02, 2017, 01:02:12 am
content: Quote from: Nusa on July 02, 2017, 12:42:44 amThanks for this tool link! That would have saved me several hours over the last couple years...

author: NorthGuy
author_rank: Super Contributor
timestamp: July 02, 2017, 03:06:53 am
content: Quote from: Nusa on July 02, 2017, 12:42:44 amFor "x%5" it's doing division through multiplication: x/5 = x*1717986919/0x200000000;Division by 0x200000000 is easily implemented through shifts.It's amazing that this still works faster on the CPU with hardware division. However, I think for the case where x is from 0 to 15, Intel's hardware division with early termination could produce faster results than multiplication.

author: orin
author_rank: Frequent Contributor
timestamp: July 02, 2017, 05:30:34 am
content: Quote from: NorthGuy on July 02, 2017, 03:06:53 amAfter the division by multiplication, then what does it do? (x - result * 5) I'd guess to get the remainder. For this problem, I can't imagine that being faster than: if ( ++x == 5 ) x = 0;If you are worried about jitter, then if ( ++x == 5 ) x = 0; else nop()*; where nop() is whatever forces your compiler to insert a no-operation opcode. *one or more depending on how long x = 0; takes.Another point on using the % operator and speed. On a highly pipelined processor, if the next instruction or so uses the result, it may stall the pipeline until the result is ready - negating the gains from pipelining and resulting in slower execution.Did the OP even tell us what CPU this is running on?FWIW, on a PIC, this sequence can be done in constant time using a test/skip instruction - something like (there are many variations):Code:; Bank switching code here if necessaryINCF chnum, WMOVWF chnumXORLW 5 ; ++chnum == 5 ? If so, the Z status bit will be setBTFSC STATUS,ZMOVWF chnum ; only executes if the Z flag is set and conveniently, W is zero; if Z is clear, in effect, a NOP instruction is executed insteadIf you want to make the test >= 5, then you'd use SUBLW* instead of XORLW and tear your hair out working out which status flag to test... but it would be the same basic instruction sequence.* very basic PICs don't have this instruction, but that can be worked around.

author: Kalvin
author_rank: Super Contributor
timestamp: July 02, 2017, 08:41:11 am
content: Without if's or divs. Channels 5 - 7 are used for padding the unused entries. Note for the Arduino: The const tables are not in ROM address spscae:Code:#include <stdint.h>typedef struct{uint16_t data; // output datauint8_t next; // next channel index}ChannelEntry_t;const ChannelEntry_t channel[] = {{0x400, 1}, /* 0 */{0x403, 2}, /* 1 */{0x408, 3}, /* 2 */{0x409, 4}, /* 3 */{0x40C, 0}, /* 4 */{0x400, 0}, /* 5, unused */{0x400, 0}, /* 6, unused */{0x400, 0} /* 7, unused */};extern void do_something(uint16_t dataval);uint8_t chnum = 0;int main (){while (1){uint16_t dataval = channel[chnum & 0x07].data;chnum = channel[chnum & 0x07].next;do_something(dataval);}return 0;}Arduino AVR C++ compiler output disassembly:Code:Disassembly of section .text.startup.main:00000000 <main>:0:80 91 00 00ldsr24, 0x00004:87 70andir24, 0x07; 76:90 e0ldir25, 0x00; 08:fc 01movwr30, r24a:ee 0faddr30, r30c:ff 1fadcr31, r31e:e8 0faddr30, r2410:f9 1fadcr31, r2512:e0 50subir30, 0x00; 014:f0 40sbcir31, 0x00; 016:80 81ldr24, Z18:91 81lddr25, Z+1; 0x011a:22 81lddr18, Z+2; 0x021c:20 93 00 00sts0x0000, r1820:0e 94 00 00call0; 0x0 <main>24:00 c0rjmp.+0; 0x26 <__zero_reg__+0x25>Same thing using pointers:Code:#include <stdint.h>struct ChannelEntry_t;typedef struct ChannelEntry_t{uint16_t data; // output dataconst ChannelEntry_t * next; // next channel entry}ChannelEntry_t;const ChannelEntry_t channel[] = {{0x400, &channel}, /* 0 */{0x403, &channel}, /* 1 */{0x408, &channel}, /* 2 */{0x409, &channel}, /* 3 */{0x40C, &channel} /* 4 */};extern void do_something(uint16_t dataval);const ChannelEntry_t * chdata = &channel;int main (){while (1){uint16_t dataval = chdata->data;chdata = chdata->next;do_something(dataval);}return 0;}and the disaasembly:Code:Disassembly of section .text.startup.main:00000000 <main>:0:e0 91 00 00ldsr30, 0x00004:f0 91 00 00ldsr31, 0x00008:80 81ldr24, Za:91 81lddr25, Z+1; 0x01c:22 81lddr18, Z+2; 0x02e:33 81lddr19, Z+3; 0x0310:30 93 00 00sts0x0000, r1914:20 93 00 00sts0x0000, r1818:0e 94 00 00call0; 0x0 <main>1c:00 c0rjmp.+0; 0x1e <__zero_reg__+0x1d>The pointer implementation is pretty efficient and straight forward, and it doesn't require if's nor modulus.

author: Kalvin
author_rank: Super Contributor
timestamp: July 02, 2017, 08:56:17 am
content: Same thing using Ubuntu GCC-compiler with -O1 option:Code:main:.LFB0:.cfi_startprocsubq$8, %rsp.cfi_def_cfa_offset 16.L2:movzblchnum(%rip), %eaxandl$7, %eaxmovzwlchannel(,%rax,4), %edimovzblchannel+2(,%rax,4), %eaxmovb%al, chnum(%rip)movzwl%di, %edicalldo_somethingjmp.L2.cfi_endprocCode:#include <stdint.h>struct ChannelEntry_t;struct ChannelEntry_t{uint16_t data; // output dataconst struct ChannelEntry_t * next; // next channel entry};const struct ChannelEntry_t channel[] = {{0x400, &channel}, /* 0 */{0x403, &channel}, /* 1 */{0x408, &channel}, /* 2 */{0x409, &channel}, /* 3 */{0x40C, &channel} /* 4 */};extern void do_something(uint16_t dataval);const struct ChannelEntry_t const * chdata = &channel;int main (){while (1){uint16_t dataval = chdata->data;chdata = chdata->next;do_something(dataval);}return 0;}Code:main:.LFB0:.cfi_startprocsubq$8, %rsp.cfi_def_cfa_offset 16.L2:movqchdata(%rip), %raxmovzwl(%rax), %edimovq8(%rax), %raxmovq%rax, chdata(%rip)movzwl%di, %edicalldo_somethingjmp.L2.cfi_endproc

author: hamster_nz
author_rank: Super Contributor
timestamp: July 02, 2017, 09:28:22 am
content: Unknown to me till jsut now, you use x64, and you have optimizations on, you do get jumpless for the "if ... then reset" coding:Code:while(1) {a++;if(a > 4)a = 0;printf("%i\n",a);}The loop becomes:Code:.L3:addl $1, %ebxmovl $.LC0, %esimovl $1, %edicmpl $5, %ebxcmovge %ebp, %ebxxorl %eax, %eaxmovl %ebx, %edxcall __printf_chkjmp .L3cmovge is "conditional move if greater or equal", an x64 opcode.