1

I have a recursive function which I know will only ever call itself 32 times.

int f(int x) { return (x == 0) ? 0 : YY + f(~x & (YY - 1)); }

(YY is a macro which isolates the most significant bit of x, which is not included for your sanity). For fun, I'm trying to optimize the function so I can get the best result on the UVA online judge (I would never do this optimization on real code). Is there a way to make this function into a macro / inline the function so that a function need not ever be called (i.e. the compiler expands a statement long enough that recursion is not needed), or is there a way to do this via an inline method?

Here's the macro, if needed:

#define YY ((((((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) | (((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) >> 4)) | ((((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) | (((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) >> 4)) >> 2)) | (((((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) | (((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) >> 4)) | ((((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) | (((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) >> 4)) >> 2)) >> 1)) ^ ((((((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) | (((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) >> 4)) | ((((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) | (((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) >> 4)) >> 2)) | (((((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) | (((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) >> 4)) | ((((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) | (((x | (x >> 16)) | ((x | (x >> 16)) >> 8)) >> 4)) >> 2)) >> 1)) >> 1))
Jacob Denson
  • 391
  • 1
  • 4
  • 14
  • You could use a constexpr function (which can then be called at run time if you know what numbers will be fed to it when you compile it)....This method cannot be used though if the number you feed into it is determined at run time (like user input) – DarthRubik May 08 '16 at 00:34
  • Actually just check this (you can look [here](http://godbolt.org/#compilers:!((compiler:clang30,options:'-Os',source:'%23define+YY+1%0A%0Ainline+int+f(int+x)+%7B+return+(x+%3D%3D+0)+%3F+0+:+YY+%2B+f(~x+%26+(YY+-+1))%3B+%7D%0A%0Avolatile+int+x+%3D+0%3B%0Aint+main()%0A%7B++%0A++x+%3D+f(x)%3B%0A%7D')),filterAsm:(colouriseAsm:!t,commentOnly:!t,directives:!t,labels:!t),version:3) if you are skeptical) but just adding the inline directive it looks like gcc simplifies the recursion into a few simple instructions (no jumps or anything like that) (I just defined YY as 1 [it might not work thusly]) – DarthRubik May 08 '16 at 00:40
  • If you know x at compile time, you could write your own loop unroller, that unrolls all your recursive calls during compilation: http://stackoverflow.com/questions/34036608/compile-time-evaluation-of-a-c-loop – Ammar Husain May 08 '16 at 00:41
  • The value of x is determine during run time, but we can always guarantee the function will only recurse 32 times, because an integer is (most of the time) 32 bits. – Jacob Denson May 08 '16 at 00:41
  • So what does the function do? – DarthRubik May 08 '16 at 00:42
  • @JacobDenson and also, you can ensure that by using `int32_t` from `stdint.h` – Iharob Al Asimi May 08 '16 at 00:42
  • 1
    @DarthRubik It finds the x'th Gray code, which is a way of encoding natural numbers. – Jacob Denson May 08 '16 at 00:43
  • #DarthRubik My assembly is rusty, so would you mind checking my macro - I've added it to the question – Jacob Denson May 08 '16 at 00:44
  • Sure what is its definition? – DarthRubik May 08 '16 at 00:44
  • Wow that is a monster! – DarthRubik May 08 '16 at 00:46
  • Sorry using that macro and only using inline does actually cause a recursion (which is unfortunate) – DarthRubik May 08 '16 at 00:48
  • I don't know if you can do what you want, but if you are using gcc you could just mark the function to be optimized for speed – DarthRubik May 08 '16 at 01:00
  • If the compiler can't / won't optimize the function calls away, you can manually convert it into a iterative version – MikeMB May 08 '16 at 01:02
  • There's a lot of repetition within that macro. The first step might be to see if the macro itself can be simplified. – Peter May 08 '16 at 01:04

1 Answers1

5

If all you want to do is convert to standard gray code, it's actually far simpler than you may realize. All you need is to XOR the binary value with itself shifted right by one. Here's an equivalent function (with the types declared unsigned), taken from the Wikipedia article:

unsigned int binaryToGray(unsigned int num)
{
    return num ^ (num >> 1);
}

I compared the output to your function f for the first million integers, and it matched.

Tom Karzes
  • 22,815
  • 2
  • 22
  • 41