【问题标题】:Boost shared_ptr dereference cost提高 shared_ptr 取消引用成本
【发布时间】:2011-12-20 17:07:01
【问题描述】:

我正在尝试比较原始指针、boost shared_ptr 和 boostweak_ptr 之间的性能。在取消引用部分,我预计 shared_ptr 和 raw_ptr 是相等的,但结果显示 shared_ptr 的速度大约是原来的两倍。对于测试,我正在创建一个带有指向 int 的指针或共享指针的数组,然后在这样的循环中取消引用:

int result;
for(int i = 0; i != 100; ++i)
{
  for(int i = 0; i != SIZE; ++i)
    result += *array[i];
}

测试的完整代码可以在这里找到: https://github.com/coolfluid/coolfluid3/blob/master/test/common/utest-ptr-benchmark.cpp

可以在此处找到不带断言的优化构建的测试时间: http://coolfluidsrv.vki.ac.be/cdash/testDetails.php?test=145592&build=7777

感兴趣的值是“DerefShared time”和“DerefRaw time”

我猜这个测试可能存在某种缺陷,但我没有弄清楚差异来自哪里。分析显示来自 shared_ptr 的 operator* 被内联,它似乎需要更多时间。我仔细检查了 boost 断言是否关闭。

如果有人能解释差异可能来自哪里,我将非常感激。

额外的独立测试: https://gist.github.com/1335014

【问题讨论】:

  • +1,前几天我也在考虑这个问题。
  • 缓存效果? shared_ptr 比原始指针大,因此您的数组将覆盖更多缓存行并需要更长的时间来读取。
  • 您在什么平台上运行这些测试?
  • 为什么是指针数组?你不应该多次取消引用 same 指针吗?
  • @BartJanssens:我会通过的。定期批评比建设性批评更容易。 ;-)

标签: c++ boost shared-ptr


【解决方案1】:

正如 Alan Stokes 在他的评论中所说,这是由于缓存效应。共享指针包含一个引用计数,这意味着它们在内存中的物理大小比原始指针大。当存储在连续数组中时,每个缓存行获得的指针更少,这意味着循环必须比原始指针更频繁地进入主内存。

您可以通过在原始指针测试中分配SIZE*2 ints 来观察此行为,但也可以将取消引用循环更改为跨步i+=2 而不是++i。这样做在我的测试中产生了大致相同的结果。我的原始测试代码如下。

#include <iostream>
#include <boost/timer.hpp>

#define SIZE 1000000

typedef int* PtrT;

int do_deref(PtrT* array)
{
    int result = 0;
    for(int i = 0; i != 1000; ++i)
    {
        for(int i = 0; i != SIZE*2; i+=2)
            result += *array[i];
    }

    return result;
}

int main(void)
{
    PtrT* array = new PtrT[SIZE*2];
    for(int i = 0; i != SIZE*2; ++i)
        array[i] = new int(i);
    boost::timer timer;
    int result = do_deref(array);
    std::cout << "deref took " << timer.elapsed() << "s" << std::endl;
    return result;
}

顺便说一句,使用boost::make_shared&lt;int&gt;(i) 而不是PtrT(new int(I)) 会将引用计数和对象一起分配在内存中,而不是在不同的位置。在我的测试中,这将共享指针取消引用的性能提高了大约 10-20%。代码如下:

#include <iostream>
#include <boost/timer.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#define SIZE 1000000

typedef boost::shared_ptr<int> PtrT;

int do_deref(PtrT* array)
{
    int result = 0;
    for(int j = 0; j != 1000; ++j)
    {
        for(int i = 0; i != SIZE; ++i)
            result += *array[i];
    }

    return result;
}

int main(void)
{
    PtrT* array = new PtrT[SIZE];
    for(int i = 0; i != SIZE; ++i)
        array[i] = boost::make_shared<int>(i);
    boost::timer timer;
    int result = do_deref(array);
    std::cout << "deref took " << timer.elapsed() << "s" << std::endl;
    return result;
}

我的结果(x86-64 Unbuntu 11 VM):

Original Raw: 6.93
New Raw: 12.9
Original Shared: 12.7
New Shared: 10.59

【讨论】:

  • 共享指针包含一个引用计数,”实际上是一个指向具有 2 个引用计数的数据结构的指针、一个指针、一个删除器,甚至可能是一个互斥体(希望不是)。
  • @curious 大小为 2 个指针 - 一个指向实际有效负载,一个指向开销。但这些都不会影响简单威慑的成本。
  • 啊,非常感谢!我将调整我的测试以使比较更公平。
猜你喜欢
  • 2017-05-02
  • 1970-01-01
  • 2013-09-17
  • 1970-01-01
  • 1970-01-01
  • 2013-05-21
  • 1970-01-01
  • 1970-01-01
  • 2012-09-05
相关资源
最近更新 更多