Pregunta

Hace una década o dos, que valía la pena escribir código numérico para evitar el uso multiplica y divide y utilizar la suma y resta en su lugar. Un buen ejemplo es el uso diferencias hacia adelante para evaluar una curva polinómica en lugar de calcular el polinomio directamente.

¿Sigue siendo el caso, o tienen arquitecturas informáticas modernas avanzado hasta el punto en donde *, / ya no son muchas veces más lento que +, -?

Para ser más específicos, estoy interesado en código compilado C / C ++ que se ejecuta en los chips modernos típica x86 con amplia a bordo de hardware de punto flotante, no una pequeña micro tratando de hacer FP en el software. Soy consciente de la canalización y otras mejoras en la arquitectura impiden recuentos de ciclos específicos, pero todavía me gustaría obtener una intuición útil.

¿Fue útil?

Solución

También depende de la mezcla de instrucciones. Su procesador tiene varias unidades de cálculo que hacen una pausa en cualquier momento, y obtendrá el máximo rendimiento si todos ellos están llenos todo el tiempo. Por lo tanto, la ejecución de un bucle de mul de es tan rápido como la ejecución de un bucle o añade -. Pero el mismo no se mantiene si la expresión se vuelve más compleja

Por ejemplo, tome este bucle:

for(int j=0;j<NUMITER;j++) {
  for(int i=1;i<NUMEL;i++) {
    bla += 2.1 + arr1[i] + arr2[i] + arr3[i] + arr4[i] ;
  }
}

para NUMITER = 10 ^ 7, Numel = 10 ^ 2, ambas matrices inicializadas a pequeños números positivos (NaN es mucho más lento), esto se lleva a 6,0 segundos utilizando dobles en un proc 64 bits. Si sustituyo el lazo con

bla += 2.1 * arr1[i] + arr2[i] + arr3[i] * arr4[i] ;

Sólo se tarda 1,7 segundos ... así ya que "fue la mano" las adiciones, las mul eran esencialmente libre; y la reducción de adiciones ayudó. Se consigue de más confuso:

bla += 2.1 + arr1[i] * arr2[i] + arr3[i] * arr4[i] ;

- mismo mul / añadir distribución, pero ahora la constante se añade en lugar de multiplicarse en - toma 3,7 segundos. Su procesador se haya optimizado para realizar cálculos numéricos típicos de manera más eficiente; de modo punto-producto como sumas de mul y sumas a escala son casi tan bueno como se pone; añadiendo constantes no es tan común, por lo que es más lenta ...

bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; /*someval == 2.1*/

toma de nuevo 1,7 segundos.

bla += someval + arr1[i] + arr2[i] + arr3[i] + arr4[i] ; /*someval == 2.1*/

(igual que el bucle inicial, pero sin caro adición constante: 2,1 segundos)

bla += someval * arr1[i] * arr2[i] * arr3[i] * arr4[i] ; /*someval == 2.1*/

(en su mayoría mul, pero una adición: 1,9 segundos)

Así que, básicamente; Es difícil decir cuál es más rápido, pero si usted desea evitar los cuellos de botella, lo más importante es tener una mezcla sana, evitar NaN o INF, evitar la adición constantes. Haga lo que haga, asegúrese de probar y probar diferentes opciones de compilador, ya que muchas veces los pequeños cambios pueden simplemente hacer la diferencia.

Algunos casos más:

bla *= someval; // someval very near 1.0; takes 2.1 seconds
bla *= arr1[i] ;// arr1[i] all very near 1.0; takes 66(!) seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; // 1.6 seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, 2.2 seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, floats 2.2 seconds
bla += someval * arr1[i]* arr2[i];// 0.9 in x64, 1.6 in x86
bla += someval * arr1[i];// 0.55 in x64, 0.8 in x86
bla += arr1[i] * arr2[i];// 0.8 in x64, 0.8 in x86, 0.95 in CLR+x64, 0.8 in CLR+x86

Otros consejos

En la teoría de la información está aquí:

Intel®64 y Manual IA-32 Arquitecturas optimización de referencia, APÉNDICE C LATENCIA DE INSTRUCCIONES y el rendimiento

Para todos los procesadores que la lista, la latencia en FMUL es muy cercana a la de FADD o FDIV. En algunos de los procesadores anteriores, es FDIV 2-3 tiempo más lento de lo que, si bien en los procesadores más recientes, es el mismo que FMUL.

Advertencias:

  1. El documento he vinculado en realidad dice que no se puede confiar en estos números en la vida real ya que el procesador va a hacer lo que quiere hacer las cosas más rápido si es correcta.

  2. Hay una buena probabilidad de su compilador decidir usar uno de los muchos juegos de instrucciones nuevos que tienen un punto flotante multiplicar / dividir disponible.

  3. Este es un documento complicado solamente destinado a ser leído por los autores de compiladores y yo podría haber conseguido equivocado. Como si no estuviera claro por qué el número de latencia FDIV está completamente ausente de algunas de las CPUs.

La mejor manera de responder a esta pregunta es para escribir en realidad un punto de referencia / perfil del procesamiento que tiene que hacer. Empírica debe usarse durante teórica siempre que sea posible. Especialmente cuando es más fácil de alcanzar.

Si ya conoce las distintas aplicaciones de la matemática que tiene que hacer, se podría escribir un unos transfermations diferentes códigos de los cálculos y ver donde sus picos de rendimiento. Esto permitirá que el procesador / compilador para generar diferentes flujos de ejecución para llenar las tuberías de procesador y darle una respuesta concreta a su respuesta.

Si estás interesado en concreto el rendimiento de las instrucciones / MUL / ADD / tipo SUB DIV incluso se podría echar en ciertos procedimientos de montaje en línea para controlar específicamente qué variantes de estas instrucciones son ejecutadas. Sin embargo es necesario asegurarse de que está manteniendo unidades de ejecución multilple ocupado para conseguir una buena idea del rendimiento del sistema es capaz de hacer.

Además de hacer algo como esto permitirá comparar el rendimiento en múltiples variaciones del procesador simplemente ejecutando el mismo programa en ellos, y también podría permitir que usted toma en cuenta las diferencias de la placa base.

Editar:

Arquitectura básica de un + - es idéntica. Por lo que, lógicamente, tardan el mismo tiempo de calcular. * Por el contrario, requieren múltiples capas, típicamente construidos de "sumadores completos" para completar una sola operación. Este garentees que mientras que un * puede ser emitido a la tubería cada ciclo tendrá una latencia más alta que un circuito / menos. A fp / operación se implementa típicamente usando un método de aproximación que iterativamente converge hacia la respuesta correcta en el tiempo. Estos tipos de aproximaciones se realizan normalmente a través de la multiplicación. Así que para punto flotante por lo general puede suponer división se necesitará más tiempo porque es poco práctico "desenrollar" las multiplicaciones (que ya es un gran circuito y de que es uno mismo) en la tubería de una multitud de circuitos multiplicadores. Aún así, el rendimiento de un sistema dado se mide mejor por medio de la prueba.

No puedo encontrar una referencia definitiva, pero una amplia experimentación me dice que la multiplicación de flotación hoy en día es casi la misma velocidad que la suma y la resta, mientras que la división no es (pero no "muchas veces" más lento, tampoco). Puede obtener la intuición que desee únicamente mediante la ejecución de sus propios experimentos - recuerde para generar los números aleatorios (millones de ellos) por adelantado, leerlos antes de empezar la sincronización, y el uso de contadores de rendimiento de la CPU (con ninguna otra ejecución de procesos, como todo lo que puede evitar que se) para la medición exacta!

La diferencia de velocidad de * / vs + - depende de su arquitectura de procesador. En 86 general y especialmente con la diferencia de velocidad se ha vuelto menos con los procesadores modernos. * Debe estar cerca de +, en caso de duda: solo experimento. También si usted tiene un problema muy difícil con una gran cantidad de operaciones de PF considerar el uso de la GPU (GeForce, ...) que funciona como un procesador vectorial.

Probablemente hay muy poca diferencia en el tiempo entre la multiplicación y la adición. división por el contrario sigue siendo significativamente más lento que la multiplicación debido a su naturaleza recursiva. en modernos deben considerarse las instrucciones de arquitectura x86 sse cuando se hace la operación de punto flotante en lugar de utilizar el fpu.Though un buen C / C ++ debe darle la opción de usar sse en lugar de la FPU.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top