为什么编译器不会优化此负载

时间:2016-01-28 15:04:26

标签: c++

在以下c ++程序中:

struct O
{
   const int id;
};

extern void externalFunc();

int func(O* o)
{
    //first load of o->id happens here:
    int first = o->id;
    externalFunc();

    //second load happens here, but why?
    return o->id + first;
}

Clang和MSVC都对编译此代码进行了所有优化,其中o-> id值从内存加载两次。

为什么这些编译器无法删除第二个加载?我试图通过将id成员标记为const来告诉编译器保证不改变值,但显然两个编译器都没有找到足够的保证。如果我删除externalFunc()的调用,他们会优化第二次加载。我如何说服编译器这个值真的不会改变?

4 个答案:

答案 0 :(得分:3)

考虑:

43

输出:{{1}}。 Demo

答案 1 :(得分:2)

externalFunc()可能会改变o->id。 (不是o,这是一个局部变量。)

答案 2 :(得分:1)

  

为什么这些编译器无法删除第二个加载?我试图通过将id成员标记为const来告诉编译器保证不改变值,但显然两个编译器都没有找到足够的保证。

因为不是。考虑这个例子。

static O mg {5};

void
externalFunc()
{
  mg.~O();
  new (&mg) O {6};
}

int
main()
{
  std::cout << mg.id << '\n';
  func(&mg);
  std::cout << mg.id << '\n';
}

第一个加载读取值5,第二个加载读取6。

  

如何说服编译器这个值真的不会改变?

只需缓存该字段即可。这仍然不能说服编译器o->id不会改变,但它会确保如果确实如此,你就不在乎了。

int
func(O* o)
{
  const int id = o->id;
  externalFunc();
  return id + id;
}

我已经习惯将通过指针(包括this指针)访问的原始字段的所有值缓存到本地(const)变量中。如果编译器可以确保值不能更改,则无需额外成本,如果不能,则可能会产生稍微好一点的代码。另外,它还允许您为函数上下文中最有意义的值指定名称。

答案 3 :(得分:0)

编译器本身在编译externalFunc()时没有func()的代码,所以它不知道它可能做什么。因此,它起到了障碍的作用。

如果您静态链接,这将属于链接时优化(可以使用-flto在GCC上启用),并且在MSVC中也受支持。

为了暗示GCC / Clang(看起来像MSVC不支持这个),该函数不会改变全局内存,你可以用pure属性标记它:

extern void __attribute__((pure)) void externalFunc();

然后它会停止构成这个障碍。