I have a bunch of code like the following:
int sign(MyEnum e)
{
switch(e)
{
case A:
case B:
return 1;
case C:
case D:
return -1;
default:
throw std::runtime_error("Invalid enum value");
}
}
int f(int a, int b, int c, MyEnum e)
{
const int sign = sign(e);
const int x = a * b - sign * c;
const int y = a + sign * c;
return x / y;
}
The arithmetic here is just an example. The actual code is more complex, but the point is that sign
is either -1 or 1 depending on an enum value, and we do a bunch of calculations where various things are multiplied by sign
. (Edit: the enum value is not known at compile time.)
I'd like this code to be optimized as though I'd written something like the following:
int f(int a, int b, int c, MyEnum e)
{
switch(e)
{
case A:
case B:
{
const int x = a * b - c;
const int y = a + c;
return x / y;
}
case C:
case D:
{
const int x = a * b + c;
const int y = a - c;
return x / y;
}
default:
throw new std::runtime_error("Invalid enum value");
}
}
Of course I don't actually want to write all the code like that, because it's a testing and maintenance nightmare.
Playing around with Compiler Explorer, it looks like the exception in sign
may be the issue here; if I have the "default" case return, say, -1, then I get what I want. But I would like some safety here.
Questions:
-O3
makes two clones of the method, one of which does what I want, although I don't know which one actually would get run. Can I give hints for this?-O3
. Can I turn on optimizations for just a particular block of code, or encourage the compiler to make them?EDIT: Since I (obviously) don't understand all the issues at hand I probably didn't give this a great title. Please feel free to edit if you know what you're doing.
Yes, you can use return instead of break ... break is optional and is used to prevent "falling" through all the other case statements. So return can be used in a similar fashion, as return ends the function execution.
One should use the inline function qualifier only when the function code is small. If the functions are larger you should prefer the normal functions since the saving in memory space is worth the comparatively small sacrifice in execution speed.
Inline functions are commonly used when the function definitions are small, and the functions are called several times in a program. Using inline functions saves time to transfer the control of the program from the calling function to the definition of the called function.
An inline function is one for which the compiler copies the code from the function definition directly into the code of the calling function rather than creating a separate set of instructions in memory. This eliminates call-linkage overhead and can expose significant optimization opportunities.
Here's an alternative take on the thing:
template <int sign>
int f(int a, int b, int c)
{
const int x = a * b - sign * c;
const int y = a + sign * c;
return x / y;
}
int f(int a, int b, int c, MyEnum e)
{
const int sign = sign(e);
if (sign == 1) return f<1>(a, b, c);
else return f<-1>(a, b, c);
}
This way, you keep the safety you want (in the form of the exception), but then transform the resulting information into a compile-time value which the compiler can use for optimisations.
As Chris pointed out in comments, if sign
is only ever used to switch the sign of c
, you can get rid of the template altogether and just flip c
's sign when calling:
int f(int a, int b, int c)
{
const int x = a * b - c;
const int y = a + c;
return x / y;
}
int f(int a, int b, int c, MyEnum e)
{
const int sign = sign(e);
if (sign == 1) return f(a, b, c);
else return f(a, b, -c);
}
Since in this situation, int sign(MyEnum)
function it not used by other translation units, then it could be marked static
.
In this context, static
means that the function is local to the translation unit, and does not link outside of this translation unit. (The keyword static
has different meanings in C++ depending on the context it is used.)
That allows the optimizers to perform more optimizations and possibly eliminate the function entirely (assuming optimization is enabled).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With