C++ 我们是否应该将指向类实例的智能指针存储在大型std::vector';是为了更好的表现?

C++ 我们是否应该将指向类实例的智能指针存储在大型std::vector';是为了更好的表现?,c++,performance,vector,c++11,move-semantics,C++,Performance,Vector,C++11,Move Semantics,当在std::vector中存储大量自定义类(不是“简单”类,例如不是std::string,不是std::complex等)的实例时,我们应该选择简单的std::vector,还是选择std::vector更好 我编写了一些基准代码(扩展了C++11上的代码,在C++03上对移动语义进行了改进),并且似乎vector为1500000项向量提供了更好的性能。事实上,在装有Windows 7 64位、Intel Core i5四核CPU和8 GB RAM的PC上,我得到了以下结果(test.exe

当在
std::vector
中存储大量自定义类(不是“简单”类,例如不是
std::string
,不是
std::complex
等)的实例时,我们应该选择简单的
std::vector
,还是选择
std::vector
更好

我编写了一些基准代码(扩展了C++11上的代码,在C++03上对移动语义进行了改进),并且似乎
vector
为1500000项向量提供了更好的性能。事实上,在装有Windows 7 64位、Intel Core i5四核CPU和8 GB RAM的PC上,我得到了以下结果(
test.exe 1500
):

  • 矢量
    :1.5秒
  • 向量:1.6秒
  • 矢量
    :1.8秒
  • 因此,在C++03(其中
    std::unique_ptr
    不可用)中,最好的选择似乎是
    vector
    ;相反,在C++11中,启用移动语义的
    std::unique\u ptr
    似乎提供了最佳结果

    我是不是遗漏了什么?这是一个好的C++准则,在大<代码> vector < /C> > s中,最好将指针(类)存储到类实例中,而不是类实例本身? 基准代码如下:

    ////////////////////////////////////////////////////////////////////////////////
    //
    // 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 copy
    private:
        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 Type
    class 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;
        }
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    
    ////////////////////////////////////////////////////////////////////////////////
    //
    //测试向量vs.向量vs.向量。
    //
    //原始基准代码来自:
    //   http://blogs.msdn.com/b/vcblog/archive/2009/06/23/stl-performance.aspx
    //
    ////////////////////////////////////////////////////////////////////////////////
    #include//std::无效的_参数
    #include//std::cout
    #包括//std::shared\u ptr、std::unique\u ptr
    #include//std::endl
    #include//std::exception
    #include//std::wstring
    #include//std::move
    #include//std::vector
    #包括//Win32平台SDK(高性能计数器等)
    使用名称空间std;
    //测量时间。
    阶级秒表
    {
    公众:
    秒表
    :m_开始(0),
    m_完成(0)
    {
    }
    静态void PerfStartup()
    {
    //将测试限制在单个处理器上运行
    //以获得所有测试的一致结果。
    SetThreadAffinityMask(GetCurrentThread(),1);
    SetThreadIdealProcessor(GetCurrentThread(),0);
    睡眠(1);
    }
    void Start()
    {
    m_finish=0;
    m_start=计数器();
    }
    无效停止()
    {
    m_finish=计数器();
    }
    //已用时间,以秒为单位
    双ElapsedTime()常量
    {
    返回(m_完成-m_开始)*1.0/频率();
    }
    无效重置()
    {
    m_开始=m_结束=0;
    }
    私人:
    长时间的开始;
    长m_完成;
    静态长计数器()
    {
    大整数李;
    QueryPerformanceCounter(&li);
    返回li.QuadPart;
    }
    静态长-长频率()
    {
    大整数李;
    查询性能频率(&li);
    返回li.QuadPart;
    }
    //禁止复制
    私人:
    秒表(常数秒表&);
    秒表和操作员=(常数秒表和);
    };
    //测量代码块的执行时间。
    类ScopedStopwatch
    {
    公众:
    ScopedStopwatch()
    {
    m_sw.Start();
    }
    ~ScopedStopwatch()
    {
    m_sw.停止();
    cout name2=L“Mohammad Usman.Mohammad Usman.Mohammad Usman.”;
    obj->电话2=L“1234567890 1234567890 1234567890 1234567890 1234567890 1234567890”;
    obj->address2=L“曼彻斯特共和国。曼彻斯特共和国曼彻斯特共和国”;
    返回obj;
    }
    共享\u ptr MakeSharedTestObject()
    {    
    自动obj=使_共享();
    obj->name=L“Stephan T.Lavavej Stephan T.Lavavej Stephan T.Lavavej”;
    obj->电话=L“314159265 314159265 314159265 314159265 314159265 314159265 314159265 314159265”;
    obj->address=L“127.0.0.0 127.0.0 127.0.0.0 127.0.0.0 127.0.0.0 127.0.0 127.0.0.0”;
    obj->name2=L“穆罕默德·乌斯曼。穆罕默德·乌斯曼。穆罕默德·乌斯曼。”;
    obj->电话2=L“1234567890 1234567890 1234567890 1234567890 1234567890 1234567890”;
    obj->address2=L“曼彻斯特共和国。曼彻斯特共和国曼彻斯特共和国”;
    返回obj;
    }
    无效测试(整数计数)
    {
    秒表::PerfStartup();
    
    cout这取决于您如何使用它。如果您经常复制项目,那么使用指针而不是值会更快,因为您只需要复制/移动指针。请注意,插入项目时,复制占主导地位,因为当vector的内存重新分配时,所有项目都必须移动/复制到新位置

    如果您经常只是读取或修改向量中的项,那么将项存储为值会更快,因为间接寻址更少,内存位置更好(更好地使用CPU缓存)。如果直接存储项目,则使用的内存也较少。使用
    保留
    放置回
    可以避免插入速度较慢的小缺点。这样,它很可能比使用指针向量更快

    除非需要其他代码指向对象,否则我将始终使用值向量

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

    除非你的类实例是巨大的、移动的或者复制它们,这将需要大量的工作,否则我会觉得太早担心它。如果每个移动操作需要移动几十或几百个字节,它将产生更大的差异。 我假设您使用的是64位系统。现在,

    sizeof(MyObject)
    将等于24(无论如何都是如此)。否则您将处理唯一指针,其大小可能为12字节,或者共享指针,其大小可能为16字节

    你为1500000次操作节省了大约0.3秒,或者每次操作节省了大约200纳秒。这真的值得吗?你真的值得吗