我写了一些基准代码(从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秒
> vector< shared_ptr< MyObject>>:1.6秒
> vector< MyObject>:1.8秒
因此,在C 03(其中std :: unique_ptr不可用)中,似乎最佳选择是vector< shared_ptr< X>> ;;而在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 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; } } ////////////////////////////////////////////////////////////////////////////////@H_404_15@
解决方法
如果您使用的是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>可能是要走的路(没有分配),但是如果你正在寻找一种“原生”开发习语,这有助于简化开发方法,其中性能不是最重要的方面(即生产力比性能更重要),那么与移动构造函数一起使用普通对象.