2012-01-13 18 views

Respuesta

218
std::fill(v.begin(), v.end(), 0); 
+30

Al mirar la salida del conjunto, gcc realmente 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 memset salta a memset, que supongo que es tan rápido. Usaría tu método. – Omnifarious

+0

Pero, saltar a memset es una instrucción única, por lo que usarla dará como resultado un tamaño binario más pequeño. –

+0

esto no es exactamente lo que OP solicitó, pero simplemente reasignar su vector a uno nuevo del mismo tamaño ('v = std :: vector (vec_size, 0)') parece un poco más rápido que 'fill' en mi máquina –

12

Si es sólo un vector de enteros, me gustaría primer intento:

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

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

+2

Dado que el estándar (2003 TC1) garantiza que un std :: vector es contiguo en la memoria, esto debería estar bien. Si su biblioteca C++ no se ajusta al TC1 2003, entonces no la use. – Mario

+2

@Mario: No habría publicado esto a menos que eso fuera cierto y se supusiera que era conocido, por supuesto. :) Pero gracias. – unwind

+1

Revisé el ensamblaje. El método ':: std :: fill' se expande a algo que es bastante rápido, aunque un poco flojo, ya que todo está en línea. Aún así lo usaría porque es mucho más agradable de leer. – Omnifarious

2

tratar

std::fill 

y también

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

cambiar el tamaño es muy bueno – Nick

109

Como siempre cuando se pregunta acerca más rápido: Medida! Utilizando los métodos anteriores (en un 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 changeing estos números plausiblemente cambia los tiempos resultantes se puede tener alguna de confianza (no tan bueno como inspeccionar el código de montaje final) que el punto de referencia artificial no ha sido optimizado por completo. Por supuesto, es mejor medir el rendimiento en condiciones reales. final Editar

de remisión, el código utilizado:

#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 su más idiomática)!

+2

+1. Este punto de referencia particular no es concluyente, pero el punto es absolutamente correcto, debe escribir una prueba de rendimiento de las alternativas, ya que en realidad se utilizarán. Si no hay diferencia de rendimiento, utilice la que sea la fuente más simple. –

+1

"... no concluyente ..." IMO esta inconclusió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 OP preguntó. Y modificaría su última oración para que diga "Si no hay una ** diferencia de rendimiento ** significativa ..." –

+1

Por "no concluyente" quise decir que solo porque tenían la misma velocidad en este programa no necesariamente significa todos tendrán la misma velocidad en el programa del interrogador. Aparte de cualquier otra cosa, necesitaría estar seguro de que la memoria realmente estaba puesta a cero; podría ser que el optimizador fuera lo suficientemente inteligente como para engañar a la prueba. Pero como no tienes el programa de pregunta, eso no es un fracaso de esta respuesta :-) Y tienes toda la razón, es muy fácil pasar el tiempo agonizando por una elección que en realidad no hace ninguna diferencia (o una diferencia insignificante)) una vez optimizado. –

18

¿Qué hay de la función miembro assign?

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

El OP desea restablecer los valores existentes, pero su respuesta es mejor cuando quiere redimensionar _y_ restablecer los valores. ¡Gracias! –

0

que tenían la misma pregunta, pero más bien corto sobre vector<bool> (que yo sepa el estándar permite implementar internamente de manera diferente que una serie continua de elementos booleanos). Por lo tanto, repetí las pruebas ligeramente modificadas por Fabio Fracassi. Los resultados son los siguientes (los 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 

Así que al parecer 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; 
} 

que utiliza GCC 7.2.0 compilador en Ubuntu 17.10. La línea de comandos para compilar:

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