Замена extrordinarily slow pow ()

У нас есть решатель CFD, и при запуске симуляции было обнаружено, что на некоторых машинах необычно медленный, но не другие. Используя Intel VTune, было обнаружено, что следующая строка была проблемой (в Fortran):

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

Свернувшись с VTune, проблема была прослежена на сборочной линии call pow и при трассировке стека показала, что она использует __slowpow() . После некоторых поисков на этой странице появилось жалобы на то же самое.

На машине с версией libc версии 2.12 симуляция заняла 18 секунд. На машине с версией libc версии 2.14 симуляция заняла 0 секунд.

Основываясь на информации на вышеупомянутой странице, проблема возникает, когда база для pow() близка к 1.0. Таким образом, мы сделали еще один простой тест, в котором мы масштабировали базу на произвольное число до pow() а затем делили на число, поднятое до экспоненты после вызова pow() . Это снизило время выполнения с 18 секунд до 0 секунд с помощью libc 2.12.

Тем не менее, нецелесообразно ставить это по всему коду, где мы делаем a**b . Как можно было бы заменить функцию pow() в libc? Например, я хотел бы, чтобы call pow сборочной линии call pow созданный компилятором Fortran, вызывал пользовательскую функцию pow() мы пишем, которая выполняет масштабирование, вызывает libc pow() а затем делит на масштабирование. Как создать прозрачный для компилятора промежуточный уровень?

редактировать

Чтобы уточнить, мы ищем что-то вроде (псевдокода):

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

Можно ли загрузить pow из libc и переименовать его в нашу пользовательскую функцию, чтобы избежать конфликтов имен? Если файл customPow.o может переименовать pow из libc, что произойдет, если libc по-прежнему необходим для других вещей? Это вызовет конфликт имен между pow в customPow.o и pow в libc?

Просто напишите свою собственную функцию pow , поместите файл .o в архив статической библиотеки libmypow.a где-нибудь в пути библиотеки компоновщика и передайте -lmypow при связывании.

Хорошо, держись сейчас. Библиотека не вызывает __slowpow() чтобы играть с вами; он вызывает __slowpow() потому что полагает, что дополнительная точность необходима, чтобы дать точный результат для значений, которые вы им даете (в этом случае база очень близка к 1, показатель порядка 1). Если вы заботитесь о точности этих вычислений, вы должны понять, почему это так, и если это имеет значение, прежде чем пытаться обойти это. Это может быть так, что для (скажем) большого отрицательного F0 все это можно безопасно округлить до 1; или это может не быть, в зависимости от того, что сделано с этим значением позже. Если вам когда-нибудь понадобится 1.d0 минус этот результат, вам понадобится дополнительная точность.

pow(a,b) совпадает с exp(b*ln(a)) , возможно, что замена будет работать для вас.

Я сам это испытал, и действительно, если я скомпилирую тестовую программу со страницы, на которую вы ссылаетесь, она использует call pow в коде сборки. Однако при компиляции с оптимизацией -ffast-math нет вызова pow, но результат несколько отличается.