【问题标题】:Which is faster on Visual C++ 2010 - std::shared_ptr or boost::shared_ptr?Visual C++ 2010 - std::shared_ptr 或 boost::shared_ptr 哪个更快?
【发布时间】:2011-09-22 04:51:19
【问题描述】:

有没有人在发布模式构建中测试过这个?还是实现如此相似而没有显着差异?

我对以下速度感兴趣:

  1. 创建一个新的 shared_ptr

  2. 创建 shared_ptr 的副本

  3. 取消引用指针以访问被指针对象

这将在一个发布版本中优化速度,使用 make_shared() 创建新的 shared_ptrs

【问题讨论】:

  • 为什么不自己测量呢?我想不会有可衡量的差异。
  • std::shared_ptr 通常基于boost::shared_ptr,但我认为理论上std:: 版本可能更快,因为它允许利用boost 可能没有的编译器特定支持。但我认为通常它们会同样快。仍然,当有疑问时测量。
  • 您执行这些操作的频率是否足够重要。
  • @sven 我希望有人已经这样做了。因此问题。如果没有,我会尝试并发布结果。
  • @Matteo Italia:Boost 库有相当多的编译器知识。它们是多平台的这一事实并不意味着它们不会利用编译它们的每个平台。

标签: c++ visual-c++ boost shared-ptr smart-pointers


【解决方案1】:

好的,所以看起来没有人这样做过。以下是我使用 WIN32 控制台应用程序的标准 VC 10 优化设置发现的结果:

  1. Visual C++ 2010 SP1 std::make_shared 和 std::shared_ptr 在填充 1000 万个指针条目的向量时比 Boost 1.46.1 等价物更快(20 次运行平均为 1.96 秒与 0.92 秒)

  2. 在复制包含 1000 万个指针条目的数组时,Boost 1.46.1 比 Visual C++ 2010 SP1 稍快(20 次运行平均为 0.15 秒与 0.17 秒)

  3. Visual C++ 2010 SP1 在解引用包含 1000 万个指针条目的向量 20 次(20 次运行平均为 0.72 秒与 0.811 秒)时,略快于 Boost 1.46.1 等效版本

结论:创建 shared_ptrs 以填充向量时存在显着差异。 Visual C++ 2010 shared_ptr 的速度几乎是 Boost 1.46.1 的两倍,这表明在实现上存在显着差异。

其他测试没有显示出显着差异。

这是我使用的代码:

#include "stdafx.h"

struct A
{
    A( const unsigned A) : m_value(A)
    {
    }

    const unsigned m_value;
};

typedef std::shared_ptr<A> APtr;
typedef boost::shared_ptr<A> ABoostPtr;


double TestSTLCreateSpeed()
{
    const unsigned NUM_ENTRIES = 10000000;
    std::vector<APtr> buffer;
    buffer.reserve(NUM_ENTRIES);

    boost::timer timer;

    for( unsigned nEntry = 0; nEntry < NUM_ENTRIES; ++nEntry)
    {
        buffer.emplace_back( std::make_shared<A>(nEntry) );
    }

    const double timeTaken = timer.elapsed();

    std::cout << "STL create test took " << timeTaken << " secs.\r\n";
    return timeTaken;
}

double BoostSTLCreateSpeed()
{
    const unsigned NUM_ENTRIES = 10000000;
    std::vector<ABoostPtr> buffer;
    buffer.reserve(NUM_ENTRIES);

    boost::timer timer;

    for( unsigned nEntry = 0; nEntry < NUM_ENTRIES; ++nEntry)
    {
        buffer.emplace_back( boost::make_shared<A>(nEntry) );
    }

    const double timeTaken = timer.elapsed();

    std::cout << "BOOST create test took " << timeTaken << " secs.\r\n";
    return timeTaken;
}

double TestSTLCopySpeed()
{
    const unsigned NUM_ENTRIES = 10000000;
    std::vector<APtr> buffer;
    buffer.reserve(NUM_ENTRIES);

    for( unsigned nEntry = 0; nEntry < NUM_ENTRIES; ++nEntry)
    {
        buffer.emplace_back( std::make_shared<A>(nEntry) );
    }

    boost::timer timer;
    std::vector<APtr> buffer2 = buffer;

    const double timeTaken = timer.elapsed();

    std::cout << "STL copy test took " << timeTaken << " secs.\r\n";
    return timeTaken;
}

double TestBoostCopySpeed()
{
    const unsigned NUM_ENTRIES = 10000000;
    std::vector<ABoostPtr> buffer;
    buffer.reserve(NUM_ENTRIES);

    for( unsigned nEntry = 0; nEntry < NUM_ENTRIES; ++nEntry)
    {
        buffer.emplace_back( boost::make_shared<A>(nEntry) );
    }

    boost::timer timer;
    std::vector<ABoostPtr> buffer2 = buffer;

    const double timeTaken = timer.elapsed();

    std::cout << "BOOST copy test took " << timeTaken << " secs.\r\n";
    return timeTaken;
}

double TestBoostDerefSpeed()
{
    const unsigned NUM_ENTRIES = 10000000;
    std::vector<ABoostPtr> buffer;
    buffer.reserve(NUM_ENTRIES);

    for( unsigned nEntry = 0; nEntry < NUM_ENTRIES; ++nEntry)
    {
        buffer.emplace_back( boost::make_shared<A>(nEntry) );
    }

    boost::timer timer;

    unsigned total = 0;

    for(unsigned nIter = 0; nIter < 20; ++nIter)
    {
        std::for_each( buffer.begin(), buffer.end(),
            [&](const ABoostPtr& pA){ 
                total += pA->m_value;
        });
    }

    const double timeTaken = timer.elapsed();

    std::cout << "BOOST deref total =  " << total << ".\r\n";

    std::cout << "BOOST deref test took " << timeTaken << " secs.\r\n";
    return timeTaken;
}

double TestSTLDerefSpeed()
{
    const unsigned NUM_ENTRIES = 10000000;
    std::vector<APtr> buffer;
    buffer.reserve(NUM_ENTRIES);

    for( unsigned nEntry = 0; nEntry < NUM_ENTRIES; ++nEntry)
    {
        buffer.emplace_back( std::make_shared<A>(nEntry) );
    }

    boost::timer timer;

    unsigned total = 0;
    for(unsigned nIter = 0; nIter < 20; ++nIter)
    {
        std::for_each( buffer.begin(), buffer.end(),
            [&](const APtr& pA){ 
                total += pA->m_value;
        });
    }

    const double timeTaken = timer.elapsed();

    std::cout << "STL deref total =  " << total << ".\r\n";

    std::cout << "STL deref test took " << timeTaken << " secs.\r\n";
    return timeTaken;
}

int _tmain(int argc, _TCHAR* argv[])
{
    double totalTime = 0.0;
    const unsigned NUM_TESTS = 20;

    totalTime = 0.0;

    for ( unsigned nTest = 0; nTest < NUM_TESTS; ++nTest)
    {
        totalTime += BoostSTLCreateSpeed();
    }

    std::cout << "BOOST create test took " << totalTime / NUM_TESTS << " secs average.\r\n";

    totalTime = 0.0;
    for ( unsigned nTest = 0; nTest < NUM_TESTS; ++nTest)
    {
        totalTime += TestSTLCreateSpeed();
    }

    std::cout << "STL create test took " << totalTime / NUM_TESTS << " secs average.\r\n";


    totalTime = 0.0;
    for ( unsigned nTest = 0; nTest < NUM_TESTS; ++nTest)
    {
        totalTime += TestBoostCopySpeed();
    }

    std::cout << "BOOST copy test took " << totalTime / NUM_TESTS << " secs average.\r\n";

    totalTime = 0.0;
    for ( unsigned nTest = 0; nTest < NUM_TESTS; ++nTest)
    {
        totalTime += TestSTLCopySpeed();
    }

    std::cout << "STL copy test took " << totalTime / NUM_TESTS << " secs average.\r\n";

    totalTime = 0.0;
    for ( unsigned nTest = 0; nTest < NUM_TESTS; ++nTest)
    {
        totalTime += TestBoostDerefSpeed();
    }

    std::cout << "Boost deref test took " << totalTime / NUM_TESTS << " secs average.\r\n";

    totalTime = 0.0;
    for ( unsigned nTest = 0; nTest < NUM_TESTS; ++nTest)
    {
        totalTime += TestSTLDerefSpeed();
    }

    std::cout << "STL deref test took " << totalTime / NUM_TESTS << " secs average.\r\n";

    return 0;
}

我会等一会儿,如果没有人反驳我的结果或得出更好的结论,我会接受我自己的答案。

【讨论】:

  • 感谢您花时间报告您的结果。
  • 感谢您的报告。现在,您可能还想使用自定义删除器测试删除行为,因为这也是经常使用的重要部分,尤其是对于shared_ptr&lt;void&gt;。
  • 知道 VS 实现中的什么让它更快吗?我对 boost 实现非常熟悉,并且没有任何明显的方法可以改进它。也许它基于知道 MS 的分配器正在使用而做出不可移植的假设?
【解决方案2】:

VS10 的版本尽可能使用右值引用和移动语义,因此原则上它比 Boost C++98 实现占上风。您可能需要相当努力地创建一个程序,该程序会显示出显着的实际差异......但请尝试一下。也不要忘记std::make_shared,由于转发,这是 C++0x 中的新内容。

更新:在任何情况下,取消引用和复制实际上都是相同的。也许在自定义删除器和分配器的存储方式以及 make_shared 的实现方式方面存在一些有趣的差异。让我检查一下来源。

更新 2:有趣的是,使用可变参数模板和右值引用的 Boost 版本肯定看起来比 VS10 版本更好,因为 VS10 没有可变参数模板和不得不使用可怕的黑色艺术来伪造这种行为。但这完全是编译时问题,因此不相关。

【讨论】:

  • Boost 不限于 C++98。它使用 C++0x 功能,如可用的右值。所以在 VC 10 上它确实使用了完美转发和移动语义等。见boost.org/doc/libs/1_46_1/libs/smart_ptr/make_shared.html
  • @mpipe3:好的——在这种情况下,Boost 的 C++0x 实现可能与 VS10 非常相似。不知道 Boost 的 make_shared 是否有和 VS10 一样的高效实现?
  • 我很困惑。您是说 VS 版本可能更快还是增强版本? :-)
  • @liori:我不确定 Boost 如何充分利用新的 C++0x 特性。看起来确实如此,因此两者将几乎相同。如果强制 Boost 符合 C++98,那么 VS10 应该会有一点优势。
猜你喜欢
  • 2011-09-27
  • 2013-06-12
  • 2013-04-16
  • 2012-09-01
  • 2011-10-29
  • 2011-09-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多