c++ performance visual-c++ vector

c++ - ¿Por qué std:: vector:: operator[] 5 a 10 veces más rápido que std:: vector:: at()?



performance visual-c++ (3)

Durante la optimización del programa, tratando de optimizar un ciclo que itera a través de un vector, encontré el siguiente hecho: :: std :: vector :: at () es EXTREMADAMENTE más lento que el operador []!

El operador [] es de 5 a 10 veces más rápido que at () , tanto en versiones de versión y depuración (VS2008 x86).

Leer un poco en la web me hizo darme cuenta de que en () tiene una verificación de límites. Ok, pero, ralentizando la operación hasta 10 veces?

¿Hay alguna razón para eso? Quiero decir, la verificación de límites es una simple comparación numérica, ¿o me falta algo?
La pregunta es: ¿cuál es el verdadero motivo de este golpe de rendimiento?
Además, ¿hay alguna forma de hacerlo aún más rápido ?

Definitivamente voy a intercambiar todas mis llamadas a () con [] en otras partes del código (¡en las que ya tengo una verificación de límite personalizada!).

Prueba de concepto:

#define _WIN32_WINNT 0x0400 #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <conio.h> #include <vector> #define ELEMENTS_IN_VECTOR 1000000 int main() { __int64 freq, start, end, diff_Result; if(!::QueryPerformanceFrequency((LARGE_INTEGER*)&freq)) throw "Not supported!"; freq /= 1000000; // microseconds! ::std::vector<int> vec; vec.reserve(ELEMENTS_IN_VECTOR); for(int i = 0; i < ELEMENTS_IN_VECTOR; i++) vec.push_back(i); int xyz = 0; printf("Press any key to start!"); _getch(); printf(" Running speed test../n"); { // at() ::QueryPerformanceCounter((LARGE_INTEGER*)&start); for(int i = 0; i < ELEMENTS_IN_VECTOR; i++) xyz += vec.at(i); ::QueryPerformanceCounter((LARGE_INTEGER*)&end); diff_Result = (end - start) / freq; } printf("Result/t/t: %u/n/n", diff_Result); printf("Press any key to start!"); _getch(); printf(" Running speed test../n"); { // operator [] ::QueryPerformanceCounter((LARGE_INTEGER*)&start); for(int i = 0; i < ELEMENTS_IN_VECTOR; i++) xyz -= vec[i]; ::QueryPerformanceCounter((LARGE_INTEGER*)&end); diff_Result = (end - start) / freq; } printf("Result/t/t: %u/n", diff_Result); _getch(); return xyz; }

Editar:
Ahora el valor está siendo asignado a "xyz", por lo que el compilador no lo "borrará".


Ejecuté su código de prueba en mi máquina:

En una compilación de depuración no optimizada, la diferencia entre los dos bucles es insignificante.

En una versión de lanzamiento optimizada, el segundo bucle for está optimizado por completo (la llamada al operator[] probablemente esté en línea y el optimizador puede ver que el bucle no hace nada y puede eliminar todo el bucle).

Si cambio el cuerpo de los bucles para hacer un trabajo real, por ejemplo, vec.at(i)++; y vec[i]++; , respectivamente, la diferencia entre los dos bucles es insignificante.

No veo esta diferencia de rendimiento de cinco a diez veces.


La razón es que un acceso sin marcar puede realizarse con una sola instrucción de procesador. Un acceso verificado también tendrá que cargar el tamaño de la memoria, compararlo con el índice y (suponiendo que esté dentro del rango) saltar una rama condicional al manejador de errores. Puede haber más problemas para manejar la posibilidad de lanzar una excepción. Esto será muchas veces más lento, y esta es precisamente la razón por la que tiene ambas opciones.

Si puede probar que el índice está dentro del rango sin un control de tiempo de ejecución, utilice el operator[] . De lo contrario, use at() o agregue su propio cheque antes de acceder. operator[] debería ser más o menos lo más rápido posible, pero explotará desordenadamente si el índice no es válido.


No se hace nada con el valor de retorno, por lo que si el compilador especifica estas funciones, puede optimizarlas completamente. O quizás pueda optimizar completamente la versión del subíndice ( [] ). Correr sin optimizaciones es inútil desde la perspectiva de la medición del rendimiento, lo que necesita es un programa simple pero útil para ejercer las funciones para que no solo se optimicen. Por ejemplo, podría mezclar el vector (intercambie aleatoriamente 50000 pares de elementos).