Sunday, 1 October 2017

Una Media Móvil Exponencial


Seguro, multithreading puede ayudar. Pero casi con seguridad puede mejorar el rendimiento en una única máquina roscada.


Primero, lo está calculando en la dirección equivocada. Solamente las máquinas más modernas pueden hacer prefetching negativo del stride. Casi todos los machihnes son más rápidos para los pasos de la unidad. Es decir. Cambiar la dirección de la matriz para que escanear de baja a alta en lugar de alta a baja es casi siempre mejor.


A continuación, reescribir un poco - por favor, permítanme acortar los nombres de las variables para que sea más fácil de escribir:


Por cierto, empezaré a usar las abreviaturas p para el precio y s para suavizar, para guardar la escritura. Soy perezoso.


y en general


Precalculando s * s


Puedes hacer


Pero es probablemente más rápido de hacer


La latencia entre avg [i] y avg [i-2] es entonces 1 multiplica y una suma, en lugar de una substracción y una multiplicación entre avg [i] y avg [i-1]. Es decir. Más del doble de rápido.


En general, se desea reescribir la recurrencia para que avg [i] se calcula en términos de avg [j] para j en la medida de lo posible, sin llenar la máquina, ya sea unidades de ejecución o registros.


Básicamente, estás haciendo más multiplicaciones en general, con el fin de obtener menos cadenas de múltiplos (y substracciones) en el camino crítico. Saltar de avg [i-2] a avg [i [es fácil, probablemente puede hacer tres y cuatro. Exactamente hasta qué punto depende de lo que es su máquina, y cuántos registros tiene.


Y la latencia del sumador de punto flotante y multiplicador. O, mejor aún, el sabor de la combinación de multiplicar añadir la instrucción que tiene - todas las máquinas modernas tienen. P. ej. Si el MADD o MSUB es de 7 ciclos de largo, puede hacer hasta 6 otros cálculos en su sombra, incluso si sólo tiene una unidad de punto flotante. Totalmente pipeline. Y así. Menos si pipelined cada ciclo del otherr, como es común para la doble precisión en virutas y GPUs más viejas. El código de ensamblaje debe ser software pipeline para que las iteraciones de bucle diferentes se superpongan. Un compilador bueno debe hacer eso para usted, pero usted puede ser que tenga que reescribir el código de C para conseguir el mejor funcionamiento.


Por cierto: no quiero decir que usted debe estar creando una matriz de [] avg. En su lugar, se necesitarían dos promedios si se calcula [i] en términos de avg [i-2], y así sucesivamente. Puedes usar una matriz de avg [i] si quieres, pero creo que solo necesitas tener 2 o 4 avgs, llamados, creativamente, avg0 y avg1 (2, 3.), y "rotarlos".


Este tipo de truco, dividir un acumulador o media en dos o más, combinando múltiples etapas de la recurrencia, es común en el código de alto rendimiento.


Oh, sí: precalculate s * s, etc.


Si lo he hecho bien, en precisión infinita esto sería idéntico. (Compruébeme, por favor.)


Sin embargo, en precisión finita FP sus resultados pueden diferir, esperemos que sólo ligeramente, debido a los redondeos diferentes. Si el desenrollado es correcto y las respuestas son significativamente diferentes, probablemente tenga un algoritmo numéricamente inestable. Tú eres el que deberías saber.


Nota: los errores de redondeo de coma flotante cambiarán los bits bajos de su respuesta. Tanto debido a la reorganización del código, y utilizando MADD. Creo que probablemente está bien, pero tienes que decidir.


Nota: los cálculos para avg [i] y avg [i-1] son ​​ahora independientes. Así que puede utilizar un conjunto de instrucciones SIMD, como Intel SSE2, que permite la operación en dos valores de 64 bits en un registro de 128 bits de ancho a la vez. Eso va a ser bueno para casi 2X, en una máquina que tiene suficientes ALUs.


Si tienes suficientes registros para reescribir avg [i] en términos de avg [i-4] (y estoy seguro de que lo haces en iA64), entonces puedes ir 4X de ancho, si tienes acceso a una máquina como 256 bit AVX.


En una GPU. Usted puede ir para las recurrencias más profundas, reescribiendo avg [i] en términos de avg [i-8], y así sucesivamente.


Algunas GPUs tienen instrucciones que calculan AX + B o incluso AX + BY como una sola instrucción. Aunque eso es más común para 32 bits que para 64 bits de precisión.


En algún momento probablemente empezaría a preguntar: ¿quieres hacer esto en múltiples precios a la vez? Esto no sólo le ayudará con multithreading, sino que también le conviene para correr en una GPU. Y usando SIMD ancho.


Adición tardía menor


Estoy un poco avergonzado por no haber aplicado la Regla de Horner a expresiones como


Un poco más eficiente. Resultados ligeramente diferentes con el redondeo.


En mi defensa, cualquier compilador decente debería hacer esto por ti.


Pero la regla de Hrner hace que la cadena de dependencia sea más profunda en términos de multiplicaciones. Es posible que tenga que desenrollar y pipeline el bucle unas cuantas veces más. O puedes hacer


Implementación del promedio móvil exponencial en Java


Tengo básicamente una matriz de valores como este:


La matriz anterior es demasiado simplificada, estoy coleccionando 1 valor por milisegundo en mi código real y necesito procesar la salida en un algoritmo que escribí para encontrar el pico más cercano antes de un punto en el tiempo. Mi lógica falla porque en mi ejemplo anterior, 0.36 es el pico real, pero mi algoritmo miraría hacia atrás y vería el último número 0.25 como el pico, ya que hay un descenso a 0.24 antes.


El objetivo es tomar estos valores y aplicarles un algoritmo que los suavice un poco para que tenga valores más lineales. (Es decir: quisiera que mis resultados fueran curvy, no jaggedy)


Se me ha dicho que aplique un filtro de media móvil exponencial a mis valores. ¿Cómo puedo hacer esto? Es muy difícil para mí leer las ecuaciones matemáticas, trato mucho mejor con el código.


¿Cómo proceso los valores en mi matriz, aplicando un cálculo del promedio móvil exponencial para igualarlos?


Respuesta Wiki


A diferencia de un promedio móvil simple (SMA), que da a todos los puntos de datos el mismo peso, un promedio móvil exponencial (EMA) da más peso a los datos recientes, por lo que es más útil cuando los datos recientes son más relevantes que el promedio histórico.


EMA se calcula de la siguiente manera:


1. Calculando un EMA% dependiendo del número de puntos de datos que desea considerar, EMA% = 2 / (n + 1) donde n es el número de puntos que desea considerar,


Por ejemplo, utilizar los últimos 5 puntos EMA% = 2 / (5 + 1) = 0,33,


(2) + (2) + (3) + (1)


Lo que esto hace es que da a los puntos de datos más antiguos un peso mucho menor. Una media móvil simple simplemente multiplicaría cada uno de los últimos 5 puntos por 20% y los sumaría, haciéndolos todos iguales.


Aquí hay un ejemplo de puntos de datos, usando los últimos 5 puntos para calcular SMA y EMA.


Usted puede ver que EMA se acerca a los datos reales, aún más suave que él, pero más sensible a sus fluctuaciones.

No comments:

Post a Comment