2008-10-19 7 views
6

En la programación que se enfrentan a diversas situaciones en las que estamos obligados a hacer uso de contenedores STL intermedios como el siguiente ejemplo representa:C++ STL: ¿Reconstrucción o reutilización del contenedor después de la limpieza?

while(true) 
{ 
    set <int> tempSet; 

    for (int i = 0; i < n; i ++) 
    { 
     if (m.size() == min && m.size() <= max) 
     { 
      tempSet.insert(i); 
     } 
    } 
    //Some condition testing code 
} 

O

set <int> tempSet; 

while(true) 
{ 
    for (int i = 0; i < n; i ++) 
    { 
     if (m.size() == min && m.size() <= max) 
     { 
      tempSet.insert(i); 
     } 
    } 
    tempSet.clear(); 

    //Some condition testing code 
} 

¿Qué método es mejor en términos de tiempo y espacio complejidad teniendo en cuenta el estado actual de los cumplidores de C++?

Respuesta

2

El segundo puede ser un poco mejor en cuanto al tiempo, pero la diferencia será extremadamente mínima: el código aún tiene que pasar y soltar cada uno de los elementos del conjunto, independientemente de si se está volviendo a crear o eliminar eso.

(espacio-sabia ellos sean idénticas.)

0

Como regla general, no es un coste de asignación de memoria asociada con la carga de datos en un recipiente. Es mucho mejor evitar pagar ese costo muchas veces, reutilizando un contenedor. Si realmente sabe cuántos elementos, o puede adivinarlo bien, debe preasignar espacio en el contenedor de la parte delantera.

Es especialmente notable si está insertando objetos en el contenedor, porque puede terminar pagando muchos costos adicionales de construcción/destrucción, ya que el contenedor se da cuenta de que es demasiado pequeño, reasigna la memoria y copia construcciones nuevas en la nueva memoria basada en los objetos existentes.

Siempre preasignación y reutilización. Ahorra tiempo y memoria.

+0

No creo que se puede asignar previamente el espacio en un conjunto. –

+0

Sí, veo eso. Probablemente tendrías que hacer algo tonto con el asignador para obtener una preasignación decente. – EvilTeach

+0

Ejecuté una prueba. el max_size es enorme. No es un problema en este caso. – EvilTeach

4

Si esta es una cuestión de set/map/list, no hará ninguna diferencia. Si se trata de vector/hash_set/hash_map/string, el segundo será más rápido o la misma velocidad. Por supuesto, esta diferencia de velocidad solo se notará si está realizando una gran cantidad de operaciones (10.000 pulgadas empujadas a vector 10.000 veces fue aproximadamente el doble de rápido: 3 segundos y algunos cambios en comparación con 7 segundos).

Si está haciendo algo así como almacenar un struct/class en su estructura de datos en lugar de un puntero a uno, esta diferencia será aún mayor porque en cada cambio de tamaño, debe copiar todos los elementos.

Nuevamente, en casi todos los casos, no importará, y en los casos en los que sí importa, se mostrará si se está optimizando y se preocupa por el rendimiento.

+0

Esta pregunta es sobre conjuntos, no vectores. También para los vectores, debato sobre el hecho de notar la diferencia en la velocidad: se supone que los vectores crecen exponencialmente (generalmente se duplican cada vez), por lo que el número de asignaciones se amortiza. –

+0

La pregunta no es específica para ninguno de los dos. Si se toma el tiempo para ejecutar realmente algunas pruebas de velocidad para el caso en el que sí importa, notará un gran golpe de rendimiento. No es la asignación lo que duele, es la memcpy después del realloc. – hazzen

+0

@ ChrisJester-Young: Sí, el número de asignaciones se amortiza, pero es mejor asignar 'log (n)' que 'Mlog (n)'. –

7

Digo que el primero es más a prueba de errores. No va a tener que acordarse de despejarlo (se saldrá del alcance y terminará). :-)

En cuanto al rendimiento, no hay mucha diferencia, pero no obstante, debe medir ambos casos y comprobarlo usted mismo.

+1

Haga un seguimiento del comentario a prueba de errores del OP: imagine lo que sucederá al agregar una instrucción continue en algún lugar del ciclo while. – dalle

+0

Sí, ¡totalmente de acuerdo! :-) –

0

Hay muy poca diferencia, aunque la segunda evita llamar al constructor y al destructor varias veces. Por otro lado, el primero es una línea más corta y garantiza que el contenedor no es visible fuera del alcance del ciclo.

15

La primera versión es correcta. Es más simple en casi todos los sentidos. Más fácil de escribir, fácil de leer, fácil de entender, fácil de mantener, etc ....

La segunda versión puede ser más rápido, pero de nuevo, no puede. Tendría que demostrar que tenía una ventaja significativa antes de usarlo.En la mayoría de los casos no triviales, supongo que no habría una diferencia de rendimiento mensurable entre los dos.

A veces en la programación incrustada es útil evitar poner cosas en la pila; en ese caso, la segunda versión sería correcta.

Utilice la primera versión por defecto; use el segundo solo si puede dar una buena razón para hacerlo (si el motivo es el rendimiento, entonces debe tener evidencia de que el beneficio es significativo).

+1

Acepto: no haga visibles las variables donde no sean necesarias, a menos que tenga una buena razón. – xtofl

1

Habrá muy poca diferencia de rendimiento entre los dos. Debe tomar su decisión en función de la legibilidad y legibilidad del código.

creo que el primer ejemplo es más fácil de leer y más seguro - lo que sucede dentro de seis meses cuando se agrega una condición al bucle, tal vez con un continuar en alguna parte - en el segundo ejemplo, se pasará por alto la clara() y tendrás un error.

1

Usted está poniendo su conjunto en la pila, por lo que el costo de asignación es casi nulo.

+0

El costo de asignación para el objeto conjunto en sí mismo es cercano a cero, pero los elementos dentro de él todavía se asignan del montón. –

+1

@Head Geek: la construcción/destrucción de los objetos dentro del conjunto es la misma en cualquier ejemplo; la única diferencia es la construcción/destrucción del objeto establecido. Entonces el punto de Ypnos es válido. –

+0

@Mike B: Lo siento, pero no veo tu punto. Mientras lo leo, ypnos está hablando sobre el costo de asignación, el tiempo que se tarda en asignar memoria para el objeto, por lo que mi objeción sigue en pie. –

-1

Creo que puede preasignar una cierta cantidad de elementos para los contenedores de STL, teniendo así un costo de asignación de memoria constante si sabe cuántos elementos habrá en el contenedor.

+1

std :: set no tiene tal funcionalidad.Tal vez estás pensando en std :: vector :: reserve? –

+0

@ Don: sí, lo era. –

0

El conjunto generalmente se implementa como un árbol negro rojo. Cada nodo se asigna por separado para que el conjunto no se beneficie de la reutilización. Es por eso que ambos enfoques tienen casi el mismo rendimiento. Llamar a "tempSet.clear()" debería tomar aproximadamente el mismo tiempo que destruir el objeto. El primer enfoque es mejor porque tiene el mismo rendimiento y sigue un estilo de programación más seguro.

se puede encontrar una discusión general sobre otros recipientes aquí: Reusing a container o creating a new one

Cuestiones relacionadas