C++编译器为什么可以优化带有副作用的内存分配?

6
另一个问题讨论了优化器移除对new的调用的合法性:编译器是否允许优化堆内存分配?。我已经阅读了这个问题、答案和N3664
从我的理解来看,编译器允许在“as-if”规则下删除或合并动态分配,即如果结果程序的行为与标准中定义的抽象机器相比没有发生变化,则可以进行更改。
我使用clang++和g++及-O1优化编译了以下两个文件的程序,但是我不明白如何允许删除这些分配。
// main.cpp
#include <cstdio>

extern int g_alloc;

static int* foo(int n)
{
  // operator new is globally overridden in the other file.
  return new int(n);
}

int main(int argc, char** argv)
{
  foo(argc);
  foo(argc*2);
  printf("allocated: %d\n", g_alloc);
  return g_alloc;
}

// new.cpp
#include <cstdio>
#include <cstdlib>
#include <new>

int g_alloc = 0;

void* operator new(size_t n)
{
  g_alloc += n;
  printf("new %lu\n", n);
  return malloc(n);
}

这个想法是通过一个带有副作用的函数覆盖默认的operator new(size_t),该函数会打印一条消息并修改一个全局变量,后者被用作退出代码。

副作用不会影响分配本身,但仍然会改变程序的输出。事实上,在没有优化编译的情况下,输出是:

new 4
new 4
allocated: 8

但是一旦启用了优化,输出结果如下:

allocated: 0

使用标准98到17时,结果相同。

编译器如何允许省略这里的分配?它如何符合as-if规则?


2
有些例程不允许具有副作用(或者至少不允许副作用被执行),因为编译器可以省略它们。 - Eljay
@J... 是的,提问者已经提到了这一点。我认为这个问题的重点在于他没有意识到允许省略分配也包括允许更改可观察行为。这可能需要在那里的答案中更加清晰地表述出来。 - user17732522
1个回答

10

谢谢。cppreference关于分配的页面确实指出“新表达式允许省略或组合分配[...]”,但这是针对c++14及更高版本的。有关复制省略的页面确认了分配省略可以改变可观察的副作用,但同样需要c++14或更高版本的功能。欢迎提供任何标准的参考。 - Julien
1
这很简单,编译器作者没有费心在C++11(或更早)模式下禁用优化。您可以在https://gcc.gnu.org/pipermail/gcc-patches/2019-July/525038.html中看到,他们根本没有考虑这个问题。的确,这意味着g ++不严格符合C ++ 11标准,但没有人真正关心(如果您关心,可以使用fno-allocation-dce来禁用优化)。 - ecatmur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接