[英]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);
}
大約比v.push_back(obj("Jon", 45, 500.6f));
的等效時間慢2倍v.push_back(obj("Jon", 45, 500.6f));
而且我不明白為什么。
我用bot g++ 4.7.2
和clang++ 3.3
。
哪里我錯了?
現在我已經糾正了我的移動控制器,我將添加更多
我正在使用Linux下的time
實用程序測試這個2並用它們編譯它們
g++-4.7 -std=c++11 -s -O3 -DNDEBUG
要么
clang++ -std=c++11 -s -O3 -DNDEBUG
您應該將數據從參數移動到移動構造函數:
obj(obj&& tmp)
:
name(std::move(tmp.name)), age(std::move(tmp.age)), money(std::move(tmp.money)) {}
雖然如果正確使用emplace_back
這應該是無關緊要的。
什么都不做更好。 你試圖讓它更快(比你在編寫移動構造函數之前實際分析的那些更快?),但你打破了它。
編譯器免費生成復制和移動構造函數和賦值運算符,並且她做得對。 通過決定編寫自己的編寫,你告訴編譯器你知道的更好,所以她只是讓路,並讓你自己
改進
。
你破壞的第一件事是,你讓你的移動構造函數實際上是復制 。 具有名稱的東西是左值,並且即使它們是右值引用,也不能隱式移動左值。 所以初始化器需要實際調用std::move
。
你破壞的第二件事是你沒有讓移動構造函數聲明它不會通過添加noexcept
來拋出它。 編譯器生成了一個有這個。 通過不聲明不拋出任何異常, std::vector
的實現在重新分配底層存儲時可能不會使用移動:它不能提供強大的異常保證而不保證移動不拋出。
這樣做會讓它表現得更好嗎? 也許。 也許不吧。 您的實現可能正在對std::string
進行小字符串優化,這意味着沒有動態分配:整個字符串"Jon"
,很小,將直接存儲在std::string
對象中。 這使得移動與復制具有相同的成本。
你可以通過動態分配它並使用unique_ptr
來使整個obj
結構利用廉價的移動。 即使在存在小字符串優化的情況下,這也會使移動比副本便宜。 但是,您需要為分配成本和額外間接費用付出便宜的代價。 無論這是否可取,只有你能說出來。
代替
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 :: string主導,因此移動構造和emplace構造之間的差異是微不足道的。
這需要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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.