azh*_*355 12 c++ vector std move-semantics c++11
目前,我正在学习C++对我自己,和我很好奇如何push_back()与emplace_back()引擎盖下工作.我总是认为emplace_back()当你试图构造并将一个大对象推到容器的后面时,如矢量更快.
让我们假设我有一个Student对象,我想要附加到学生矢量的背面.
struct Student {
string name;
int student_ID;
double GPA;
string favorite_food;
string favorite_prof;
int hours_slept;
int birthyear;
Student(string name_in, int ID_in, double GPA_in, string food_in,
string prof_in, int sleep_in, int birthyear_in) :
/* initialize member variables */ { }
};
Run Code Online (Sandbox Code Playgroud)
假设我调用push_back()并将Student对象推送到向量的末尾:
vector<Student> vec;
vec.push_back(Student("Bob", 123456, 3.89, "pizza", "Smith", 7, 1997));
Run Code Online (Sandbox Code Playgroud)
我在这里的理解是在向量之外push_back创建Student对象的实例,然后将其移动到向量的背面.
我也可以安抚而不是推动:
vector<Student> vec;
vec.emplace_back("Bob", 123456, 3.89, "pizza", "Smith", 7, 1997);
Run Code Online (Sandbox Code Playgroud)
我的理解是,Student对象是在向量的最后面构造的,因此不需要移动.
因此,放置更快是有意义的,特别是如果添加了许多Student对象.但是,当我计算这两个版本的代码时:
for (int i = 0; i < 10000000; ++i) {
vec.push_back(Student("Bob", 123456, 3.89, "pizza", "Smith", 7, 1997));
}
Run Code Online (Sandbox Code Playgroud)
和
for (int i = 0; i < 10000000; ++i) {
vec.emplace_back("Bob", 123456, 3.89, "pizza", "Smith", 7, 1997);
}
Run Code Online (Sandbox Code Playgroud)
我期望后者更快,因为不需要移动大的Student对象.奇怪的是,emplace_back版本最终变慢了(多次尝试).我还尝试插入10000000个Student对象,其中构造函数接受引用和参数,push_back()并emplace_back()存储在变量中.这也没有用,因为安慰仍然较慢.
我已经检查过以确保在两种情况下都插入了相同数量的对象.时差不是太大,但安慰结束时间慢了几秒钟.
我对如何push_back()和emplace_back()工作的理解是否有问题?非常感谢您的宝贵时间!
根据要求,这是代码.我正在使用g ++编译器.
推回:
struct Student {
string name;
int student_ID;
double GPA;
string favorite_food;
string favorite_prof;
int hours_slept;
int birthyear;
Student(string name_in, int ID_in, double GPA_in, string food_in,
string prof_in, int sleep_in, int birthyear_in) :
name(name_in), student_ID(ID_in), GPA(GPA_in),
favorite_food(food_in), favorite_prof(prof_in),
hours_slept(sleep_in), birthyear(birthyear_in) {}
};
int main() {
vector<Student> vec;
vec.reserve(10000000);
for (int i = 0; i < 10000000; ++i)
vec.push_back(Student("Bob", 123456, 3.89, "pizza", "Smith", 7, 1997));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Emplace back:
struct Student {
string name;
int student_ID;
double GPA;
string favorite_food;
string favorite_prof;
int hours_slept;
int birthyear;
Student(string name_in, int ID_in, double GPA_in, string food_in,
string prof_in, int sleep_in, int birthyear_in) :
name(name_in), student_ID(ID_in), GPA(GPA_in),
favorite_food(food_in), favorite_prof(prof_in),
hours_slept(sleep_in), birthyear(birthyear_in) {}
};
int main() {
vector<Student> vec;
vec.reserve(10000000);
for (int i = 0; i < 10000000; ++i)
vec.emplace_back("Bob", 123456, 3.89, "pizza", "Smith", 7, 1997);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这种行为是由于其复杂性std::string.这里有几件事情在互动:
push_back版本中,编译器能够在编译时确定字符串的长度,而编译器无法为该emplace_back版本执行此操作.因此,emplace_back呼叫需要呼叫strlen.此外,由于编译器不知道字符串文字的长度,它必须为SSO和非SSO情况发出代码(参见Jason Turner的"初始化列表已破坏,让我们修复它们" ;这是一个长篇大论,但他遵循将字符串插入到整个矢量中的问题)考虑这种更简单的类型:
struct type {
std::string a;
std::string b;
std::string c;
type(std::string a, std::string b, std::string c)
: a{a}
, b{b}
, c{c}
{}
};
Run Code Online (Sandbox Code Playgroud)
注意如何构造函数的副本 a,b和c.
针对仅分配内存的基线进行测试,我们可以看到push_back优于emplace_back:
因为示例中的字符串都适合SSO缓冲区,所以复制与在这种情况下移动一样便宜.因此,构造函数非常有效,并且改进emplace_back效果较小.
此外,如果我们在程序集中搜索调用push_back和调用emplace_back:
// push_back call
void foo(std::vector<type>& vec) {
vec.push_back({"Bob", "pizza", "Smith"});
}
Run Code Online (Sandbox Code Playgroud)
// emplace_back call
void foo(std::vector<type>& vec) {
vec.emplace_back("Bob", "pizza", "Smith");
}
Run Code Online (Sandbox Code Playgroud)
(大会没有在这里复制.它很庞大.std::string很复杂)
我们可以看到emplace_back有调用strlen,而push_back没有调用.由于字符串文字和std::string正在构造的文本之间的距离增加,编译器无法优化调用strlen.
显式调用std::string构造函数会删除对它的调用strlen,但不会再将它们构造到位,因此无法加速emplace_back.
所有这一切说,如果我们用足够长串离开SSO,分配成本完全淹没了这些细节,所以无论emplace_back和push_back有同样的表现:
如果修复构造函数type以移动其参数,emplace_back则在所有情况下都会变得更快.
struct type {
std::string a;
std::string b;
std::string c;
type(std::string a, std::string b, std::string c)
: a{std::move(a)}
, b{std::move(b)}
, c{std::move(c)}
{}
};
Run Code Online (Sandbox Code Playgroud)
但是,SSO push_back案件放缓了; 编译器似乎发出了额外的副本.
struct type {
std::string a;
std::string b;
std::string c;
template <typename A, typename B, typename C>
type(A&& a, B&& b, C&& c)
: a{std::forward<A>(a)}
, b{std::forward<B>(b)}
, c{std::forward<C>(c)}
{}
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
505 次 |
| 最近记录: |