260

What's the fastest way to reset every value of a std::vector<int> to 0 and keeping the vectors initial size ?

A for loop with the [] operator ?

Etienne de Martel
  • 34,692
  • 8
  • 91
  • 111
Matthieu Riegler
  • 31,918
  • 20
  • 95
  • 134

7 Answers7

450
std::fill(v.begin(), v.end(), 0);
Qix - MONICA WAS MISTREATED
  • 14,451
  • 16
  • 82
  • 145
Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
  • 64
    Looking at the assembly output, gcc actually unrolls this loop into using the mmx registers to dump in 16 bytes at a time until it gets close to the end. I'd say that's pretty fast. The memset version jumps to memset, which I'm guessing is about as fast. I'd use your method. – Omnifarious Jan 13 '12 at 10:04
  • But, jumping to memset is a single instruction, so using it will result in a smaller binary size. – Alexander Shishenko Oct 12 '16 at 20:39
  • 2
    this is not exactly what OP asked for, but simply reassigning your vector to a new one of the same size (`v = std::vector(vec_size,0)`) seems slightly faster than `fill` on my machine – Yibo Yang Jun 16 '17 at 04:09
  • 1
    This is the most idiomatic way of doing it, more idiomatic than using `assign`. – alfC Aug 01 '17 at 05:02
  • 1
    does assigning it to a new vector do heap allocation ? and then discard the allocation of the existing vector ? I could see that being slower than memset et al – Conrad Jones Jan 12 '18 at 19:40
  • Here it is the library and the compiler converting the `std::fill(..., 0)` into `memset`: https://godbolt.org/z/DcbH7d – alfC May 13 '20 at 03:18
  • std::fill(v.begin(), v.end(),(dataType) 0); std::fill and std::accumulate do not seem to work for me without type casting on GCC 7.5 – nilesh Dec 06 '20 at 13:09
196

As always when you ask about fastest: Measure! Using the Methods above (on a Mac using Clang):

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    |

using 100000 iterations on an vector of 10000 ints.

Edit: If changeing this numbers plausibly changes the resulting times you can have some confidence (not as good as inspecting the final assembly code) that the artificial benchmark has not been optimized away entirely. Of course it is best to messure the performance under real conditions. end Edit

for reference the used code:

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

Conclusion: use std::fill (because, as others have said its most idiomatic)!

Fabio Fracassi
  • 3,791
  • 1
  • 18
  • 17
  • 3
    +1. This particular benchmark isn't conclusive, but the point is absolutely correct, you should write a performance test of the alternatives as they will actually be used. If there's no performance difference then use whichever is the simplest source. – Steve Jessop Jan 13 '12 at 12:14
  • 3
    "... not conclusive ..." IMO this inconclusiveness in itself is already a good point for doing benchmarks, more often than not the Optimizer already does a very good job for the kind of situations the OP asked about. And I'd modify your last sentence to read "If there's no **significant** performance difference ..." – Fabio Fracassi Jan 13 '12 at 15:15
  • 1
    By "not conclusive" I meant that just because they were all the same speed in this program doesn't necessarily mean they'll all be the same speed in the questioner's program. Aside from anything else, you'd need to be certain that the memory was actually zeroed - it could be the optimizer was smart enough to cheat the test. But since you don't have the questioner's program, that's not a failing of this answer :-) And you're absolutely right, it's very easy to spend time agonizing over a choice that actually makes no difference at all (or an insignificant difference) once optimized. – Steve Jessop Jan 13 '12 at 17:16
  • +1: edited the answer to address your very relevant observations. – Fabio Fracassi Jan 13 '12 at 17:55
  • where's `vector.assign`, which exists expressly for this purpose? – Mooing Duck Jan 13 '12 at 17:58
  • 4
    **UPDATE** Using [Nonius](https://github.com/rmartinho/nonius) for benchmarks: [clang3.6-libc++-c++1y-O3](http://tinyurl.com/qgvdmfe), [gcc4.9-c++1y-O3](http://tinyurl.com/ps2u9kn) and [gcc5-c++1y-O3](http://tinyurl.com/naud8xl) - **TL;DR**: `assign` is slower, except for small capacities on `libc++`. CODE [coliru](http://tinyurl.com/pjsonf3)/[paste](http://tinyurl.com/npzel7t) – sehe Oct 08 '15 at 22:24
  • What about the new "for-each" syntax? `for (auto& elem : v) elem = 0;` My understanding is that compilers can take extreme liberties in optimizing these sorts of loops, it's still pretty idiomatic, and it doesn't rely on `std::fill` (in fact since it can be implemented in a template for *any* writeable iterable class, it doesn't even rely on the standard library). – Kyle Strand Nov 14 '15 at 20:17
  • 2
    Also, wow, if you care about speed without optimizations (which might be plausible if you are deploying in 'debug' mode, which some teams do), `fill` looks terrible. It is *two orders of magnitude* slower in this test. – Kyle Strand Nov 14 '15 at 20:20
  • `assign` seems to me more meaningful API and there's no reason it should be slower than `fill`, so I will consider that a problem in the implementation. And also it's faster than `fill` on debug so I prefer it anyway. – ceztko Jul 14 '17 at 10:36
  • @ceztko, maybe `.assign` is slower because it contemplates (through conditionals) whether to resize the vector. In that sense `std::fill` is more idiomatic because it doesn't take an argument with the size. There should be an `.assign(Value)` function. – alfC Jul 22 '17 at 00:03
  • @alfC: a bound check can't cost almost 0.5 secs compared to the other methods, and of course the non resize case should be favoured in the implementation. – ceztko Jul 22 '17 at 00:10
  • 1
    @ceztko, in the gcc implementation of STL, `assign` consists in two conditionals (comparing to `size` and `capacity`) and if the case is favorable, it calls `std::fill_n` and and internal `erase_at_end` (which surely entails an additional conditional). – alfC Jul 27 '17 at 07:08
  • 5
    @KyleStrand: It's not that fill is terrible, it is a template and the code is generated with -O0 inside your translation unit. When you use memset, you are using the libc code which was compiled with -O3 (even when you compile your code with -O0). If you care about speed in debug and you use templates, you will have to use explicit template instantiation in a separate file which you compile with -O3 – Tic Feb 23 '18 at 13:33
  • @Tic That's an extremely helpful insight. Thanks. – Kyle Strand Feb 23 '18 at 15:29
29

How about the assign member function?

some_vector.assign(some_vector.size(), 0);
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 4
    The OP wanted to reset existing values, but your answer is better when wanting to resize _and_ reset the values. Thanks! –  Oct 06 '17 at 23:12
17

If it's just a vector of integers, I'd first try:

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

It's not very C++, so I'm sure someone will provide the proper way of doing this. :)

unwind
  • 391,730
  • 64
  • 469
  • 606
  • 3
    Since the standard (2003 TC1) guarantees that a std::vector is contiguous in memory, this should be fine. If your c++ library does not conform to the 2003 TC1, then don't use this. – Mario Jan 13 '12 at 09:54
  • 2
    @Mario: I wouldn't have posted this unless that was true and assumed to be well-known, of course. :) But thanks. – unwind Jan 13 '12 at 09:57
  • 1
    I checked the assembly. The `::std::fill` method expands to something that's pretty darned fast, though a bit on the code-bloaty side since it's all inline. I'd still use it though because it's much nicer to read. – Omnifarious Jan 13 '12 at 10:05
  • 4
    You'd better to add check if vector is empty and do nothing in this case. Calculating &buf[0] for empty vector can generate assertions in STL code. – Sergey Nov 07 '13 at 22:28
8

I had the same question but about rather short vector<bool> (afaik the standard allows to implement it internally differently than just a continuous array of boolean elements). Hence I repeated the slightly modified tests by Fabio Fracassi. The results are as follows (times, in seconds):

            -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

So apparently for these sizes, vector<bool>::assign() is faster. The code used for tests:

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

I used GCC 7.2.0 compiler on Ubuntu 17.10. The command line for compiling:

g++ -std=c++11 -O0 main.cpp
g++ -std=c++11 -O3 main.cpp
7

try

std::fill

and also

std::size siz = vec.size();
//no memory allocating
vec.resize(0);
vec.resize(siz, 0);
nttstar
  • 341
  • 2
  • 10
0

Have a vector of zeroes ready, then switch it with current vector when you need zeroes:

std::vector<int> zeroes(N,0);
std::vector<int> currentVec(N);
...
currentVec.swap(zeroes);

this effectively zeroes the currentVec in O(1) complexity. But until next time you need zeroing, you have to fill the other (zeroes) with zeroes, asynchronously. You can use a dedicated thread for this. Then during swapping, just an overhead of "mutex" will be there.

std::lock_guard<std::mutex> lg(mut);
currentVec.swap(zeroes);

while in another thread:

std::lock_guard<std::mutex> lg(mut);
std::fill(zeroes.begin(),zeroes.end(),0);
huseyin tugrul buyukisik
  • 11,469
  • 4
  • 45
  • 97