3

このコード

#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);
}

と同等のものよりも約 2 倍遅く、そのv.push_back(obj("Jon", 45, 500.6f));理由がわかりません。

これを botg++ 4.7.2とでテストしましclang++ 3.3た。

どこが間違っていますか?


移動コンストラクターを修正したので、さらに追加します

これは push_back バージョンです

これは emplace_back バージョンです

Linuxのユーティリティでこの2をテストし、timeそれらをコンパイルしています

g++-4.7 -std=c++11 -s -O3 -DNDEBUG

また

clang++ -std=c++11 -s -O3 -DNDEBUG
4

5 に答える 5

5

何もしない方が良いです。あなたはそれをより速くしようとしました (何より速く?移動コンストラクターを書く前に実際にプロファイリングしましたか?)、しかしあなたはそれを壊しました。

コンパイラはコピー コンストラクターとムーブ コンストラクター、および代入演算子を無料で生成します。自分で作成することを決定することで、コンパイラーに自分がよく知っていることを伝えることになります。

最初に壊れたのは、移動コンストラクターを実際にコピーしたことです。名前のあるものは左辺値であり、右辺値参照であっても、左辺値を暗黙的に移動することはできません。したがって、イニシャライザは実際に を呼び出す必要がありますstd::move

あなたが破った2番目のことは、移動コンストラクターに、追加によってスローしないことを宣言させなかっnoexceptたことです。コンパイラが生成したものにはこれがありました。例外がスローされないことを宣言しないことにより、 の実装はstd::vector、基になるストレージを再割り当てするときにおそらくムーブを使用しません。ムーブがスローしないという保証なしに、強力な例外保証を提供することはできません。

これをすべて行うと、パフォーマンスが向上しますか? 多分。そうでないかもしれない。あなたの実装は で小さい文字列の最適化を行っている可能性がありますstd::string。これは、動的な割り当てがないことを意味します。"Jon"小さい文字列全体がオブジェクトに直接格納されstd::stringます。これにより、移動のコストはコピーと同じになります。

obj動的に割り当ててunique_ptr. これにより、小さな文字列の最適化が存在する場合でも、移動はコピーよりも安価になります。ただし、その安さの代償として、割り当てのコストと余分な間接費が発生します。それが望ましいかどうかは、あなただけが知ることができます。

于 2012-11-20T13:30:22.630 に答える
4

データを引数からmoveコンストラクターに移動する必要があります。

obj(obj&& tmp)
: 
name(std::move(tmp.name)), age(std::move(tmp.age)), money(std::move(tmp.money)) {}

emplace_back正しく使用すれば、これは関係ありませんが。

于 2012-11-20T13:16:57.507 に答える
2

これはおそらくあなたが望むものです:

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)

他のすべてのものは、コンパイラによって自動的に最適に生成されます。

于 2012-11-20T13:15:55.967 に答える
2

実行時間は、文字列リテラルからの std::string の構築によって支配されるため、move 構築と 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;
}
于 2012-11-20T13:41:57.900 に答える
2

それ以外の

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)) {}
于 2012-11-20T13:09:28.023 に答える