2010-04-05 21 views
7

A fin de dar funciones la opción de modificar el vector no puedo hacerC++ Vector en/[] velocidad del operador

curr = myvec.at(i); 
doThis(curr); 
doThat(curr); 
doStuffWith(curr); 

pero tengo que hacer:

doThis(myvec.at(i)); 
doThat(myvec.at(i)); 
doStuffWith(myvec.at(i)); 

(como el respuestas de mi otra pregunta señalaron)

  • voy a hacer un montón de llamadas al infierno myvec.at() continuación. ¿Qué tan rápido es, en comparación con el primer ejemplo que usa una variable para almacenar el resultado?

  • ¿Existe una opción diferente para mí? ¿De alguna manera puedo usar punteros?

Cuando se pone serio habrá miles de llamadas a myvec.at() por segundo. Entonces, cada pequeño comedor de rendimiento es importante.

+1

Además de las consideraciones de rendimiento, pregúntese qué está ganando usando 'at()' en lugar de '[]'. Bounds comprobar en los índices es bueno para la depuración, pero '[]' * también * hace esto cuando su compilación en modo de depuración, y utilizando 'at()' en lugar de '[]' no es un sustituto para escribir código correcto, y es redundante si el código ya es correcto –

+9

@ Unicornio de nariz roja: no todos los compiladores verifican '[]' en el modo de depuración. –

+0

@Brian: desafortunadamente, eso es cierto. ¿Y sabes qué? Eso ** apesta **. Pero, afortunadamente, esto puede solucionarse instalando una versión alternativa de la biblioteca estándar, por ejemplo, STLport: http://www.stlport.org/. –

Respuesta

17

Usted puede utilizar una referencia:

int &curr = myvec.at(i); 
// do stuff with curr 

La función miembro at hace una comprobación de límites para asegurarse de que el argumento está dentro del tamaño de la vector. El perfilado es solo una forma de saber exactamente cuánto más lento es en comparación con operator[]. El uso de una referencia aquí le permite hacer la búsqueda una vez y luego usar el resultado en otros lugares. Y puede convertirlo en una referencia-a-const si desea protegerse de un cambio accidental del valor.

+1

Depende de qué significa "dar a las funciones la opción de modificar el vector". Algunas modificaciones vectoriales invalidan referencias, cualquier cosa que cause una reasignación. –

+2

@Steve, ese es un buen punto. Supongo que OP está modificando * valores * dentro del vector en función del código de muestra dado. Si las funciones pueden modificar el vector en sí, necesitaremos un enfoque diferente. –

2

El operador [] podría ser más rápido que at, porque no es necesario para hacer la comprobación de límites.

Puede hacer curr una referencia para hacer lo que quiera.

MyClass & curr = myvec.at(i); 

También puede hacer algunas pruebas comparativas antes de preocuparse. Los procesadores modernos pueden manejar miles de operaciones por segundo con bastante facilidad.

2

Cuando el rendimiento es un problema, no hay sustituto para la creación de perfiles. Las capacidades de optimización de los compiladores cambian de una versión a otra, y las pequeñas e insignificantes modificaciones en el código fuente pueden cambiar drásticamente el rendimiento resultante.

Nadie puede responder esta pregunta, sino usted mismo: cree un arnés de prueba y lance varios algoritmos y vea lo que obtiene.

ps. si el rendimiento realmente es un problema, bueno, obtuve un aumento del factor 10 de velocidad de un decodificador png eliminando los vectores y reemplazándolos con matrices en bruto. De nuevo, esto fue para Visual Studio 6. No estoy afirmando que una sustitución de matriz en bruto le dará una mejora de factor 10, pero es algo que debe intentar.

+0

No sé acerca de VS6 (afortunadamente pude usar gcc en ese momento), pero un aumento de velocidad de diez veces parece una gran diferencia. ¿Estabas usando los mismos algoritmos en ambos casos? ¿Se insinuó el tamaño del vector en la implementación? La implementación actual de un vector en g ++ contiene tres punteros, para 'begin()', 'end()' y 'begin() + capacity', a menos que active iteradores marcados, las operaciones en un vector son tan rápidas como las operaciones en datos sin procesar . –

+1

El algoritmo fue el mismo. simplemente cambiamos vectores de bytes en matrices en bruto de bytes. las funciones se cambiaron para tomar punteros, y donde los enteros necesarios eran del tamaño de la matriz. Algunos bucles se rodaron en memcpy. –

0

La complejidad de at() es constante, es decir, en la práctica esto significa que debe diseñarse para que no tenga ninguna penalización de rendimiento relevante.

Puede usar [], que también es una complejidad constante, pero no verifica los límites. Esto sería equivalente a usar la aritmética del puntero y, por lo tanto, potencialmente un bit más rápido que el anterior.

En cualquier caso, el vector está diseñado específicamente para el acceso de rendimiento constante a cualquiera de sus elementos. Entonces esta debería ser la menor de tus preocupaciones.

2

El motivo por el que el primero no funciona es que no está estableciendo un puntero o un iterador en la dirección de la i-ésima variable. En su lugar, está configurando curr igual al valor de la i-ésima variable y luego modificando curr. Estoy asumiendo que doThis y doThat son referencias.

hacer esto:

MyObject& curr = myvec.at(i); 
1

opciones que veo, en más o menos orden inverso de preferencia:

  1. almacenan punteros en su contenedor en lugar de los objetos reales. Esto puede ser aconsejable de todos modos, si los objetos son lo suficientemente complejos que copiarlos es problemático.
  2. Utilice el operador de indexación [] en lugar de at().
  3. Simplemente llame al at() una vez y guárdelo en una referencia (consulte la respuesta de Kristo anterior).
  4. Olvídalo hasta que tengas un problema con el tiempo de ejecución excesivo. Si eso sucede, perfile su código primero para asegurarse de que el cuello de botella esté aquí, y solo entonces preocúpese de hacer uno de los anteriores para acelerar las cosas.

Honestamente, lo que debe hacer es jugar con los cuatro enfoques diferentes, y simplemente use el que produce el código más fácil de entender. En la mayoría de los casos, nos complace sacrificar algunos ciclos de máquina por código que es más fácil de mantener para los seres humanos.

4

De mis propias pruebas con código similar (compilado bajo gcc y Linux), operator[] puede ser notablemente más rápido que at, no debido a la comprobación de límites, sino a la sobrecarga de la gestión de excepciones. Reemplazando at (que arroja una excepción fuera de límites) con mi propia verificación de límites que planteó un assert fuera de los límites dio una mejora mensurable.

Al usar una referencia, como Kristo said, solo puede incurrir en los límites de comprobación de sobrecarga una vez.

Ignorando la comprobación de límites y la gestión de excepciones, tanto operator[] como at se deben optimizar para que sean equivalentes al acceso directo a la matriz o al acceso directo a través del puntero.

Como dijo Chris Becke, sin embargo, no hay sustituto para la creación de perfiles.

+0

En realidad, disparar excepciones es generalmente lento, su control no es necesariamente demasiado lento. –

+0

@Martin: Depende mucho de la implementación. En algunos casos, es el desenrollado de la pila lo que es caro, no el "disparador" (lanzamiento) – MSalters

+0

@MSalters - sí, pero la prueba no es necesariamente costosa si la excepción no lo es. –

0

Los vectores son los más adecuados para la velocidad de acceso. El acceso a un elemento aleatorio en un vector es de complejidad O (1) en comparación con O (n) para listas enlazadas generales y O (log n) para árboles de enlace.

Sin embargo, esta pregunta no se realiza correctamente ya que la respuesta a su otra pregunta lo ha desviado al no explicar cómo solucionar el problema original mediante una referencia.

0

Si es el caso, cargue un vector, luego procese sin agregar o eliminando más elementos, luego considere obtener un puntero al arreglo subyacente, y usando operaciones de matriz sobre eso para 'evitar el sobrecarga del vector '.

Si está agregando o eliminando elementos como parte de su procesamiento, no es seguro hacerlo, ya que el vector puede mover la matriz subyacente en cualquier punto.