为什么GCC不优化删除C ++中的空指针? [英] Why doesn't GCC optimize out deletion of null pointers in C++?

查看:254
本文介绍了为什么GCC不优化删除C ++中的空指针?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  int main(){
int * ptr = nullptr;
删除ptr;

$ / code>

使用GCC(7.2),有一个调用关于操作符删除的指令。使用Clang和Intel编译器,不存在这样的指令,空指针删除被完全优化(在所有情况下都是 -O2 )。你可以在这里测试: https://godbolt.org/g/JmdoJi



我想知道是否可以通过GCC打开这样的优化? (我的更广泛的动机源于定制 swap vs std :: swap 对于可移动类型的问题,其中删除null指针可以表示在第二种情况下的性能损失;参见 https://stackoverflow.com/a/45689282/580083 以详细信息。)



更新



为了澄清我对这个问题的动机:If我只用 delete ptr; 没有 if(ptr)移动赋值运算符和一个析构函数,然后 std :: swap 与该类的对象产生3 call 使用GCC的说明。这可能是一个相当大的性能损失,例如,当排序这样的对象数组时。

;
无处不在,但不知道这是否也不是性能损失,因为 delete 表达式需要检查 ptr 以及。但是,在这里,我想,编译器只会生成一个检查。



另外,我真的很喜欢调用 delete 没有看守,这对我来说是一个惊喜,它可能会产生不同的(表现)结果。

更新



我只做了一个简单的基准测试,即排序对象,它们在移动赋值运算符和析构函数中调用 delete 。源是在这里: https://godbolt.org/g/7zGUvo



使用GCC 7.1和 -O2 标记测量的 std :: sort 的运行时间至强E2680v3:

链接代码中存在一个错误,它比较指针而不是指向值。更正的结果如下所示:


  1. 没有 if guard:<如果 guard
  2. 10.6 [s] 31.5 [s] ,
  3. 如果和自定义 swap 10.4 [s] 31.3 [s]。

这些结果在许多运行中绝对一致且偏差最小。前两种情况之间的性能差异是显着的,我不会说这是一些像代码一样的非常罕见的角落案例。

解决方案

根据C ++ 14 [expr.delete] / 7:


如果delete-expression的操作数的值不是空指针值,则:


  • [...省略...]

  • ul>

    否则,未指定是否调用释放函数。


这两个编译器都符合标准,因为它没有被指定是否调用 operator delete 来删除空指针。



请注意,godbolt在线编译器只是在不链接的情况下编译源文件。因此,该阶段的编译器必须允许将 operator delete 替换为另一个源文件。



正如在另一个答案中已经推测的那样 - 在替换 operator delete 的情况下,gcc可能会争取一致的行为。这个实现意味着某人可以为了调试目的而重载该函数,并且打破 delete 表达式的所有调用,即使它碰巧正在删除空指针。



更新:删除了这个可能不是实际问题的猜测,因为OP提供的基准证明它实际上是。


Consider a simple program:

int main() {
  int* ptr = nullptr;
  delete ptr;
}

With GCC (7.2), there is a call instruction regarding to operator delete in the resulting program. With Clang and Intel compilers, there are no such instructions, the null pointer deletion is completely optimized out (-O2 in all cases). You can test here: https://godbolt.org/g/JmdoJi.

I wonder whether such an optimization can be somehow turned on with GCC? (My broader motivation stems from a problem of custom swap vs std::swap for movable types, where deletion of null pointers can represent a performance penalty in the second case; see https://stackoverflow.com/a/45689282/580083 for details.)

UPDATE

To clarify my motivation for the question: If I use just delete ptr; without if (ptr) guard in a move assignment operator and a destructor of some class, then std::swap with objects of that class yields 3 call instructions with GCC. This might be a considerable performance penalty, e.g., when sorting an array of such objects.

Moreover, I can write if (ptr) delete ptr; everywhere, but wonder, whether this cannot be a performance penalty as well, since delete expression needs to check ptr as well. But, here, I guess, compilers will generate a single check only.

Also, I really like the possibility to call delete without the guard and it was a surprise for me, that it could yield different (performance) outcomes.

UPDATE

I just did a simple benchmark, namely sorting objects, which invoke delete in their move assignment operator and destructor. The source is here: https://godbolt.org/g/7zGUvo

Running times of std::sort measured with GCC 7.1 and -O2 flag on Xeon E2680v3:

There is a bug in the linked code, it compares pointers, not pointed values. Corrected results are as follows:

  1. without if guard: 17.6 [s] 40.8 [s],
  2. with if guard: 10.6 [s] 31.5 [s],
  3. with if guard and custom swap: 10.4 [s] 31.3 [s].

These results were absolutely consistent across many runs with minimal deviation. The performance difference between first two cases is significant and I wouldn't say that this is some "exceedingly rare corner case" like code.

解决方案

According to C++14 [expr.delete]/7:

If the value of the operand of the delete-expression is not a null pointer value, then:

  • [ ...omitted... ]

Otherwise, it is unspecified whether the deallocation function will be called.

So both compilers do comply with the standard, because it's unspecified whether operator delete is called for deletion of a null pointer.

Note that the godbolt online compiler just compiles the source file without linking. So the compiler at that stage must allow for the possibility that operator delete will be replaced by another source file.

As already speculated in another answer -- gcc may be angling for consistent behaviour in the case of a replacement operator delete; this implementation would mean that someone can overload that function for debug purposes and break on all invocations of the delete expression, even when it happened to be deleting a null pointer.

UPDATED: Removed speculation that this might not be a practical issue, since OP provided benchmarks showing that it in fact is.

这篇关于为什么GCC不优化删除C ++中的空指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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