我可以鼓励g ++内联返回符号的开关吗?

Dan*_*ury 11 c++ compiler-optimization

我有一堆如下代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

这里的算术只是一个例子。实际的代码更复杂,但要点是sign根据枚举值是-1或1,并且我们进行了一堆计算,其中各种事情都乘以sign。(编辑:枚举值在编译时未知。)

我希望对这段代码进行优化,就像我写了以下代码一样:

  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");
    }
  }
Run Code Online (Sandbox Code Playgroud)

当然,我实际上并不想编写所有这样的代码,因为这是测试和维护的噩梦。

使用Compiler Explorer,看起来sign这里可能是一个例外。如果我有“默认”情况下的返回值,例如-1,那么我得到了我想要的。但是我想要一些安全。

问题:

  1. 是否有引发异常的根本原因阻止(或阻止编译器使用)此优化?
  2. 看起来好像在编译该方法时-O3将其克隆为两个方法,其中一个克隆完成了我想要的操作,尽管我不知道哪个实际上可以运行。我可以为此提供提示吗?
  3. 我不知道如果我想编译一切-O3。我可以仅针对特定的代码块启用优化,还是鼓励编译器进行优化?
  4. 是否有一些精美的模板元编程技巧或可以用来编写看起来像第一个块的代码但可以生成看起来像第二个块的代码的东西?
  5. 关于我要做什么的其他建议?

编辑:由于我(显然)不了解手头的所有问题,所以我可能没有给这个标题好。如果您知道自己在做什么,请随时进行编辑。

Rei*_*ica 6

这是另一种选择:

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);
}
Run Code Online (Sandbox Code Playgroud)

这样,您可以保持所需的安全性(以异常的形式),但是随后将结果信息转换为编译时值,编译器可以使用该值进行优化。

正如Chris在评论中指出的那样,如果sign仅用于切换的符号c,则可以完全摆脱模板,而c在调用时只需翻转一下符号即可:

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);
}
Run Code Online (Sandbox Code Playgroud)


Elj*_*jay 4

由于在这种情况下,int sign(MyEnum)其他翻译单元不使用该函数,因此可以将其标记为static

在这种情况下,static意味着该函数是翻译单元的本地函数,并且不会链接到该翻译单元之外。(该关键字static在 C++ 中具有不同的含义,具体取决于所使用的上下文。)

这允许优化器执行更多优化,并可能完全消除该函数(假设启用了优化)。

  • 在优化“f”时,无论“sign”是否是静态的,编译器都具有相同的优化可能性(关于“f”)。当然,如果“sign”内联到“f”,则可以将其删除。 (2认同)