#c #performance #pow
#c #Производительность #pow
Вопрос:
Мне приходится много раз повышать 10 до степени удвоения.
Есть ли более эффективный способ сделать это, чем с помощью математической библиотеки pow(10,double)
? Если это имеет значение, мои двойные значения всегда отрицательны между -5 и -11.
Я предполагаю, что pow (double, double) использует более общий алгоритм, чем требуется для pow (10, double), и поэтому может быть не самым быстрым методом. Учитывая некоторые из приведенных ниже ответов, это могло быть неверным предположением.
Что касается причины, то это для логарифмической интерполяции. У меня есть таблица значений x и y. Мой объект имеет известное значение x (которое почти всегда является двойным).
double Dbeta(struct Data *diffusion, double per){
double frac;
while(per>diffusion->x[i]){
i ;
}
frac = (per-diffusion->x[i-1])/(diffusion->x[i]-diffusion->x[i-1]);
return pow(10,log10DB[i-1] frac * (log10DB[i]-log10DB[i-1]));
}
Эта функция вызывается много раз.
Мне сказали изучить профилирование, так что это то, что я сделаю в первую очередь.
Мне только что сказали, что я мог бы использовать натуральные логарифмы вместо базового 10, что, очевидно, правильно. (моя глупость иногда поражает даже меня самого.)
После замены всего на натуральные логарифмы все работает немного быстрее. С профилированием (это новое слово, которое я узнал сегодня) Я обнаружил, что 39% моего кода тратится на функцию exp, поэтому для тех, кто задавался вопросом, действительно ли именно эта часть была узким местом в моем коде, это было.
Комментарии:
1. Действительно
double
ли мощности имеют целочисленные значения?2. И будет ли второй аргумент
pow
всегда быть целым числом? Вы когда-нибудь передадите, например-6.8
, (или аналогичный) в качестве второго аргумента? Или это всегда будет только-5
,-6
,-7
,-8
,-9
,-10
или-11
(и ничего другого между ними)?3. «Мне приходится много раз повышать 10 до степени удвоения» Это довольно необычно, можете ли вы показать часть своего кода?
4.
I suspect this is the bottleneck
Не создавайте подозрительный профиль и будьте уверены, иначе вы можете просто тратить время. Не забудьте распечатать копию правил оптимизации .5. если показатели всегда являются целыми числами или принадлежат ограниченному набору известных значений, тогда просто используйте таблицу поиска
Ответ №1:
Для pow(10.0, n)
этого должно быть быстрее установить c = log(10.0)
, что вы можете вычислить один раз, а затем использовать exp(c*n)
, что должно быть значительно быстрее, чем pow(10.0, n)
(что в основном делает то же самое внутри, за исключением того, что оно будет вычисляться log(10.0)
снова и снова, а не только один раз). Помимо этого, вероятно, вы больше ничего не можете сделать.
Комментарии:
1.
which is basically doing that
Ох, не знал, это действительно так!
Ответ №2:
Да, функция pow работает медленно (примерно в 50 раз превышает стоимость умножения, для тех, кто запрашивает тесты).
-
С помощью некоторого логарифмического / экспоненциального обмана мы можем выразить 10 ^ x как
10^x = exp(log(10^x)) = exp(x * log(10)).
Таким образом, вы можете реализовать 10 ^ x с
exp(x * M_LN10)
помощью, что должно быть более эффективным, чем pow. -
Если двойная точность не критична, используйте плавающую версию функции
expf
(илиpowf
), которая должна быть более эффективной, чем двойная версия. -
Если приблизительная точность в порядке, предварительно вычислите таблицу в диапазоне [-5, -11] и выполните быстрый поиск с помощью линейной интерполяции.
Некоторые тесты (с использованием glibc 2.31):
Benchmark Time
---------------------------------
pow(10, x) 15.54 ns
powf(10, x) 7.18 ns
expf(x * (float)M_LN10) 3.45 ns
Комментарии:
1. спасибо, очень полезно. Это может быть достаточно точным, чтобы использовать float, поэтому я тоже попробую. Я думаю, что таблица поиска была бы слишком неточной.