为什么这个未使用的variables没有被优化?
我和Godbolt的CompilerExplorer玩过。 我想看看某些优化有多好。 我最低工作的例子是:
#include <vector> int foo() { std::vector<int> v {1, 2, 3, 4, 5}; return v[4]; }
生成的汇编程序(由clang 5.0.0,-O2 -std = c ++ 14):
foo(): # @foo() push rax mov edi, 20 call operator new(unsigned long) mov rdi, rax call operator delete(void*) mov eax, 5 pop rcx ret
正如人们所看到的,铿锵知道答案,但在返回之前做了很多事情。 在我看来,即使vector是创build,因为“运营商新/删除”。
任何人都可以向我解释这里发生了什么,为什么它不只是返回?
由GCC生成的代码(不在这里复制)似乎明确地构造了向量。 有谁知道GCC不能推断出结果?
std::vector<T>
是一个相当复杂的类,涉及dynamic分配。 虽然clang++
有时候可以省略堆分配 ,这是一个相当棘手的优化,你不应该依赖它。 例:
int foo() { int* p = new int{5}; return *p; }
foo(): # @foo() mov eax, 5 ret
作为一个例子,使用std::array<T>
(不会dynamic分配) 产生完全内联的代码 :
#include <array> int foo() { std::array v{1, 2, 3, 4, 5}; return v[4]; }
foo(): # @foo() mov eax, 5 ret
正如Marc Glisse在其他答案的评论中指出的那样,这是标准在[expr.new]中所说的#10 :
允许实现省略对可replace的全局分配函数([new.delete.single],[new.delete.array])的调用。 当这样做的时候,存储是由实现提供的,或者是通过扩展另一个新expression式的分配来提供的。 这个实现可以扩展一个新的expression式e1的分配,为下一个新的expression式e2提供存储,如果分配不被扩展,
正如注释注释, operator new
可以被replace。 这可能发生在任何翻译部门。 因此,对于未更换的情况优化程序需要全程序分析。 如果它被replace,你必须要调用它当然。
缺省operator new
是否是库I / O调用是未指定的。 这很重要,因为库I / O调用是可观察的,因此它们也不能被优化。
N3664改为[expr.new],在一个答案和一个评论中引用,允许newexpression式不调用可replace的全局分配函数。 但是, vector
使用std::allocator<T>::allocate
分配内存,它直接调用::operator new
,而不是通过newexpression式 。 因此,特殊权限不适用,一般来说编译器不能忽略这样的直接调用::operator new
。
所有的希望不会丢失,但是,对于std::allocator<T>::allocate
的规范有这样的说法:
备注:通过调用
::operator new
获得存储空间,但未指定调用此函数的时间或频率。
利用这个权限,libc ++的std::allocator
使用特殊的clang内build函数向编译器指明elision是允许的。 使用-stdlib=libc++
, clang将您的代码编译为
foo(): # @foo() mov eax, 5 ret