estoy corriendo en un comportamiento de optimización incompatibles con diferentes compiladores para el siguiente código:optimización del acceso a los miembros en C++
class tester
{
public:
tester(int* arr_, int sz_)
: arr(arr_), sz(sz_)
{}
int doadd()
{
sm = 0;
for (int n = 0; n < 1000; ++n)
{
for (int i = 0; i < sz; ++i)
{
sm += arr[i];
}
}
return sm;
}
protected:
int* arr;
int sz;
int sm;
};
La función doadd
simula algún acceso intensivo a los miembros (ignorar los desbordamientos, además de esta pregunta). En comparación con un código similar implementado como una función:
int arradd(int* arr, int sz)
{
int sm = 0;
for (int n = 0; n < 1000; ++n)
{
for (int i = 0; i < sz; ++i)
{
sm += arr[i];
}
}
return sm;
}
El método doadd
corre alrededor de 1,5 veces más lento que la función dearradd
cuando se compila en modo de lanzamiento con Visual C++ 2008. Cuando modifico el método doadd
a ser como sigue (Aliasing todos los miembros con los lugareños):
int doadd()
{
int mysm = 0;
int* myarr = arr;
int mysz = sz;
for (int n = 0; n < 1000; ++n)
{
for (int i = 0; i < mysz; ++i)
{
mysm += myarr[i];
}
}
sm = mysm;
return sm;
}
Runtimes se convierten en más o menos lo mismo. ¿Estoy en lo cierto al concluir que esta es una optimización faltante por el compilador de Visual C++? g++
parece hacerlo mejor y ejecutar tanto la función de miembro como la función normal a la misma velocidad al compilar con -O2
o -O3
.
La evaluación comparativa se realiza mediante la invocación de la función de doadd
miembro y arradd
en algunos suficientemente grande array (unos pocos millones de números enteros en tamaño).
EDIT: Algunas pruebas de grano fino muestra que el principal culpable es el miembro sm
. Reemplazar todos los demás por las versiones locales todavía hace que el tiempo de ejecución sea largo, pero una vez que reemplazo sm
por mysm
, el tiempo de ejecución pasa a ser igual a la versión de la función.
Resolución
decepcionado con las respuestas (siento chicos), que Shaked de mi pereza y se sumergió en los listados de desmontaje de este código. Mi answer below resume los hallazgos. En resumen: no tiene nada que ver con el aliasing, tiene todo que ver con el desenrollado de bucles, y con algunas heurísticas extrañas se aplica MSVC al decidir qué bucle se debe desenrollar.
¿Su instancia 'tester' está asignada en el montón? – ereOn
¿Tal vez sea por el almacenamiento en caché? ¿los has llamado en diferente orden? – ruslik
@eeOn: no, en la pila en 'main' –