web-dev-qa-db-ja.com

あるstd :: vectorを別のstd :: vectorの最後に追加する最も効率的な方法は何ですか?

V1をターゲットベクトルとし、v2をその後ろに追加する必要があります。

私は今やっています:

v1.reserve(v1.size() + v2.size()); 
copy(v2.begin(), v2.end(), back_inserter(v1));

これが最も効率的な方法ですか?または、メモリのチャンクをコピーするだけで実行できますか?ありがとう!

48
Alex Jenter

多くの議論(およびMatthieu M.とvillintehaspamからの合理的なコメント)の後、提案を次のように変更します。

v1.insert( v1.end(), v2.begin(), v2.end() );

以前の提案をここに保管します。

v1.reserve( v1.size() + v2.size() ); 
v1.insert( v1.end(), v2.begin(), v2.end() );

後者の方法で行う理由はいくつかありますが、どれも十分な強度はありません。

  • ベクターがどのサイズに再割り当てされるかについての保証はありません。合計サイズが1025の場合、実装に応じて2048に再割り当てされる可能性があります。 reserveについてもそのような保証はありませんが、特定の実装ではそれは真実かもしれません。ボトルネックを探す場合は、それを確認するのが妥当かもしれません。
  • reserveは、意図が明確であることを示しています-この場合、最適化はより効率的です(reserveは、最高の実装でキャッシュを準備できます)。
  • また、reserveを使用すると、単一の再割り当てのみが行われることをC++標準で保証しますが、insertは非効率的に実装され、複数の再割り当てを行います(特定の実装でテストすることもできます)。
61

おそらく、専用のメソッドを使用する方がより簡単で簡単です: vector.insert

v1.insert(v1.end(), v2.begin(), v2.end());

Michaelが言及しているように、反復子が入力反復子でない限り、ベクターは必要なサイズを計算し、追加されたデータを線形複雑度で一度にコピーします。

22
UncleBens

次のコードを使用して簡単にパフォーマンスを測定し、

v1.insert( v1.end(), v2.begin(), v2.end() );

(すでに述べたように)正しい選択のようです。それでも、報告されたパフォーマンスは以下のとおりです。

テストコード:

#include <vector>
#include <string>

#include <boost/timer/timer.hpp>

//==============================================================================
// 
//==============================================================================

/// Returns a vector containing the sequence [ 0, ... , n-1 ].
inline std::vector<int> _range(const int n)
{
    std::vector<int> tmp(n);
    for(int i=0; i<n; i++)
        tmp[i] = i;
    return tmp;
}

void test_perf_vector_append()
{
    const vector<int> testdata1 = _range(100000000);
    const vector<int> testdata2 = _range(100000000);

    vector<int> testdata;

    printf("--------------------------------------------------------------\n");
    printf(" METHOD:  Push_back()\n");
    printf("--------------------------------------------------------------\n");
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;
        for(size_t i=0; i<testdata2.size(); i++)
        {
            testdata.Push_back(testdata2[i]);
        }
    }

    printf("--------------------------------------------------------------\n");
    printf(" METHOD:  reserve() + Push_back()\n");
    printf("--------------------------------------------------------------\n");
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;
        testdata.reserve(testdata.size() + testdata2.size());
        for(size_t i=0; i<testdata2.size(); i++)
        {
            testdata.Push_back(testdata2[i]);
        }
    }

    printf("--------------------------------------------------------------\n");
    printf(" METHOD:  insert()\n");
    printf("--------------------------------------------------------------\n");
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;

        testdata.insert( testdata.end(), testdata2.begin(), testdata2.end() );
    }

    printf("--------------------------------------------------------------\n");
    printf(" METHOD:  reserve() + insert()\n");
    printf("--------------------------------------------------------------\n");
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;

        testdata.reserve( testdata.size() + testdata.size() ); 
        testdata.insert( testdata.end(), testdata2.begin(), testdata2.end() );
    }

    printf("--------------------------------------------------------------\n");
    printf(" METHOD:  copy() + back_inserter()\n");
    printf("--------------------------------------------------------------\n");
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;

        testdata.reserve(testdata.size() + testdata2.size()); 
        copy(testdata2.begin(), testdata2.end(), back_inserter(testdata));
    }

    printf("--------------------------------------------------------------\n");
    printf(" METHOD:  reserve() + copy() + back_inserter()\n");
    printf("--------------------------------------------------------------\n");
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;

        testdata.reserve(testdata.size() + testdata2.size()); 
        copy(testdata2.begin(), testdata2.end(), back_inserter(testdata));
    }

}

Visual Studio 2008 SP1、x64、リリースモード、/ O2/LTCGでは、出力は次のようになります。

--------------------------------------------------------------
 METHOD:  Push_back()
--------------------------------------------------------------
 0.933077s wall, 0.577204s user + 0.343202s system = 0.920406s CPU (98.6%)

--------------------------------------------------------------
 METHOD:  reserve() + Push_back()
--------------------------------------------------------------
 0.612753s wall, 0.452403s user + 0.171601s system = 0.624004s CPU (101.8%)

--------------------------------------------------------------
 METHOD:  insert()
--------------------------------------------------------------
 0.424065s wall, 0.280802s user + 0.140401s system = 0.421203s CPU (99.3%)

--------------------------------------------------------------
 METHOD:  reserve() + insert()
--------------------------------------------------------------
 0.637081s wall, 0.421203s user + 0.218401s system = 0.639604s CPU (100.4%)

--------------------------------------------------------------
 METHOD:  copy() + back_inserter()
--------------------------------------------------------------
 0.743658s wall, 0.639604s user + 0.109201s system = 0.748805s CPU (100.7%)

--------------------------------------------------------------
 METHOD:  reserve() + copy() + back_inserter()
--------------------------------------------------------------
 0.748560s wall, 0.624004s user + 0.124801s system = 0.748805s CPU (100.0%)
18
Stefan

Boostを使用する場合は、RangeExライブラリの開発バージョンをダウンロードできます Boost Vaultから 。このライブラリ。しばらく前にBoostに受け入れられましたが、今のところメインディストリビューションと統合されていません。その中に、あなたが望むものを正確に実行する新しい範囲ベースのアルゴリズムがあります:

boost::Push_back(v1, v2);

内部的には、UncleBensの答えのように機能しますが、コードはより簡潔で読みやすいです。

7
Manuel

ポッドタイプのベクターがあり、本当にパフォーマンスが必要な場合は、memcpyを使用できます。これはvector <>。insert(...)よりも高速である必要があります。

v2.resize(v1.size() + v2.size());
memcpy((void*)&v1.front(), (void*)&v2[v1.size()], sizeof(v1.front())*v1.size());

更新:パフォーマンスが本当に必要な場合にのみこれを使用しますが、reallyが必要です。コードisはポッドタイプに対して安全です。

3
Viktor Sehr