簡體   English   中英

C ++ 11 - 移動語義在構造上很慢

[英]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.2clang++ 3.3

哪里我錯了?


現在我已經糾正了我的移動控制器,我將添加更多

這是一個push_back版本

這是emplace_back版本

我正在使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM