c – 我们是否应该将智能指针存储到大型std :: vector中的类实例中以获得更好的性能?

c – 我们是否应该将智能指针存储到大型std :: vector中的类实例中以获得更好的性能?,第1张

概述当在std :: vector中存储大量自定义类的实例(不是“简单”类,例如不是std :: string,而不是std :: complex等)时,我们应该选择一个简单的std :: vector< X>,或者是std :: vector< std :: unique_ptr< X>>更好的选择? 我写了一些基准代码(从this blog post扩展代码关于C 03上的C 11移动语义改进), 当在std :: vector中存储大量自定义类的实例(不是“简单”类,例如不是std :: string,而不是std :: complex等)时,我们应该选择一个简单的std :: vector< X>,或者是std :: vector< std :: unique_ptr< X>>更好的选择?

我写了一些基准代码(从this blog post扩展代码关于C 03上的C 11移动语义改进),似乎向量< unique_ptr< X>>为1,500,000个项目向量提供更好的性能.事实上,在装有windows 7 64位,Intel Core i5四核cpu和8 GB RAM的PC上,我得到了以下结果(test.exe 1500):

> vector< unique_ptr< MyObject>>:1.5秒@H_419_5@> vector< shared_ptr< MyObject>>:1.6秒@H_419_5@> vector< MyObject>:1.8秒

因此,在C 03(其中std :: unique_ptr不可用)中,似乎最佳选择是vector< shared_ptr< X>&gt ;;而在C 11中,支持move-semantics的std :: unique_ptr似乎提供了最好的结果.

我在这里错过了什么吗?这是一个很好的C指南,在大向量中,最好将(智能)指针存储到类实例而不是类实例本身吗?

基准代码如下:

//////////////////////////////////////////////////////////////////////////////////// Test vector<X> vs. vector<unique_ptr<X>> vs. vector<shared_ptr<X>>.//// Original benchmark code from://   http://blogs.msdn.com/b/vcblog/archive/2009/06/23/stl-performance.aspx//////////////////////////////////////////////////////////////////////////////////#include <exception>    // std::invalID_argument#include <iostream>     // std::cout#include <memory>       // std::shared_ptr,std::unique_ptr#include <ostream>      // std::endl#include <stdexcept>    // std::exception#include <string>       // std::wstring#include <utility>      // std::move#include <vector>       // std::vector#include <windows.h>    // Win32 Platform SDK (high performance counters,etc.)using namespace std;// Measure time.class Stopwatch{public:    Stopwatch()        : m_start(0),m_finish(0)    {    }    static voID PerfStartup()    {        // to confine the test to run on a single processor         // in order to get consistent results for all tests.        SetThreadAffinityMask(GetCurrentThread(),1);        SetThreadIDealProcessor(GetCurrentThread(),0);        Sleep(1);    }    voID Start()    {        m_finish = 0;        m_start = Counter();    }    voID Stop()    {        m_finish = Counter();    }    // Elapsed time,in seconds    double elapsedtime() const    {        return (m_finish - m_start) * 1.0 / Frequency();    }    voID reset()    {        m_start = m_finish = 0;    }private:    long long m_start;    long long m_finish;    static long long Counter()     {        LARGE_INTEGER li;        queryPerformanceCounter(&li);        return li.QuadPart;    }    static long long Frequency()     {        LARGE_INTEGER li;        queryPerformanceFrequency(&li);        return li.QuadPart;    }// Ban copyprivate:    Stopwatch(const Stopwatch&);    Stopwatch& operator=(const Stopwatch&);};// Measure execution time of a block of code.class ScopedStopwatch{public:    ScopedStopwatch()    {        m_sw.Start();    }    ~ScopedStopwatch()    {        m_sw.Stop();        cout << "Elapsed time: " << m_sw.elapsedtime() << " sec" << endl;    }private:    Stopwatch m_sw;    ScopedStopwatch(const ScopedStopwatch&);    ScopedStopwatch& operator=(const ScopedStopwatch&);};// User defined Typeclass MyObject{public:    wstring name;    wstring address;    wstring telephone;    wstring name2;    wstring address2;    wstring telephone2;    // Default constructor    MyObject()    {    }    // copy Constructor    MyObject(const MyObject& other)        : name(other.name),telephone(other.telephone),address(other.address),name2(other.name2),telephone2(other.telephone2),address2(other.address2)    {    }    // copy assignment operator    MyObject& operator=(const MyObject& other)    {        if (this != &other)        {            name = other.name;            telephone = other.telephone;            address = other.address;            name2 = other.name2;            telephone2 = other.telephone2;            address2 = other.address2;        }        return *this;    }    // Move constructor    MyObject(MyObject&& other)        : name(move(other.name)),telephone(move(other.telephone)),address(move(other.address)),name2(move(other.name2)),telephone2(move(other.telephone2)),address2(move(other.address2))    {    }    // Move assignment operator    MyObject& operator=(MyObject&& other)    {        if (this != &other)        {            name = move(other.name);            telephone = move(other.telephone);            address = move(other.address);            name2 = move(other.name2);            telephone2 = move(other.telephone2);            address2 = move(other.address2);        }        return *this;    }};MyObject MakeTestObject(){    MyObject obj;    obj.name = L"Stephan T. Lavavej Stephan T. Lavavej Stephan T. Lavavej";    obj.telephone = L"314159265 314159265 314159265 314159265 314159265";    obj.address = L"127.0.0.0 127.0.0.0 127.0.0.0 127.0.0.0 127.0.0.0 127.0.0.0";    obj.name2 = L"Mohammad Usman. Mohammad Usman. Mohammad Usman. ";    obj.telephone2 = L"1234567890 1234567890 1234567890 1234567890 1234567890";    obj.address2 = L"Republik Of mancunia. Republik Of mancunia Republik Of mancunia";    return obj;}unique_ptr<MyObject> MakeUniqueTestObject(){    unique_ptr<MyObject> obj( new MyObject() );    obj->name = L"Stephan T. Lavavej Stephan T. Lavavej Stephan T. Lavavej";    obj->telephone = L"314159265 314159265 314159265 314159265 314159265";    obj->address = L"127.0.0.0 127.0.0.0 127.0.0.0 127.0.0.0 127.0.0.0 127.0.0.0";    obj->name2 = L"Mohammad Usman. Mohammad Usman. Mohammad Usman. ";    obj->telephone2 = L"1234567890 1234567890 1234567890 1234567890 1234567890";    obj->address2 = L"Republik Of mancunia. Republik Of mancunia Republik Of mancunia";    return obj;}shared_ptr<MyObject> MakeSharedTestObject(){        auto obj = make_shared<MyObject>();    obj->name = L"Stephan T. Lavavej Stephan T. Lavavej Stephan T. Lavavej";    obj->telephone = L"314159265 314159265 314159265 314159265 314159265";    obj->address = L"127.0.0.0 127.0.0.0 127.0.0.0 127.0.0.0 127.0.0.0 127.0.0.0";    obj->name2 = L"Mohammad Usman. Mohammad Usman. Mohammad Usman. ";    obj->telephone2 = L"1234567890 1234567890 1234567890 1234567890 1234567890";    obj->address2 = L"Republik Of mancunia. Republik Of mancunia Republik Of mancunia";    return obj;}voID Test(int count){    Stopwatch::PerfStartup();    cout << "Inserting " << count << " items in vector.\n";    cout << "\nTesting vector<MyObject>\n";    {        ScopedStopwatch sw;        vector<MyObject> v;        for (int i = 0; i < count; i++)        {            v.push_back(MakeTestObject());        }    }    cout << "\nTesting vector<unique_ptr<MyObject>>\n";    {        ScopedStopwatch sw;        vector<unique_ptr<MyObject>> v;        for (int i = 0; i < count; i++)        {            v.push_back(MakeUniqueTestObject());        }    }    cout << "\nTesting vector<shared_ptr<MyObject>>\n";    {        ScopedStopwatch sw;        vector<shared_ptr<MyObject>> v;        for (int i = 0; i < count; i++)        {            v.push_back(MakeSharedTestObject());        }    }}int main(int argc,char * argv[]){    static const int kExitOk = 0;    static const int kExitError = 1;    try    {        if (argc != 2)        {            throw invalID_argument("Bad Syntax. Pass insertion count (x 1,000).");        }        const int countK = atoi(argv[1]);        Test(countK * 1000);        return kExitOk;    }    catch (const exception & e)       {        cerr << "*** ERROR: " << e.what() << endl;        return kExitError;    }}////////////////////////////////////////////////////////////////////////////////
解决方法 在C 11中,如果您没有使用启用移动的对象,那么您应该使用std :: unique_ptr< T>的向量.来自#include< memory>.的std ::的unique_ptr< T>重量较轻,具有与std :: shared_ptr< T>相似的语义.但在一个重要领域有所不同:对象所有权是明确的.在vector的情况下,向量拥有它包含的对象.现在,如果您正在使用启用移动的对象,只需使用对象的向量,因为它通常“足够快”. C 11中所有启用STL的容器都使用了移动语义(即是的,稍慢,但你在生产力方面获益).如果性能是一个问题,你可以回到std :: unqiue_ptr< T>原因如下.

如果您使用的是pre-C 11,则boost :: shared_ptr< T>并不是你可以做的更糟糕的事情,并且可能是一个适当的过渡路径,直到std :: unique_ptr< T>变得可用.使用boost :: shared_ptr< T>涉及原子增量和指针的赋值.两者都比std :: unique_ptr< T>相当便宜,但更昂贵(和不同的语义).

Move构造函数比移动std :: unique_ptr< T>更昂贵并不让我感到惊讶.因为移动构造函数仍在分配一个对象(即使它的内容/内容被借用,移动,重新定位),而移动一个std :: unique_ptr< T>只是一个整数/指针赋值.使用jemalloc(3)可能会降低Move构造函数的成本,但这只能在* NIX平台上使用.

由于最后一点,基准并不完全是苹果对苹果.如果您正在寻找一致的性能,std :: unique_ptr< T>可能是要走的路(没有分配),但是如果你正在寻找一种“原生”开发习语,这有助于简化开发方法,其中性能不是最重要的方面(即生产力比性能更重要),那么与移动构造函数一起使用普通对象.

总结

以上是内存溢出为你收集整理的c – 我们是否应该将智能指针存储到大型std :: vector中的类实例中以获得更好的性能?全部内容,希望文章能够帮你解决c – 我们是否应该将智能指针存储到大型std :: vector中的类实例中以获得更好的性能?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1254965.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存