当在 a 中存储大量自定义类的实例(不是“简单”类,例如不是 a std::string
、不是 astd::complex
等)时std::vector
,我们应该选择一个简单的std::vector<X>
,还是std::vector<std::unique_ptr<X>>
更好的选择?
我写了一些基准代码(从这篇关于 C++03 的 C++11 移动语义改进的博客文章vector<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 中(wherestd::unique_ptr
不可用),似乎最好的选择是vector<shared_ptr<X>>
; 相反,在 C++11 中,启用移动语义std::unique_ptr
似乎提供了最好的结果。
我在这里错过了什么吗?这是一个很好的 C++ 指南,在 big vector
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;
}
}
////////////////////////////////////////////////////////////////////////////////