производительность — ускорение двойного абсолютного значения в переполнении стека

Я профилирую свой код и оптимизировал все, что мог, переходя к функции, которая выглядит примерно так:

double func(double a, double b, double c, double d, int i){
if(i > 10 && a > b || i < 11 && a < b)
return abs(a-b)/c;
else
return d/c;
}

Он вызывается миллионы раз во время выполнения программы, и профилировщик показывает мне, что ~ 80% всего времени уходит на вызовы abs(),

  1. Я заменил abs() с fabs() и это дало примерно 10% ускорения, что не имеет особого смысла для меня, поскольку я много раз слышал, что они одинаковы для чисел с плавающей запятой и abs() следует использовать всегда. Это неправда или я что-то упустил?

  2. Каков был бы самый быстрый способ оценить абсолютное значение для двойного, что могло бы еще больше улучшить производительность?

Если это имеет значение, я использую g++ на Linux X86_64.

3

Решение

Сделайте все 3 вычисления. Вставьте результат в массив из 3 элементов. Используйте не ветвящуюся арифметику, чтобы найти правильный индекс массива. Верните этот результат.

То есть,

bool icheck = i > 10;
bool zero = icheck & (a > b);
bool one = !icheck & (b > a);
bool two = !zero & !one;
int idx = one | (two << 1);
return val[idx];

куда val содержит результат трех вычислений. Использование & вместо && это важно.

Это устраняет проблемы предсказания вашей ветви. Наконец, убедитесь, что циклический код может видеть реализацию, так что издержки вызова могут быть устранены.

6

Другие решения

Интересный вопрос.

double func(double a, double b, double c, double d, int i){
if(i > 10 && a > b || i < 11 && a < b)
return abs(a-b)/c;
else
return d/c;
}

Первые мысли таковы:

  • где «встроенный» классификатор?
  • есть большой потенциал для ошибочного прогнозирования отрасли, и
  • много логических оценок короткого замыкания.

Я собираюсь предположить, что a никогда не равно b — мой инстинкт инстинкта состоит в том, что есть 50% -ная вероятность, что это верно для вашего набора данных, и это допускает некоторые интересные оптимизации. Если это не правда, то мне нечего предположить, что Якк этого не сделал.

double amb = a - b;
bool altb = a < b; // or signbit(amb) if it proves faster for you
double abs_amb = (1 - (altb << 1)) * amb;
bool use_amb = i > 10 != altb;
return (use_amb * abs_amb + !use_amb * d) / c;

Одна из целей, о которых я помнил при структурировании работы, состояла в том, чтобы обеспечить некоторый параллелизм в конвейере выполнения процессора; это можно проиллюстрировать так:

amb    altb    i > 10
\  /    \     /
abs_amb  use_amb
\  /      \
use_amb*abs_amb  !use_amb*d
\    /
+ /c
4

Вы пытались развернуть, если так:

double func(double a, double b, double c, double d, int i){
if(i > 10 && a > b)
return (a-b)/c;
if (i < 11 && a < b)
return (b-a)/c;
return d/c;
}
1

Я бы посмотрел на сборку, созданную с помощью вызова fabs (). Это может быть накладные расходы при вызове функции. Если это так, замените его встроенным раствором. Если действительно стоит проверить абсолютное значение, это дорого, попробуйте поразрядно и (&) с битовой маской, которая равна 1 везде, кроме знакового бита. Я сомневаюсь, что это будет дешевле, чем то, что генерирует fabs () производителя стандартной библиотеки.

0
По вопросам рекламы [email protected]