-4

This question is strictly related to the C or C++ language capabilities. I don't recommend the code below as a design pattern. I don't use it and I don't encourage it. But I'm just curious to improve my knowledge!

I have a define that contains a label and a goto condition.

#define BROP(num, sum)                  \
num = rand_lcg(generated);              \
if (num % 2)                            \
{                                       \
rng1:                                   \
    generated = rand_lcg(generated);    \
    if (generated < 512)                \
        sum -= generated;               \
    else                                \
        goto rng1;                      \
}

And later in the code I use it like this:

for (i = 0; i < iterations; i++)
{
    BROP(num, sum);
    BROP(num, sum);
    BROP(num, sum);
    // ...
}

I end up in a situation where the loop gets unrolled and the label gets redefined.

Is there a smart construct I can use to make the compiler rename the label each time the define is "instantiated"?

I know of ALL the alternatives of avoiding this statement but still, I don't know the answer to the question.

halfer
  • 19,824
  • 17
  • 99
  • 186
VAndrei
  • 5,420
  • 18
  • 43
  • 3
    Is there a reason you couldn't just use a `do-while` loop construct instead of the `goto` in the macro? – wolfPack88 Mar 16 '15 at 14:42
  • 4
    Actually ... why isnt this a function? – Borgleader Mar 16 '15 at 14:43
  • @wolfPack88 Yes. To avoid branching. – VAndrei Mar 16 '15 at 14:43
  • 3
    C or C++? They are different languages! – Basile Starynkevitch Mar 16 '15 at 14:44
  • @BasileStarynkevitch any would be fine. – VAndrei Mar 16 '15 at 14:45
  • 5
    How does this avoid branching? Each of the `if` statements will generate a branch, and you make it harder for the compiler to optimize for you. – kdopen Mar 16 '15 at 14:45
  • How many times would you expand the macro? More like 10 times or more like 10.000 times? – Codor Mar 16 '15 at 14:46
  • 2
    @VAndrei, this is really a bad strategy. Tags are there to guide people to the question they feel comfortable with. – Jens Gustedt Mar 16 '15 at 14:46
  • @kdopen I want to have only the if statements as branches. That's why I unroll in the for loop. – VAndrei Mar 16 '15 at 14:46
  • Then your only option is to pass the label name in as an argument to `BROP` if you want to avoid compiler extensions to the language. – kdopen Mar 16 '15 at 14:47
  • 2
    This is a typical example for two common problems with question. First this is preliminary optimization, in the 3rd millenium compilers are probably much better than many programmers can do by hand. Then this is a xy problem, your problem with unrolling and redefinition of labels is just the one that you shouldn't be interested in, but why you'd trying such complicated constructs in the first place. – Jens Gustedt Mar 16 '15 at 14:49
  • I'm a bit surprised by the high downvotes number. I added a link to a previous post where I explain why I want to avoid branching. The question is strictly related to a language capability not to best common practices. – VAndrei Mar 16 '15 at 14:53
  • Why not write a inline function and force it to be inlined, and/or use do/while? – user3528438 Mar 16 '15 at 14:55
  • @VAndrei: I understand wanting to avoid branching; what I don't understand is how you claim to avoid branching by using an `if`-`else`. That is a branch in and of itself. – wolfPack88 Mar 16 '15 at 14:56
  • @wolfPack88 I posted a link to the post where I explain why I want to strictly control the branches. – VAndrei Mar 16 '15 at 14:57
  • @VAndrei: And again, I fail to see how your construct has "strict" control versus the `do`-`while` loop. – wolfPack88 Mar 16 '15 at 14:59
  • @wolfPack88 Every time you want to get out of the while loop, you check something. That branch increases the number of branches I want to have in the source code. The branch can also be predicted somehow and it messes my statistics. – VAndrei Mar 16 '15 at 15:04
  • @VAndrei: The same thing is done here. Every time you want to know if you go back to the label or not, you check something (with an `if`-`else` instead of a `while` condition). In the case of the `while` loop, the compiler can help you a bit with optimizations. With `goto`, not as much. – wolfPack88 Mar 16 '15 at 15:06
  • I'm pretty certain that an inline function would make all your problems go away. – Lundin Mar 16 '15 at 15:11
  • [Possibly related question](http://stackoverflow.com/questions/12030022/branching-elimination-using-bitwise-operators). – Lundin Mar 16 '15 at 15:18
  • To all people who downvoted: I changed the question's text so that I enforce that I do not at all recommend using a construct like that one. I would delete the question but there are 2 answers that bring value. It would be fair to justify your downvote. – VAndrei Mar 16 '15 at 15:42

2 Answers2

3

You really should make it a do...while loop:

#define BROP(num, sum)  do{                \
   bool again = false;                     \
   num = rand_lcg(generated);              \
   if (num % 2)  {                         \
   do { again = false;                     \
     generated = rand_lcg(generated);      \
     if (generated < 512)                  \
           sum -= generated;               \
     else                                  \
        again = true;                      \
   } while(again); } while(0)

See this for the old outerdo{...}while(0) trick.

If you (wrongly) insist on having a label and are using the GCC compiler (or Clang/LLVM which should be compatible on that), you could use local labels extension (i.e. with __label__ ...)

You might also generate the label from the __LINE__ number using concatenation in the preprocessor. Take inspiration from

#define STUPID_LOOP_BIS(Test,Lin) do { \
 lab##Lin: if (Test) goto lab##Lin; } while(0)
#define STUPID_LOOP_AT(Test,Lin) STUPID_LOOP_BIS(Test,Lin)
#define STUPID_LOOP(Test) STUPID_LOOP_AT(Test,__LINE__)

for obscure reasons you need all the three macros!

And use

  STUPID_LOOP(x++ < 100);
  STUPID_LOOP(y-- > 0);

on separate lines. Of course adapt and improve for your need.

You definitely should use and trust more the compiler optimization abilities and have static inline functions. Not every test is compiled to a machine branch (e.g. because of CMOV instructions); not every loop is compiled to a machine loop (e.g. because of loop unrolling). You are probably losing your developer's time, and more importantly, you are disabling optimizations by your tricks (so your code will likely go slower, not faster).

If using GCC or Clang enable optimizations and warnings: so compile with
gcc -Wall -Wextra -O3 -mtune=native

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • Is there a reason you don't just change the loop condition to `while(generated < 512)` instead of using a separate `bool` variable? – wolfPack88 Mar 16 '15 at 15:00
  • Does while(again); add a branch? I don't need any additional branches in my code other than the ones I explicitly wrote. – VAndrei Mar 16 '15 at 15:05
2

Ignoring the why of it, the following version of BROP compiles cleanly as both C and C++

#define BROP(num, sum, lbl)            \
num = rand_lcg(generated);              \
if (num % 2)                            \
{                                       \
lbl :                                   \
    generated = rand_lcg(generated);    \
    if (generated < 512)                \
        sum -= generated;               \
    else                                \
        goto lbl;                      \
}

I invoked it as

for (i = 0; i < 1000; i++)
{
    BROP(num,sum, lbl1);
    BROP(num,sum, lbl2);
}

This doesn't rely on any compiler extensions, so you should be able to use it across a large range of compilers.

kdopen
  • 8,032
  • 7
  • 44
  • 52
  • With preprocessor concatenation and `__LINE__` it is still portable, and you don't have to manually invent the `lbl2` label! – Basile Starynkevitch Mar 16 '15 at 15:00
  • Agreed, but I was going for the simplest solution. I suspect this is either for benchmarking or a test-suite, so being explicit is often the better choice. – kdopen Mar 16 '15 at 15:01