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.8秒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纳秒。这真的值得吗?你真的值得吗