2010-07-23 15 views
17

después de perfilar mucho descubrí que este método ocupa la mayor parte del% del tiempo de cálculo. Realmente no veo una manera de optimizar, ya que es una función horrible. (es ...) Tal vez alguien me puede mostrar una buena idea más o menos?¿Es posible optimizar esta función?

public static double perceivedLoudness(double L_G, double L_ETQ, double a0) { 
    double t1 = 1d + 1/4d * Math.pow(10d, 0.1d * (L_G - a0 - L_ETQ)); 
    double t2 = Math.pow(t1, 0.25); 
    return 0.064d * Math.pow(10, 0.025 * L_ETQ) * (t2 - 1); 
} 

Aquí es la versión mejorada:

public static double perceivedLoudness(double L_G, double L_ETQ, double a0) { 
    double x = L_G - a0 - L_ETQ; 
    double t1 = 0.25 * Math.exp(0.230259 * x) + 1; 
    double t2 = Math.sqrt(Math.sqrt(t1)); 
    return ltqFactors[(int)L_ETQ] * (t2 - 1); 
} 

La búsqueda de ltqFactors va de esta manera. Los valores de ltq tienen 20 puntos de la función ltq dada, que aproximadamente debería ser suficiente.

for(int i = 0; i < etqValues.length; ++i) { 
    ltqFactors[(int)etqValues[i]] = 0.064d * Math.exp(etqValues[i] * 0.05756462732485114210d); 
    } 

Editar: Después de más pruebas con más archivos, vengo hasta una velocidad de ~ 100% hasta:

  • antigua: 6,2% con llamadas 7000000
  • Nuevos: 3, 2% 8000000 llamadas.

¡Gracias hasta ahora!

Edit2: No sé qué respuesta a aceptar. :( Con algunas otras mejoras (principalmente tablas de búsqueda) el tiempo de procesamiento para 9000 archivos de sonido bajó de 4: 30min a 3: 28min.

Mantendré esta pregunta abierta para ver si hay otras ideas, pero luego aceptar una respuesta

Editar:. estoy un poco frustrado ahora uso un treeviewer JFace para permitir al usuario navegar por los resultados, y necesito más tiempo para actualizar que la propia cálculos:../

+0

Quiero saber cómo alguien podría incluso llegar a tal función. Esas constantes parecen tan al azar! –

+1

@controlfreak http://ergo.ucsd.edu/~holcus/papers/JSNC2000.pdf – InsertNickHere

+0

No hay nada en ese método que deba tomarse mucho tiempo para calcular, ¿está seguro de que no está ocupando la mayor parte del% porque es siendo llamado muchas veces? –

Respuesta

23

Su función parece ser analítica, le sugiero que la reemplace por completo con un método de interpolación. De esta forma, reduce las costosas llamadas al Math.Pow a unas pocas operaciones aritméticas.

Lo mejor en este caso debe ser una aproximación de función racional. Es probable que su función tenga polos en el plano complejo, esto generalmente derrota la interpolación polinómica.

Tenga en cuenta que tiene dos variables: L_G - a0 - L_ETQ y L_ETQ. La interpolación debe realizarse en una sola variable.

Me gustaría ir a la aproximación de función racional de t2 como una función de L_G - a0 - L_ETQ. Eche un vistazo a Recetas Numéricas para técnicas de implementación.

Además, para la última parte, reemplace

Math.pow(10, 0.025 * L_ETQ); 

por

Math.exp(L_ETQ * 0.05756462732485114210d) 

(que es exp(L_ETQ * 0.025 * log(10))).

Por lo que debe estar bien con un puñado de operaciones aritméticas y una exponencial.

EDIT: See a graph of t2 as a function of L_G - a0 - L_ETQ.

EDIT: Reemplazar

double t1 = 1d + 1/4d * Math.pow(10d, 0.1d * (L_G - a0 - L_ETQ)); 
double t2 = Math.pow(t1, 0.25); 

por

double x = L_G - a0 - L_ETQ; 
double t1 = 0.25 * Math.exp(0.230259 * x) + 1; 
double t2 = Math.sqrt(Math.sqrt(t1)); 

y usted debe ganar un poco más%. En este punto, la aproximación racional puede ser sobreingeniería: tiene dos exp, y dos sqrt.

+0

@Alexandre Esto parece ser interesante. Gracias. – InsertNickHere

+0

Una advertencia: muchos operadores de coma flotante son tan rápidos en los procesadores modernos que, en su lugar, el uso de un método de interpolación puede ser más lento. – Steve314

+0

@steve: pow se calcula con exp y log, y debe tener en cuenta varios casos especiales. La inspección visual muestra un candidato perfecto para la aproximación de Padé cerca de un punto "típico", e incluso ~ 50 operaciones aritméticas (lo que permite una aproximación muy precisa) serán más rápidas que un pow. –

0
  1. intente guardar en caché algunos valores (supongo que L_G y L_ETQ no son esa variable, ¿no?)
  2. intenta implementar en código dependiente de la arquitectura y usa JNI.
3

Las matemáticas no parecen inmediatamente reordenarse para evitar cálculos duplicados, por lo que el enfoque depende de cómo se usa esta función y de la precisión de los resultados que necesita.

El mejor enfoque sería evitar volver a calcular el valor para el mismo conjunto de valores de entrada. ¿Puede su código guardar resultados de cálculo para los mismos valores de entrada? De lo contrario, podría tener una memoria caché para valores, pero tenga cuidado de que los dobles puedan tener muchos valores, puede doblar los dobles en un intervalo conocido (por ejemplo, de 0 a 1 pliegues en números enteros de 0 a 99).

3

Conjeturaría

double t2 = Math.sqrt(Math.sqrt(t1)); 

es más rápido que

double t2 = Math.pow(t1, 0.25); 
+0

He agregado eso. – InsertNickHere

+1

@Insert: ¿y has dicho que en realidad es más rápido? –

+1

@Joachim No soy tan bueno en microbenchmarks, pero si mis resultados son correctos, es mucho más rápido. (~ Rápido: 33133194, Lento: 617337165/nanosegundos). He hecho 2147483 cálculos con 100 "iteraciones" cada uno para probar. – InsertNickHere

2

En echando un vistazo a ese documento se hace referencia, parece que L_ETQ y a0 son simplemente una función de la frecuencia (corteza) de la sonar.

Así, al menos, se podía llegar a una tabla de los resultados de varios cálculos para frecuencias dadas. Por ejemplo, almacenar en caché los resultados de:

.064 * Math.pow(10, 0.025 * L_ETQ) 

por frecuencia. [Can también caché (a0 + L_ETQ) * 0.1]

También, probablemente menor efecto, si lo hay, pero me gustaría convertir el cuarto a 0.25.

+0

He agregado eso. – InsertNickHere

1

Pregenere una tabla de búsqueda para la gama de entradas que su programa puede manejar.

¡No hay nada más rápido que eso! :)

1

Almacenamiento en caché de las salidas, en contra de los parametros de entrada, puede ayudar a:

http://en.wikipedia.org/wiki/Memoization

+0

Me preocuparía más la cantidad total de errores de caché. Si tiene un rango muy amplio de valores, la probabilidad de cache hits sería increíblemente baja. Sin embargo, eso reduciría el cálculo posterior de los mismos conjuntos de valores. Además, me preocuparía el costo del aspecto de un conjunto de caché grande [incluso con hashing] – monksy

+0

L_G - a0 - L_ETQ puede tener una gran variabilidad ya que el nivel de presión sonora se mide con doble precisión y no quiero perder eso En términos generales, puedo tener> 3600000 valores diferentes. (Suponiendo 1000 presiones diferentes, 20 a0 y 20 valores L_ETQ.) – InsertNickHere

1

Esta mención no ha sido todavía, así que habrá.

Es posible que desee considerar pasar de matemática de punto flotante a entero. Las operaciones son bastante más rápidas. Los gráficos tienden a usar números enteros en lugar de flotantes debido a cómo se agregan y almacenan los flotantes. Tendrás que convertir hacia y desde, pero estoy seguro de que recibirás un gran impulso en el rendimiento. El único problema con las matemáticas enteras es que tienes que definir con cuánta precisión estás dispuesto a vivir.

+0

Eso podría ser correcto, pero quiero/necesito la precisión. – InsertNickHere

+2

¿Se puede conformar con un límite superior fijo en la cantidad de decimales? – monksy

0

Lo haría take some stackshots against it, para eliminar las conjeturas. De esa manera podría estar seguro de que el tiempo no se está tomando en otro lugar, como al leer los datos y convertirlos a coma flotante, o al abrir/cerrar archivos. Cuando estaba seguro de que esta rutina consumía un porcentaje importante de tiempo, estoy bastante seguro de que pasará casi todo el tiempo en llamadas a las funciones matemáticas y en convertir L_ETQ a entero. Es posible memorizar esas funciones matemáticas. Por otra parte, como dijo Alexandre, es posible que puedas hacerlo todo con interpolación.

Lo que pasa es que probablemente tenga más de una cosa que optimizar. Si esto lleva como 180 segundos, y si el 50%, por ejemplo, de esa vez esta rutina está en la pila, entonces si reduce su tiempo a la mitad, habrá reducido el tiempo en 45 segundos a 135 segundos. Sin embargo, ahora esta rutina solo está en la pila durante 45/135 segundos o 1/3. Eso significa que otras cosas están usando los otros 2/3 o 90 segundos, y apuesto a que hay algunas cosas que también podrías optimizar. Si puede reducirlos a, por ejemplo, 45 segundos, entonces el total se ha reducido a 90, y el% tomado por la rutina matemática vuelve al 50%, así que tal vez pueda exprimir un poco más. Así es como va. Cada vez que cortas una parte de ella, otras partes aumentan en porcentaje, por lo que puedes ir tras ellas, una y otra vez hasta que realmente la exprimas tanto como sea posible.

Cuestiones relacionadas