2012-02-14 9 views
24

Tenemos un solucionador de CFD y al ejecutar una simulación, se encontró que funcionaba extraordinariamente lento en algunas máquinas pero no en otras. Uso de Intel VTune, se encontró la siguiente línea era el problema (en Fortran):Sustitución de la función powrd extraordinariamente lenta()

RHOV= RHO_INF*((1.0_wp - COEFF*EXP(F0)))**(1.0_wp/(GAMM - 1.0_wp)) 

de perforación en con VTune, el problema se remonta a la línea call pow montaje y al trazar la pila, se mostró que estaba utilizando __slowpow(). Después de buscar, this page apareció quejándose de lo mismo.

En la máquina con la versión 2.12 de libc, la simulación tomó 18 segundos. En la máquina con la versión 2.14 de libc, la simulación tomó 0 segundos.

Según la información en la página mencionada, el problema surge cuando la base para pow() es cercana a 1.0. Así que hicimos otra prueba simple donde escalamos la base por un número arbitrario antes del pow() y luego dividimos por el número elevado al exponente después de la llamada pow(). Esto redujo el tiempo de ejecución de 18 segundos a 0 segundos con el libc 2.12 también.

Sin embargo, no es práctico poner esto sobre el código donde hacemos a**b. ¿Cómo se podría reemplazar la función pow() en libc? Por ejemplo, me gustaría que la línea de montaje call pow generada por el compilador Fortran llame a una función personalizada pow() que escribamos que hace la escala, llama a la libc pow() y luego se divide por la escala. ¿Cómo se puede crear una capa intermedia transparente para el compilador?

Editar

Para aclarar, estamos buscando algo como (pseudo-código):

double pow(a,b) { 
    a *= 5.0 
    tmp = pow_from_libc(a,b) 
    return tmp/pow_from_libc(5.0, b) 
} 

¿Es posible cargar el pow de libc y cambie su nombre en nuestra función personalizada para evitar los conflictos de nombres? Si el archivo customPow.o puede renombrar pow desde libc, ¿qué ocurre si todavía se necesita libc para otras cosas? ¿Eso causaría un conflicto de nombres entre pow en customPow.o y pow en libc?

+0

¡Buen ol 'Fortran! Interesante pregunta +1 –

Respuesta

7

Sólo tiene que escribir su propia función pow, poner el archivo .o en un archivo de biblioteca estática libmypow.a en algún lugar de la ruta de biblioteca del enlazador, y pasan -lmypow al vincular.

+1

¿Eso permitiría la función 'pow' personalizada para llamar a 'pow' en libc sin embargo? Este 'pow' personalizado simplemente escalará la base si es necesario, luego llamará a libc' pow' y luego sin escala si es necesario. Parece que habrá algunos conflictos de nombres. – tpg2114

+9

Si usa enlaces dinámicos, existen ataques 'dlsym' que puede usar para lograr el comportamiento deseado, pero es frágil. Un mejor enfoque, si solo lo necesita para trabajar en sistemas con el enlazador GNU, es la opción '--wrap' de' ld' (que 'gcc' puede pasar a' ld' mediante '-Wl, - wrap, pow'). Luego pones '__wrap_pow' en' libmypow.a', y lo haces llamar '__real_pow' donde necesita usar el libc pow, y todo debería estar bien. –

3

pow(a,b) es lo mismo que exp(b*ln(a)), tal vez esa sustitución funcione para usted.

+1

Eso probablemente evadiría la lentitud de la llamada, pero estamos buscando una forma de reemplazar esencialmente la llamada de función generada por el operador '**' en Fortran sin tener que cambiar la base de código real que tenemos, si es posible. – tpg2114

+2

Así que enlace su propia versión de pow() que usa esta identidad. –

+2

esto da un resultado diferente para 1.0000000000000020^1.5: 1.0000000000000031 con 'call pow', 1.0000000000000029 con' -ffast-math' y 1.5000000000000013 con 'exp (b * ln (a))' – steabert

1

He probado esto por mí mismo y, de hecho, si compilo el programa de prueba desde la página que enlazo, usa call pow en el código de ensamblaje. Sin embargo, compilando con la optimización -ffast-math no hay llamadas a pow, pero el resultado es ligeramente diferente.

22

Bueno, espere ahora. La biblioteca no llama al __slowpow() solo para jugar con usted; llama al __slowpow() porque cree que la precisión adicional es necesaria para dar un resultado preciso para los valores que le da (en este caso, base muy cerca de 1, exponente de la orden 1). Si le importa la precisión de este cálculo, debe comprender por qué es así y si es importante antes de tratar de evitarlo. Podría ser el caso que para (digamos) gran F0 negativo todo este asunto se puede redondear a 1; o puede que no, dependiendo de lo que se haga con este valor más adelante.Si alguna vez necesitas 1.d0 menos este resultado, vas a querer esa precisión extra.

+0

Eso es ciertamente cierto. Pero, al menos en nuestra instancia, nuestro código es dimensional así que las únicas veces que tenemos una base cercana a uno es cuando computamos cosas para visualización o procesamiento posterior de algún tipo, por lo que obtener entre 1e-15 de la respuesta correcta no es terriblemente importante . Ejecuté una comparación para ver cuánto perdimos y el error es ~ 1e-13, que para nuestro código de segundo orden es menor que nuestro error de discretización, así que es seguro para nosotros reemplazar todo pow() con uno menos preciso. . – tpg2114

Cuestiones relacionadas