カスタムクラス(「単純な」クラスではない、たとえば、ではない、などではない)のインスタンスを多数格納する場合、単純なを選択する必要がありますか、それともより適切な選択ですか?std::string
std::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(std::unique_ptr
利用できない場合)では、最良の選択はvector<shared_ptr<X>>
;であるように思われます。代わりに、C ++ 11では、move-semantics-enabledstd::unique_ptr
が最良の結果を提供するようです。
ここで何かが足りませんか?これは、クラスインスタンス自体よりもクラスインスタンスへvector
の(スマート)ポインタを格納する方が良いという、優れた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;
}
}
////////////////////////////////////////////////////////////////////////////////