Rendimiento de reparto de size_t a double

TL; DR: ¿Por qué está multiplicando / lanzando datos ensize_t lento y por qué esto varía según la plataforma?

Estoy teniendo algunos problemas de rendimiento que no entiendo completamente. El contexto es un capturador de fotogramas de la cámara en el que se lee una imagen de uint16_t de 128x128 y se procesa posteriormente a una velocidad de varios 100 Hz.

En el post-procesamiento genero un histograma.frame->histo que es deuint32_t y tienethismaxval = 2 ^ 16 elementos, básicamente cuento todos los valores de intensidad. Usando este histograma calculo la suma y la suma al cuadrado:

double sum=0, sumsquared=0;
size_t thismaxval = 1 << 16;

for(size_t i = 0; i < thismaxval; i++) {
    sum += (double)i * frame->histo[i];
    sumsquared += (double)(i * i) * frame->histo[i];
}

Al perfilar el código con el perfil obtuve lo siguiente (muestras, porcentaje, código):

 58228 32.1263 :  sum += (double)i * frame->histo[i];
116760 64.4204 :  sumsquared += (double)(i * i) * frame->histo[i];

o, la primera línea ocupa el 32% del tiempo de CPU, la segunda línea el 64%.

Hice algunas evaluaciones comparativas y parece ser el tipo de datos / casting que es problemático. Cuando cambio el código a

uint_fast64_t isum=0, isumsquared=0;

for(uint_fast32_t i = 0; i < thismaxval; i++) {
    isum += i * frame->histo[i];
    isumsquared += (i * i) * frame->histo[i];
}

corre ~ 10x más rápido. Sin embargo, este impacto de rendimiento también varía según la plataforma. En la estación de trabajo, una CPU Core i7 950 @ 3.07GHz el código es 10 veces más rápido. En mi Macbook8,1, que tiene un Intel Core i7 Sandy Bridge 2.7 GHz (2620M), el código es solo 2 veces más rápido.

Ahora me estoy preguntando:

¿Por qué el código original es tan lento y fácil de acelerar?¿Por qué esto varía tanto por plataforma?

Actualizar:

Compilé el código anterior con

g++ -O3  -Wall cast_test.cc -o cast_test

Actualización2:

Ejecuté los códigos optimizados a través de un perfilador (Instrumentos en Mac, comoTiburón) y encontré dos cosas:

1) El bucle en sí toma una cantidad de tiempo considerable en algunos casos.thismaxval es de tiposize_t.

for(size_t i = 0; i < thismaxval; i++) toma el 17% de mi tiempo de ejecución totalfor(uint_fast32_t i = 0; i < thismaxval; i++) lleva 3,5%for(int i = 0; i < thismaxval; i++) no aparece en el perfilador, asumo que es menos del 0.1%

2) Los tipos de datos y el reparto son los siguientes:

sumsquared += (double)(i * i) * histo[i]; 15% (consize_t i)sumsquared += (double)(i * i) * histo[i]; 36% (conuint_fast32_t i)isumsquared += (i * i) * histo[i]; 13% (conuint_fast32_t i, uint_fast64_t isumsquared)isumsquared += (i * i) * histo[i]; 11% (conint i, uint_fast64_t isumsquared)

Asombrosamente,int es más rápido queuint_fast32_t?

Actualización 4:

Corrí algunas pruebas más con diferentes tipos de datos y compiladores diferentes, en una máquina. Los resultados son los siguientes.

Para testd 0 - 2 el código relevante es

    for(loop_t i = 0; i < thismaxval; i++)
        sumsquared += (double)(i * i) * histo[i];

consumsquared un doble, yloop_t size_t, uint_fast32_t yint para las pruebas 0, 1 y 2.

Para las pruebas 3--5 el código es

    for(loop_t i = 0; i < thismaxval; i++)
        isumsquared += (i * i) * histo[i];

conisumsquared de tipouint_fast64_t yloop_t otra vezsize_t, uint_fast32_t yint Para las pruebas 3, 4 y 5.

Los compiladores que utilicé son gcc 4.2.1, gcc 4.4.7, gcc 4.6.3 y gcc 4.7.0. Los tiempos están en porcentajes del tiempo total de cpu del código, por lo que muestran un rendimiento relativo, no absoluto (aunque el tiempo de ejecución fue bastante constante a los 21 s). El tiempo de la CPU es para las dos líneas, porque no estoy seguro de si el generador de perfiles separó correctamente las dos líneas de código.

gcc:    4.2.1  4.4.7  4.6.3  4.7.0
----------------------------------
test 0: 21.85  25.15  22.05  21.85
test 1: 21.9   25.05  22     22
test 2: 26.35  25.1   21.95  19.2
test 3: 7.15   8.35   18.55  19.95
test 4: 11.1   8.45   7.35   7.1
test 5: 7.1    7.8    6.9    7.05

o:

Según esto, parece que la conversión es costosa, independientemente del tipo de entero que use.

Además, parece que gcc 4.6 y 4.7 no pueden optimizar el bucle 3 (size_t y uint_fast64_t) correctamente.

Respuestas a la pregunta(2)

Su respuesta a la pregunta