是否允许编译器优化堆内存分配? [英] Is the compiler allowed to optimize out heap memory allocations?

查看:240
本文介绍了是否允许编译器优化堆内存分配?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑下面的简单代码,使用 new (我知道没有 delete [] 但它不属于这个问题):

  int main()
{
int * mem = new int [100];

return 0;
}

编译器允许优化 / code> call?



在我的研究中, g ++(5.2.0)和Visual Studio 2015未优化调用,同时clang(3.0+)是。所有测试都已经完成了完整的优化(-O3 for g ++和clang,Visual Studio的发布模式)。



不是 / code>在系统调用下,使编译器不能(并且非法)优化。



EDIT :我现在从程序中排除未定义的行为:

  #include< new& 

int main()
{
int * mem = new(std :: nothrow)int [100]
return 0;
}

clang 3.0不再优化,但更高版本



EDIT2

  #include< new& 

int main()
{
int * mem = new(std :: nothrow)int [1000]

if(mem!= 0)
return 1;

return 0;
}

clang总是返回1

解决方案

历史似乎是clang遵循 N3664:澄清内存分配,允许编译器优化内存分配但由于 Nick Lewycky指出


Shafik指出,似乎违反了因果关系,但N3664开始的生命为N3433,我相信我们先写优化,然后写完论文。


所以clang实现了优化,后来成为一个作为C ++ 14一部分实现的提议。



基本问题是无论这是在 N3664 之前的有效优化,这是一个艰难的问题。我们必须转到C ++标准草案部分<$ c $中包含的 as-if规则 c> 1.9 强调我):

$


这个国际标准中的语义描述定义了一个
参数化的非确定性抽象机。这个国际
标准没有要求符合
实现的结构。特别地,它们不需要复制或模拟抽象机的
结构。相反,符合实现
需要模拟(仅)抽象
机器
的可观察行为,如下所述。 5


其中 5 说:



< >

此条款有时称为as-if规则,因为
实现可以忽略此
国际标准的任何要求只要结果好像需要
已经被遵守,只要可以从程序的可观察的
行为确定。例如,实际实现需要
不评估表达式的一部分,如果它可以推断其值是
未使用,并且没有影响程序的
的可观察行为的副作用产生。


由于 new 可能会抛出异常,改变程序的返回值,这似乎反对它被 as-if规则所允许。



虽然,可以认为它是实现细节何时抛出异常,因此ang可以决定即使在这种情况下它不会导致异常,因此省略 new 调用不会违反 as-if规则



as-if规则下也似乎有效,以优化对非投放版本的调用。



但是我们可能有一个替换全局运算符new在不同的翻译单元,可能会导致这影响可观察的行为,所以编译器必须有一些方法证明这不是case ,否则将不能在不违反 as-if规则的情况下执行此优化。先前版本的clang在这种情况下确实优化为这个godbolt示例显示,它通过 Casey here ,代码如下:

  #include< cstddef> 

extern void * operator new(std :: size_t n);

template< typename T>
T * create(){return new T(); }

int main(){
auto result = 0;
for(auto i = 0; i <1000000; ++ i){
result + =(create< int>()!= nullptr);
}

返回结果;
}

并将其优化为:

  main:#@main 
movl $ 1000000,%eax#imm = 0xF4240
ret
pre>

这确实似乎太激进了,但后来的版本似乎没有这样做。


Consider the following simple code that makes use of new (I am aware there is no delete[], but it does not pertain to this question):

int main()
{
    int* mem = new int[100];

    return 0;
}

Is the compiler allowed to optimize out the new call?

In my research, g++ (5.2.0) and Visual Studio 2015 do not optimize out the new call, while clang (3.0+) does. All tests have been made with full optimizations enabled (-O3 for g++ and clang, Release mode for Visual Studio).

Isn't new making a system call under the hood, making it impossible (and illegal) for a compiler to optimize that out?

EDIT: I have now excluded undefined behaviour from the program:

#include <new>  

int main()
{
    int* mem = new (std::nothrow) int[100];
    return 0;
}

clang 3.0 does not optimize that out anymore, but later versions do.

EDIT2:

#include <new>  

int main()
{
    int* mem = new (std::nothrow) int[1000];

    if (mem != 0)
      return 1;

    return 0;
}

clang always returns 1.

解决方案

The history seems to be that clang is following the rules laid out in N3664: Clarifying Memory Allocation which allows the compiler to optimize around memory allocations but as Nick Lewycky points out :

Shafik pointed out that seems to violate causality but N3664 started life as N3433, and I'm pretty sure we wrote the optimization first and wrote the paper afterwards anyway.

So clang implemented the optimization which later on became a proposal that was implemented as part of C++14.

The base question is whether this is a valid optimization prior to N3664, that is a tough question. We would have to go to the as-if rule covered in the draft C++ standard section 1.9 Program execution which says(emphasis mine):

The semantic descriptions in this International Standard define a parameterized nondeterministic abstract machine. This International Standard places no requirement on the structure of conforming implementations. In particular, they need not copy or emulate the structure of the abstract machine. Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.5

where note 5 says:

This provision is sometimes called the "as-if" rule, because an implementation is free to disregard any requirement of this International Standard as long as the result is as if the requirement had been obeyed, as far as can be determined from the observable behavior of the program. For instance, an actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no side effects affecting the observable behavior of the program are produced.

Since new could throw an exception which would have observable behavior since it would alter the return value of the program, that would seem to argue against it being allowed by the as-if rule.

Although, it could be argued it is implementation detail when to throw an exception and therefore clang could decide even in this scenario it would not cause an exception and therefore eliding the new call would not violate the as-if rule.

It also seems valid under the as-if rule to optimize away the call to the non-throwing version as well.

But we could have a replacement global operator new in a different translation unit which could cause this to affect observable behavior, so the compiler would have to have some way a proving this was not the case, otherwise it would not be able to perform this optimization without violating the as-if rule. Previous versions of clang did indeed optimize in this case as this godbolt example shows which was provided via Casey here, taking this code:

#include <cstddef>

extern void* operator new(std::size_t n);

template<typename T>
T* create() { return new T(); }

int main() {
    auto result = 0;
    for (auto i = 0; i < 1000000; ++i) {
        result += (create<int>() != nullptr);
    }

    return result;
}

and optimizing it to this:

main:                                   # @main
    movl    $1000000, %eax          # imm = 0xF4240
    ret

This indeed seems way too aggressive but later versions do not seem to do this.

这篇关于是否允许编译器优化堆内存分配?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆