2010-06-29 20 views
8

Me he encontrado con un problema curioso. Un algoritmo que estoy trabajando consiste en un montón de cálculos de este tipoRendimiento de punto flotante de 32 bits frente a de 64 bits

q = x(0)*y(0)*z(0) + x(1)*y(1)*z(1) + ... 

donde la longitud de la suma está entre 4 y 7.

Los cálculos originales se puede hacer todo el uso de precisión de 64 bits. Para la experimentación, traté de usar una precisión de 32 bits para los valores de entrada x, y, z (de modo que los cálculos se realizan usando 32 bits), y el almacenamiento del resultado final como un valor de 64 bits (conversión directa).

Espero que el rendimiento de 32 bits sea mejor (tamaño de caché, tamaño SIMD, etc.), pero para mi sorpresa no hubo diferencia en el rendimiento, incluso en la disminución.

La arquitectura en cuestión es Intel 64, Linux y GCC. Ambos códigos parecen usar SSE y las matrices en ambos casos están alineadas con el límite de 16 bytes.

¿Por qué sería así? Mi conjetura hasta ahora es que la precisión de 32 bits puede usar SSE solo en los primeros cuatro elementos, y el resto se hace en serie compuesto por la sobrecarga del elenco.

+0

Agregaste una recompensa, ¿qué te gustó de la respuesta de dsimcha? También podría valer la pena probar el GCC más reciente que puedas o el compilador de Intel http://software.intel.com/en-us/articles/non-commercial-software-download/ para ver si hacen un mejor trabajo compilando/vectorizando . – Rup

+0

@Rup Me gusta su respuesta, sin embargo, también me gustaría tener otras opiniones, así que puse una recompensa – Anycorn

Respuesta

24

En x87 al menos, todo está realmente hecho en 80 bits de precisión internamente. La precisión realmente solo determina cuántos de esos bits están almacenados en la memoria. Esta es una de las razones por las cuales las diferentes configuraciones de optimización pueden cambiar ligeramente los resultados: cambian la cantidad de redondeo de 80 bits a 32 o 64 bits.

En la práctica, el uso de coma flotante de 80 bits (long double en C y C++, real en D) es por lo general lenta porque no hay modo eficaz de cargar y almacenar 80 bits de la memoria. 32 y 64 bits suelen ser igualmente rápidos siempre que el ancho de banda de la memoria no sea el cuello de botella, es decir, si todo está en caché de todos modos. 64 bits puede ser más lento si ocurre cualquiera de las siguientes situaciones:

  1. El ancho de banda de memoria es el cuello de botella.
  2. Los números de 64 bits no están alineados correctamente en los límites de 8 bytes. Los números de 32 bits solo requieren una alineación de 4 bytes para una eficiencia óptima, por lo que son menos quisquillosos. Algunos compiladores (el compilador Digital Mars D viene a la mente) no siempre lo hacen bien para los dobles de 64 bits almacenados en la pila. Esto hace que sea necesario duplicar la cantidad de operaciones de memoria para cargar uno, lo que en la práctica da como resultado un golpe de rendimiento de 2x en comparación con los flotadores de 64 bits alineados correctamente o los flotadores de 32 bits.

En cuanto a las optimizaciones SIMD ir, hay que señalar que la mayoría de los compiladores son horrible en código auto-vectorización. Si no desea escribir directamente en lenguaje ensamblador, la mejor forma de aprovechar estas instrucciones es utilizar operaciones tipo array, que están disponibles, por ejemplo, en D e implementadas en términos de instrucciones SSE. Del mismo modo, en C o C++, es probable que desee utilizar una biblioteca de alto nivel de funciones que sean optimizadas para SSE, aunque no conozco ninguna buena idea, porque la mayoría de las veces programo en D.

.
+4

"x87" - Ligeramente mejor que esos viejos procesadores x86. :-) – Thanatos

+4

http://en.wikipedia.org/wiki/X87 – Adam

0

Probablemente es porque su procesador todavía hace el conteo de 64 bits y luego recorta el número. Hubo algún indicador de CPU que podría cambiar, pero no recuerdo ...

0

Primero compruebe el ASM que se produce. Puede que no sea lo que esperas.

Proveedores escribirlo como un bucle:

typedef float fp; 
fp q = 0 
for(int i = 0; i < N; i++) 
    q += x[i]*y[i]*z[i] 

Algunos compilador puede notar el bucle y no la forma desenrollada.

Por último, su código usa () en lugar de []. Si su código realiza muchas llamadas a función (de 12 a 21), eso amortiguará el costo de FP e incluso eliminar el cálculo de fp en conjunto no hará mucha diferencia. Inlineing OTOH podría.

+0

gracias, en realidad 'q()' son macros convirtiendo directamente al acceso del puntero sin formato – Anycorn

+0

@aaa: Bueno, si hay alguna matemática en absoluto, todavía podría ser un gran porcentaje Además, no sé qué tan bien el compilador se ocupa de mezclar FP y otras cosas. Eso podría ser suficiente para bloquear el uso de operaciones vectoriales. – BCS

Cuestiones relacionadas