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

时间:2019-07-16 15:52:06

标签: c++ compiler-optimization

我有一堆如下代码:

int sign(MyEnum e)
{
  switch(e)
  {
    case A:
    case B:
      return 1;
    case C:
    case D:
      return -1;
    default:
      throw new 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;
}

这里的算法只是一个例子。实际的代码更加复杂,但是要点是,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");
    }
  }

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

使用Compiler Explorer,似乎sign中的异常可能是这里的问题;如果我有“默认”情况下的返回值,例如-1,那么我得到了我想要的。但是我想在这里一些安全。

问题:

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

编辑:由于我(显然)不了解当前遇到的所有问题,因此我可能没有给这个标题冠冕堂皇。如果您知道自己在做什么,请随时进行编辑。

2 个答案:

答案 0 :(得分: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);
}

通过这种方式,您可以保留想要的安全性(以异常的形式),但是随后将所得信息转换为编译时值,编译器可以使用该值进行优化。

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

c

答案 1 :(得分:3)

由于在这种情况下,int sign(MyEnum)的功能未被其他翻译单元使用,因此可以将其标记为static

在这种情况下,static表示该功能在翻译单元本地,并且不链接到该翻译单元之外。 (根据使用的上下文,关键字static在C ++中具有不同的含义。)

这使优化器可以执行更多优化,并可能会完全消除该功能(假设启用了优化)。