💨

[C++] push_backよりもemplace_backを使った方がよい

2023/12/01に公開

C++でemplace_backというメンバ関数について学んだのでアウトプットする。

emplace_backは処理効率が良い

C++のvectorクラスにはpush_backというメンバ関数があるが、C++11から新たにemplace_backというメンバ関数が追加されている。

どちらも機能としてはコンテナに要素を追加するという点では同じだが、処理効率がemplace_backの方が高い。

以下は処理時間を計測したコード。

#include <iostream>
#include <string>
#include <vector>
#include <chrono>
#include <thread>

class Sample
{
  double _member;
public:
  Sample(int param)
  {
    for(int i = 0;i < 100;i++)
    {
      _member = _member * param;
    }
  }
};

int main()
{
  // push_backの処理時間を計測
  auto startTime = std::chrono::system_clock::now();
  std::vector<Sample> vec1;
  for(int i = 0;i < 100000;i++)
  {
    vec1.push_back(1);
  }
  auto endTime = std::chrono::system_clock::now();
  std::chrono::duration<double, std::milli> elapsed = endTime - startTime;
  std::cout << elapsed.count() << "ms" << std::endl; // 27.967ms


  // emplace_backの処理時間を計測
  startTime = std::chrono::system_clock::now();
  std::vector<Sample> vec2;
  for(int i = 0;i < 100000;i++)
  {
    vec1.emplace_back(1);
  }
  endTime = std::chrono::system_clock::now();
  elapsed = endTime - startTime;
  std::cout << elapsed.count() << "ms" << std::endl; // 25.997ms

  return 0;
}

vec1にはpush_backでSampleクラスのオブジェクトをコンテナに追加、vec2にはemplace_backで追加している。
push_backでの追加は27.967msの処理時間だが、emplace_backは25.997ms。
emplace_backの方が処理時間は速い。
どちらも処理結果は同じだが、処理効率には差が出る。

emplace_backの方が処理効率が良い理由

なぜemplace_backの方が処理効率が良いかというと、以下のようにコンテナに追加される際の動作に違いがあるからだ。

  • push_back
    引数として受け取った値をコピーまたはムーブしてvectorの要素へ格納する。そのため引数として渡す際に一時オブジェクトを生成する必要があり、その分のコストがかかる。
vec1.push_back(1); // 1.int型からSampleクラスの一時オブジェクトを生成
                   // 2.Sampleクラスの一時オブジェクトを元にvectorの要素がムーブコンストラクトされ追加
  • emplace_back
    引数として受け取った値をそのままvectorの要素のコンストラクタに渡して、vectorの要素として生成する。一時オブジェクトは使わない。
vec1.push_back(1); // 1.int型からvectorに入れるSampleオブジェクトを直接生成し追加

emplace_backの方が一時オブジェクトを使うコストが無いので処理効率が良いということだ。
コンストラクタ内で重い処理をやるタイプのオブジェクトをコンテナに追加する場合等は顕著に差が出ると思う。

補足

vectorにはemplace_backだけではなく、emplaceというメンバ関数もある。
これはinsertの高効率版メンバ関数。

Discussion