std :: vector <int>のすべての値を0にリセットする最速の方法


回答:


340
std::fill(v.begin(), v.end(), 0);

48
アセンブリの出力を見ると、gccは実際にこのループを展開し、mmxレジスターを使用して、最後に近づくまで一度に16バイトずつダンプします。それはかなり速いと思います。memsetバージョンはmemsetにジャンプしますが、これはほぼ同じくらい速いと思います。私はあなたの方法を使います。
方位、2013年

ただし、me​​msetへのジャンプは単一の命令であるため、これを使用するとバイナリサイズが小さくなります。
Alexander Shishenko

2
これは正確にはOPが要求したものではありませんが、ベクターを同じサイズ(v = std::vector<int>(vec_size,0))の新しいベクターに再割り当てするだけfillで、私のマシンよりも少し高速に見えます
Yibo Yang

1
これは最も慣用的な方法であり、を使用するよりも慣用的ですassign
alfC 2017

1
それを新しいベクトルに割り当てるとヒープが割り当てられますか?そして、既存のベクトルの割り当てを破棄しますか?memsetなどよりも遅いことがわかりました
コンラッドジョーンズ

150

いつものように最速について尋ねるとき:測定!上記のメソッドの使用(Clangを使用するMacの場合):

Method      |  executable size  |  Time Taken (in sec) |
            |  -O0    |  -O3    |  -O0      |  -O3     |  
------------|---------|---------|-----------|----------|
1. memset   | 17 kB   | 8.6 kB  | 0.125     | 0.124    |
2. fill     | 19 kB   | 8.6 kB  | 13.4      | 0.124    |
3. manual   | 19 kB   | 8.6 kB  | 14.5      | 0.124    |
4. assign   | 24 kB   | 9.0 kB  | 1.9       | 0.591    |

10000整数のベクトルで100000回の反復を使用します。

編集:この数値を変更すると、結果として得られる時間が変更される可能性がある場合、人工的なベンチマークが完全に最適化されていないことをある程度確信できます(最終的なアセンブリコードの検査ほど良くありません)。もちろん、実際の条件下でパフォーマンスを測定することをお勧めします。 編集を終了

使用されたコードの参照用:

#include <vector>

#define TEST_METHOD 1
const size_t TEST_ITERATIONS = 100000;
const size_t TEST_ARRAY_SIZE = 10000;

int main(int argc, char** argv) {

   std::vector<int> v(TEST_ARRAY_SIZE, 0);

   for(size_t i = 0; i < TEST_ITERATIONS; ++i) {
   #if TEST_METHOD == 1 
      memset(&v[0], 0, v.size() * sizeof v[0]);
   #elif TEST_METHOD == 2
      std::fill(v.begin(), v.end(), 0);
   #elif TEST_METHOD == 3
      for (std::vector<int>::iterator it=v.begin(), end=v.end(); it!=end; ++it) {
         *it = 0;
      }
   #elif TEST_METHOD == 4
      v.assign(v.size(),0);
   #endif
   }

   return EXIT_SUCCESS;
}

結論:使用std::fill(他の人が最も慣用的に言っているため)!


3
+1。この特定のベンチマークは決定的なものではありませんが、要点は完全に正しいです。実際に使用されるので、代替案のパフォーマンステストを作成する必要があります。パフォーマンスに違いがない場合は、最も単純なソースを使用します。
スティーブジェソップ2012年

3
「...決定的ではない...」IMOこの決定的でないこと自体は、ベンチマークを実行するための優れたポイントです。オプティマイザーは、OPが要求したような状況に対して、非常に優れた機能を果たしています。「何がない場合は、私は読むためにあなたの最後の文を変更したい重要なパフォーマンスの違いは...」
ファビオFracassi

4
ベンチマークにNoniusを使用したUPDATEclang3.6-libc ++-c ++ 1y-O3gcc4.9-c ++ 1y-O3およびgcc5-c ++ 1y-O3 - TL; DRassign容量が小さい場合を除いて、低速ですlibc++。CODE コリル / 貼り付け
sehe

2
また、すごい、最適化なしの速度を気にかけている場合(一部のチームが行う「デバッグ」モードで展開している場合、これはもっともらしい)、fillひどいように見えます。このテストは、2桁遅くなります。
カイルストランド

5
@KyleStrand:塗りつぶしがひどいというわけではなく、テンプレートであり、コードは翻訳単位内で-O0を使用して生成されます。memsetを使用するときは、-O3でコンパイルされたlibcコードを使用しています(-O0でコードをコンパイルした場合でも)。デバッグの速度を気にし、テンプレートを使用する場合は、-O3でコンパイルする別のファイルで明示的なテンプレートのインスタンス化を使用する必要があります
Tic

25

どの程度assignメンバ関数?

some_vector.assign(some_vector.size(), 0);

2
OPは既存の値をリセットしたかったのですが、値のサイズを変更してリセットしたい場合の方が良いでしょう。ありがとう!

15

それが整数のベクトルである場合、私は最初に試してみます:

memset(&my_vector[0], 0, my_vector.size() * sizeof my_vector[0]);

これはあまりC ++ではないので、誰かがこれを行う適切な方法を提供すると確信しています。:)


3
標準(2003 TC1)では、std :: vectorがメモリ内で連続していることが保証されているため、これで問題ありません。c ++ライブラリが2003 TC1に準拠していない場合は、これを使用しないでください。
マリオ

2
@マリオ:もちろん、それが真実でよく知られていると想定されていない限り、私はこれを投稿しなかったでしょう。:)しかし、ありがとう。
2012年

1
組み立てを確認しました。この::std::fillメソッドは、非常に高速なものに拡張されますが、すべてインラインであるため、コードが膨れ上がっています。読む方がずっといいので、私はまだそれを使います。
12

4
ベクトルが空かどうかのチェックを追加し、この場合は何もしない方がよいでしょう。空のベクトルの&buf [0]を計算すると、STLコードでアサーションが生成される可能性があります。
セルゲイ

4

試す

std::fill

そしてまた

std::size siz = vec.size();
//no memory allocating
vec.resize(0);
vec.resize(siz, 0);

リサイズはとてもいいです
Nick

3

私は同じ質問をしましたが、どちらかと言えば短いですvector<bool>(afaik標準では、ブール要素の連続した配列とは異なる方法で内部的に異なる方法で実装できます)。したがって、私はファビオフラカシによる少し変更されたテストを繰り返しました。結果は次のとおりです(時間、秒単位):

            -O0       -O3
         --------  --------
memset     0.666     1.045
fill      19.357     1.066
iterator  67.368     1.043
assign    17.975     0.530
for i     22.610     1.004

したがって、これらのサイズの場合、明らかにvector<bool>::assign()高速です。テストに使用されるコード:

#include <vector>
#include <cstring>
#include <cstdlib>

#define TEST_METHOD 5
const size_t TEST_ITERATIONS = 34359738;
const size_t TEST_ARRAY_SIZE = 200;

using namespace std;

int main(int argc, char** argv) {

    std::vector<int> v(TEST_ARRAY_SIZE, 0);

    for(size_t i = 0; i < TEST_ITERATIONS; ++i) {
#if TEST_METHOD == 1
        memset(&v[0], false, v.size() * sizeof v[0]);
#elif TEST_METHOD == 2
        std::fill(v.begin(), v.end(), false);
   #elif TEST_METHOD == 3
        for (std::vector<int>::iterator it=v.begin(), end=v.end(); it!=end; ++it) {
            *it = 0;
        }
   #elif TEST_METHOD == 4
      v.assign(v.size(),false);
   #elif TEST_METHOD == 5
      for (size_t i = 0; i < TEST_ARRAY_SIZE; i++) {
          v[i] = false;
      }
#endif
    }

    return EXIT_SUCCESS;
}

Ubuntu 17.10でGCC 7.2.0コンパイラを使用しました。コンパイルのためのコマンドライン:

g++ -std=c++11 -O0 main.cpp
g++ -std=c++11 -O3 main.cpp
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.