Коэффициент полезного действия 10 при удвоении мощности

#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, поэтому я тоже попробую. Я думаю, что таблица поиска была бы слишком неточной.