C++11-move语义的构建速度较慢
C++11 - move semantics is slow on construction
此代码
#include <iostream>
#include <vector>
struct obj
{
std::string name;
int age;
float money;
obj():name("NO_NAME"),age(0),money(0.0f){}
obj(const std::string& _name, const int& _age, const float& _money):name(_name),age(_age),money(_money){}
obj(obj&& tmp): name(tmp.name), age(tmp.age), money(tmp.money) {}
obj& operator=(obj&&) {return *this;}
};
int main(int argc, char* argv[])
{
std::vector<obj> v;
for( int i = 0; i < 5000000; ++i )
{
v.emplace_back(obj("Jon", 45, 500.6f));
}
return(0);
}
比CCD_ 1的等效速度慢大约2倍,我不明白为什么。
我已经用机器人g++ 4.7.2
和clang++ 3.3
对此进行了测试。
我哪里错了?
现在我已经更正了我的移动解释,我将添加更多
这是一个推送版本的
这是template_back版本
我正在Linux下用time
实用程序测试这2,并用编译它们
g++-4.7 -std=c++11 -s -O3 -DNDEBUG
或
clang++ -std=c++11 -s -O3 -DNDEBUG
什么都不做更好。你试图让它更快(比什么更快?在你写移动构造函数之前,你真的对进行了评测吗?),但你破坏了它。
编译器免费生成复制和移动构造函数以及赋值运算符,而且她做得很好。通过决定自己编写,你就是在告诉编译器你更了解,所以她只是让开,让你改进自己打破它。
你打破的第一件事是,你让你的move构造函数实际上复制。有名称的事物是左值,即使它们是右值引用,也不能隐式地移动左值。因此初始化程序需要实际调用std::move
。
你破坏的第二件事是,你没有通过向move构造函数添加noexcept
来声明它不抛出。由于没有声明没有抛出异常,std::vector
的实现在重新分配底层存储时可能不会使用移动:如果没有移动不会抛出的保证,它就无法提供强大的异常保证。
这样做会让它表现得更好吗?大概也许不是。您的实现可能在std::string
上进行小字符串优化,这意味着没有动态分配:整个字符串"Jon"
很小,将直接存储在v.push_back(obj("Jon", 45, 500.6f));
0对象中。这使得移动的成本与复制的成本相同。
通过动态分配并使用unique_ptr
,可以使整个obj
结构利用廉价的移动。这将使移动比复制更便宜,即使在进行小字符串优化的情况下也是如此。然而,你正在用分配成本和额外的间接性来为这种廉价付出代价。这是否可取,只有你自己才能判断。
您应该将数据从参数移动到移动构造函数:
obj(obj&& tmp)
:
name(std::move(tmp.name)), age(std::move(tmp.age)), money(std::move(tmp.money)) {}
尽管如果正确使用emplace_back
,这应该无关紧要。
而不是
v.emplace_back(obj("Jon", 45, 500.6f));
尝试
v.emplace_back("Jon", 45, 500.6f);
push_back
具有启用移动的过载。emplace_back
用于就地施工。
编辑:R.Martinho Fernandes说的话。:)
obj(obj&& tmp): name(std::move(tmp.name)), age(std::move(tmp.age)), money(std::move(tmp.money)) {}
这可能就是您想要的:
struct obj
{
std::string name;
int age;
float money;
obj()
: name("NO_NAME")
, age(0)
, money(0.0f)
{
}
obj(std::string _name, int _age, float _money)
: name(std::move(_name))
, age(std::move(_age))
, money(std::move(_money))
{
}
};
int main(int argc, char* argv[])
{
std::vector<obj> v;
for( int i = 0; i < 5000000; ++i )
{
v.emplace_back("Jon", 45, 500.6f);
}
return(0);
}
请注意,我更改了您的obj(std::string _name, int _age, float _money)
构造函数以移动_name
,而不是对其进行不必要的复制。
您也错误地调用了emplace_back
,应该是emplace_back("Jon", 45, 500.6f)
。
所有其他内容都是由编译器自动优化生成的。
运行时间主要由字符串文本的std::字符串构造决定,因此move构造和template构造之间的区别很小。
这在我的机器上需要400毫秒:
#include <iostream>
#include <vector>
using namespace std;
struct obj
{
string name;
int age;
float money;
};
int main(int argc, char* argv[])
{
vector<obj> v;
for( int i = 0; i < 5000000; ++i )
{
v.emplace_back(obj{"Jon", 45, 500.6f});
}
return v.size();
}
这在我的机器上需要80毫秒:
#include <iostream>
#include <vector>
using namespace std;
struct obj
{
int age;
float money;
};
int main(int argc, char* argv[])
{
vector<obj> v;
for( int i = 0; i < 5000000; ++i )
{
v.emplace_back(obj{45, 500.6f});
}
return v.size();
}
请注意,一个普通结构将为其生成一个合理的默认移动构造函数
这已经在我的机器上花费了220毫秒:
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
int t = 0;
for( int i = 0; i < 5000000; ++i )
{
string s("Jon");
t += s.size();
}
return t;
}
- C++为构建时间获取QDateTime的可靠方法
- 无法在 CLion 中构建 C++ 项目
- 函数向量_指针有不同的原型,我可以构建一个吗
- 如何使用ndk-build.cmd构建Android.so文件
- libssh 的函数在构建 libssh 时无法在 Qt 和 cmake 错误中找到
- 为什么在读取文件大小时文件IO速度会发生变化
- 使用cmake从源代码构建MySQL连接器/C++失败(与以前的声明冲突)
- VSCode-有一个红色下划线,但程序构建和运行正确,并且出现配音错误
- 构建可组合有向图(扫描仪生成器的汤普森构造算法)
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- 文件系统:复制功能的速度秘诀是什么
- 无法使用Qt Creator在Windows中构建yaml-cpp
- 构建一个由C和C++文件组成的库
- llvm构建器向基本块添加终止符
- 使用 Unity 构建加快C++构建速度,并减少标头依赖项
- 如何构建模板的显式实例化以提高编译速度?
- C 通过使用库来提高大型项目的构建速度
- C++11-move语义的构建速度较慢
- c#构建速度vs原生c++
- c++ vector或Queue来构建内存和速度方面的大Q