C++ 是否允许编译器优化堆外内存分配?
考虑以下使用C++ 是否允许编译器优化堆外内存分配?,c++,gcc,optimization,clang,language-lawyer,C++,Gcc,Optimization,Clang,Language Lawyer,考虑以下使用new的简单代码(我知道没有delete[],但它与此问题无关): 是否允许编译器优化new调用 在我的研究中,和Visual Studio 2015没有优化出新的调用。所有测试都启用了完全优化(-O3用于g++和clang,发布模式用于VisualStudio) 难道不是new在幕后进行系统调用,使得编译器无法(而且是非法的)优化它吗 编辑:我现在从程序中排除了未定义的行为: #include <new> int main() { int* mem = n
new
的简单代码(我知道没有delete[]
,但它与此问题无关):
是否允许编译器优化new
调用
在我的研究中,和Visual Studio 2015没有优化出新的调用。所有测试都启用了完全优化(-O3用于g++和clang,发布模式用于VisualStudio)
难道不是new
在幕后进行系统调用,使得编译器无法(而且是非法的)优化它吗
编辑:我现在从程序中排除了未定义的行为:
#include <new>
int main()
{
int* mem = new (std::nothrow) int[100];
return 0;
}
#包括
int main()
{
int*mem=new(std::nothrow)int[100];
返回0;
}
不再是了,但是
EDIT2:
#include <new>
int main()
{
int* mem = new (std::nothrow) int[1000];
if (mem != 0)
return 1;
return 0;
}
#包括
int main()
{
int*mem=new(std::nothrow)int[1000];
如果(mem!=0)
返回1;
返回0;
}
谨记C++标准告诉一个正确的程序应该做什么,而不是应该如何做。它根本不能告诉后者,因为新的体系结构可以而且确实在标准编写之后出现,并且标准必须对他们有用
new
不必是引擎盖下的系统调用。有些计算机在没有操作系统和系统调用概念的情况下可用
因此,只要终端行为没有改变,编译器就可以优化任何内容。包括新的
有一个警告。替换全局运算符new可以在不同的翻译单元中定义
在这种情况下,新产品的副作用可能无法优化消除。但是,如果编译器能够保证新操作符没有副作用,就像发布的代码是整个代码一样,那么优化是有效的。
新的can抛出std::bad_alloc不是一个要求。在这种情况下,当新的优化时,编译器可以保证不会抛出任何异常,也不会产生副作用。
记住C++标准告诉我们正确的程序应该做什么,而不是应该如何做。它根本不能告诉后者,因为新的体系结构可以而且确实在标准编写之后出现,并且标准必须对他们有用
new
不必是引擎盖下的系统调用。有些计算机在没有操作系统和系统调用概念的情况下可用
因此,只要终端行为没有改变,编译器就可以优化任何内容。包括新的
有一个警告。替换全局运算符new可以在不同的翻译单元中定义
在这种情况下,新产品的副作用可能无法优化消除。但是,如果编译器能够保证新操作符没有副作用,就像发布的代码是整个代码一样,那么优化是有效的。
新的can抛出std::bad_alloc不是一个要求。在这种情况下,当new被优化时,编译器可以保证不会抛出异常,也不会发生副作用。代码段中可能发生的最坏情况是
new
抛出std::bad\u alloc
,这是未经处理的。然后发生的事情是实现定义的
在最佳情况为无操作,最坏情况未定义的情况下,允许编译器将它们考虑为不存在。现在,如果您确实尝试捕获可能的异常:
int main() try {
int* mem = new int[100];
return 0;
} catch(...) {
return 1;
}
。。。然后。在您的代码段中可能发生的最坏情况是
new
抛出未处理的std::bad_alloc
。然后发生的事情是实现定义的
在最佳情况为无操作,最坏情况未定义的情况下,允许编译器将它们考虑为不存在。现在,如果您确实尝试捕获可能的异常:
int main() try {
int* mem = new int[100];
return 0;
} catch(...) {
return 1;
}
。。。然后。编译器完全可以(但不是必需的)优化原始示例中的分配,根据标准§1.9,在EDIT1示例中更是如此,这通常被称为“假设规则”:
一致性实现需要模拟(仅)抽象机器的可观察行为,如下所述:[3页条件] 一种更具可读性的表示形式可在网站上找到 有关要点如下:
- 您没有挥发物,因此1)和2)不适用
- 您不输出/写入任何数据或提示用户,因此3)和4)不适用。但是,即使您这样做了,他们也会在EDIT1中感到满意(在最初的示例中也可以这样说,尽管从纯理论的角度来看,这是非法的,因为程序流和输出——理论上——不同,但请参见下面的两段)
new
抛出(不会发生,另请参见下一段),可观察到的行为将不同,程序的退出代码和程序后面可能出现的任何输出都会不同
现在,在单个小分配的特殊情况下,您可以给编译器“怀疑的好处”,它可以保证分配不会失败。即使在内存压力非常大的系统上,当可用的分配粒度小于最小分配粒度时,也不可能启动进程,堆也将在调用
main
之前设置好。因此,如果此分配失败,在调用main
之前,程序将永远不会启动或已经遇到不正常的结束。到目前为止,假设编译器知道这一点,即使在理论上分配可能抛出,它也是合法的
#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;
}
main: # @main
movl $1000000, %eax # imm = 0xF4240
ret