【问题标题】:Empty destructor causing slow code?空析构函数导致代码慢?
【发布时间】:2011-08-18 23:23:49
【问题描述】:

我正在为非 STL 容器类型实现自定义迭代器,并遇到了以下行为,在这个阶段,这对我来说似乎有点出乎意料。

当您定义“空”dtor 时,似乎会对性能产生重大影响?为什么??

为了深入了解这一点,我为 std::vector 实现了一个简单的迭代器,这样我就可以直接将性能与标准 STL 迭代器进行比较。为了公平测试,我只是从“vector.hpp”中复制了一个简化的实现,并尝试添加一个额外的“空”dtor:

template <typename _Myvec> 
    class my_slow_iterator    // not inheriting from anything!!
{
public :
    _Myvec::pointer _ptr;     // pointer to vector element 

/* All of the standard stuff - essentially from "vector.hpp" */

/* An additional empty dtor */
    ~my_slow_iterator () {}
};

然后我修改了 std::vector 以便我可以让它返回我的新迭代器类型并使用以下内容进行基准测试 - 对包含 2000000 个随机整数的向量进行排序,平均运行 3 次:

std::vector vec;
// fill via rand();
int tt = clock();
std::sort(vec.begin(), vec.end());
tt = clock() - tt;    // elapsed time in ms

我得到了以下结果(VS2010、Release build、_ITERATOR_DEBUG_LEVEL 0 等):

  • 使用标准 STL 迭代器:550 毫秒。
  • 使用 my_slow_iterator 并删除空 dtor:560 毫秒。
  • my_slow_iterator 与包含的空 dtor 一起使用:900 毫秒。

在这种情况下,空的 dtor 似乎导致了大约 40% 的减速。

显然,如果 dtor 是空的,那么为什么要拥有它,但我期待像这样的简单“空”函数会在编译时内联并优化掉。如果不是这种情况,那么我想了解发生了什么,以防此类问题在更复杂的情况下产生影响。

编辑:使用 O2 优化编译。

编辑:再深入一点,复制 ctor 似乎也出现了类似的效果。最初(在上面的测试中)my_slow_iterator 没有定义 copy-ctor,因此使用编译器生成的默认值。

如果我定义了以下copy-ctor(它的作用并不比我预期的编译器生成的要多):

my_slow_iterator (
    const my_slow_iterator<_Myvec> &_src
    ) : _ptr(_src._ptr) {}

对于与上述相同的测试,我看到以下结果:

  • 使用my_slow_iterator,删除dtor,包括copy-ctor:690ms
  • 使用my_slow_iterator,包括dtor,包括copy-ctor:980ms

这是对性能的进一步影响(尽管没有那么剧烈)。

为什么/如何编译器默认函数如此高效?用户定义的 ctor/dtor 是否在后台隐式执行某些操作?

【问题讨论】:

  • 发布版本,是的,但是是否开启了优化?您应该查看源代码/程序集列表 (/FAs) 以了解实际生成的内容:msdn.microsoft.com/en-us/library/367y26c6(v=vs.71).aspx
  • O2 优化,当然!我有点犹豫是否试图理解来自我的 std::sort 基准的汇编程序,但我想我可以尝试将其分解为对 dtor 的一次调用。
  • 按照建议将 throw() 添加到空 dtor 似乎没有帮助
  • @Darren,_Myvec::pointer 真的只是一个原始指针吗?
  • @Doug:我把那行改成_Myvec::value_type *_ptr 只是为了确定,似乎没什么区别...

标签: c++ visual-studio-2010


【解决方案1】:

我记得在 Linux 上遇到过与 GCC (-O3) 类似的事情。用户定义的析构函数的代码尽管是空的并且在头文件中,但仍被发出,而编译器生成的默认析构函数没有产生任何指令。这让我感到困惑,最终我使代码在没有显式析构函数的情况下工作(尽管以能够在其中添加 assert() 为代价,这就是为什么需要空的原因——它在调试版本中不是空的)。

【讨论】:

  • 那么你认为这是编译器优化问题,还是语言要求??有趣的是,不同的编译器会产生类似的效果。
  • 我不明白为什么语言会强制它。我没有详细研究它,但它确实让我觉得(有点令人惊讶)错过了优化机会。
  • 我不知道我是否感到完全满意,但我想优化选择取决于编译器。我想一般来说,这种效果是一个很好的论据,总是尽可能使用编译器默认函数,并且只在绝对必要时定义用户 ctor/dtor 等。
猜你喜欢
  • 2020-02-29
  • 2013-04-30
  • 1970-01-01
  • 2012-03-09
  • 2013-04-13
  • 2015-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多