La forma más rápida de restablecer todos los valores de std :: vector <int> a 0

198

¿Cuál es la forma más rápida de restablecer todos los valores de a std::vector<int>a 0 y mantener el tamaño inicial de los vectores?

¿Un bucle for con el operador []?

Matthieu Riegler
fuente
44
std :: fill
Andriy Tylychko
1
¿"Más rápido" como en rendimiento? ¿O como en la forma más fácil de implementar / mantener?
TheGeneral

Respuestas:

341
std::fill(v.begin(), v.end(), 0);
Cat Plus Plus
fuente
48
Al observar la salida del ensamblaje, gcc en realidad desenrolla este bucle para usar los registros mmx para volcar en 16 bytes a la vez hasta que se acerca al final. Yo diría que es bastante rápido. La versión de memset salta a memset, que supongo que es casi tan rápida. Yo usaría tu método.
Omnifarioso
Pero, saltar a memset es una sola instrucción, por lo que su uso dará como resultado un tamaño binario más pequeño.
Alexander Shishenko
2
esto no es exactamente lo que solicitó OP, pero simplemente reasignar su vector a uno nuevo del mismo tamaño ( v = std::vector<int>(vec_size,0)) parece un poco más rápido que fillen mi máquina
Yibo Yang
1
Esta es la forma más idiomática de hacerlo, más idiomática que usarla assign.
alfC
1
¿Asignarlo a un nuevo vector hace la asignación del montón? y luego descartar la asignación del vector existente? Pude ver que es más lento que memset et al.
Conrad Jones
150

Como siempre cuando preguntas sobre el más rápido: ¡Mide! Usando los métodos anteriores (en una Mac usando 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    |

usando 100000 iteraciones en un vector de 10000 ints.

Editar: si el cambio de estos números cambia de manera plausible los tiempos resultantes, puede tener cierta confianza (no tan buena como la inspección del código de ensamblaje final) de que el punto de referencia artificial no se ha optimizado por completo. Por supuesto, lo mejor es ensuciar el rendimiento en condiciones reales. fin Editar

para referencia el código usado:

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

Conclusión: ¡ uso std::fill(porque, como otros han dicho, es lo más idiomático)!

Fabio Fracassi
fuente
3
+1. Este punto de referencia en particular no es concluyente, pero el punto es absolutamente correcto, debe escribir una prueba de rendimiento de las alternativas, ya que realmente se utilizarán. Si no hay una diferencia de rendimiento, utilice la fuente más simple.
Steve Jessop el
3
"... no es concluyente ..." OMI, esta falta de conclusión en sí misma ya es un buen punto para hacer puntos de referencia, la mayoría de las veces el Optimizer ya hace un muy buen trabajo para el tipo de situaciones sobre las que el OP preguntó. Y modificaría su última oración para leer "Si no hay una diferencia de rendimiento significativa ..."
Fabio Fracassi
44
ACTUALIZACIÓN Usando Nonius para los puntos de referencia: clang3.6-libc ++ - c ++ 1y-O3 , gcc4.9-c ++ 1y-O3 y gcc5-c ++ 1y-O3 - TL; DR : assignes más lento, excepto para pequeñas capacidades en libc++. CÓDIGO coliru / paste
sehe
2
Además, wow, si le importa la velocidad sin optimizaciones (lo que podría ser plausible si está implementando en modo 'depuración', lo que hacen algunos equipos), se fillve terrible. Es dos órdenes de magnitud más lento en esta prueba.
Kyle Strand
55
@KyleStrand: No es que el relleno sea terrible, es una plantilla y el código se genera con -O0 dentro de su unidad de traducción. Cuando usa memset, está usando el código libc que se compiló con -O3 (incluso cuando compila su código con -O0). Si le importa la velocidad en la depuración y usa plantillas, tendrá que usar una instanciación de plantilla explícita en un archivo separado que compile con -O3
Tic
25

¿Qué hay de la assignfunción miembro?

some_vector.assign(some_vector.size(), 0);
flujo libre
fuente
2
El OP quería restablecer los valores existentes, pero su respuesta es mejor cuando desea cambiar el tamaño y restablecer los valores. ¡Gracias!
15

Si es solo un vector de enteros, primero probaría:

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

No es muy C ++, así que estoy seguro de que alguien proporcionará la forma adecuada de hacerlo. :)

relajarse
fuente
3
Dado que el estándar (2003 TC1) garantiza que un vector std :: es contiguo en la memoria, esto debería estar bien. Si su biblioteca de c ++ no se ajusta al TC1 2003, entonces no use esto.
Mario
2
@ Mario: No habría publicado esto a menos que fuera cierto y se supusiera que era conocido, por supuesto. :) Pero gracias.
Descansa el
1
Revisé la asamblea. El ::std::fillmétodo se expande a algo que es bastante rápido, aunque un poco en el lado del código, ya que todo está en línea. Sin embargo, todavía lo usaría porque es mucho más agradable de leer.
Omnifarioso
44
Será mejor que agregue verificar si el vector está vacío y no hacer nada en este caso. Calcular & buf [0] para un vector vacío puede generar aserciones en el código STL.
Sergey
4

tratar

std::fill

y también

std::size siz = vec.size();
//no memory allocating
vec.resize(0);
vec.resize(siz, 0);
nttstar
fuente
cambiar el tamaño es muy agradable
Nick
3

Tenía la misma pregunta pero bastante corta vector<bool>(afaik el estándar permite implementarlo internamente de manera diferente que solo una matriz continua de elementos booleanos). Por lo tanto, repetí las pruebas ligeramente modificadas de Fabio Fracassi. Los resultados son los siguientes (tiempos, en segundos):

            -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

Entonces, aparentemente para estos tamaños, vector<bool>::assign()es más rápido. El código utilizado para las pruebas:

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

Usé el compilador GCC 7.2.0 en Ubuntu 17.10. La línea de comando para compilar:

g++ -std=c++11 -O0 main.cpp
g++ -std=c++11 -O3 main.cpp
Yauhen Yakimenka
fuente