Я профилировал свою программу, и она тратит 20% процессорного времени, в основном оценивая следующее выражение:
abs(x) > abs(y)
где x, y — переменные с плавающей запятой двойной точности.
Есть ли способ реорганизовать выражение в более быстрый вариант?
Следующая строка (вызывается в двух разных местах) занимает около 10% процессорного времени в каждой строке:
(Это фрагмент из функции Image_3 :: TestGradientAtPoint)
if (abs(maxx[ch]) < abs(a)) maxx[ch] = a;
01187AC9 mov eax,dword ptr [ch]
01187ACC sub esp,8
01187ACF fld qword ptr [ebp+eax*8-68h]
01187AD3 fstp qword ptr [esp]
01187AD6 call abs (11305F9h)
01187ADB fld qword ptr [ebp-70h]
01187ADE fstp qword ptr [esp]
01187AE1 fstp qword ptr [ebp-0F8h]
01187AE7 call abs (11305F9h)
01187AEC add esp,8
01187AEF fcomp qword ptr [ebp-0F8h]
01187AF5 fnstsw ax
01187AF7 test ah,41h
01187AFA jne Image_3::TestGradientAtPoint+176h (1187B06h)
01187AFC mov eax,dword ptr [ch]
01187AFF fld qword ptr [ebp-70h]
01187B02 fstp qword ptr [ebp+eax*8-68h]
Профилировщик заявил, что вызов abs () занял 20% процессорного времени.
Я вызываю метод порядка 10 ^ 8 итераций — я работаю с большими изображениями.
Я забыл это сказать, но код работает в режиме отладки, и мне нужно немного его оптимизировать, потому что я все еще хочу использовать отладчик MSVC в разумные сроки.
Это не может быть быстрее, но если арифметические выражения вычисляются быстрее:
if ((x - y) * (-x - y) < 0)
// then abs(x) > abs(y)
Я полагаю, что это фиксирует число выражений в 3 (арифметическое выражение 2 и сравнение с нулем), а не в выражении 3 из метода abs (каждый abs проверяет наличие отрицательного значения, инвертирует знак, в противном случае просто возвращает значение, затем сравнивает каждый abs)
РЕДАКТИРОВАТЬ:
Как и сказал Андре, вы всегда можете явным образом выровнять поплавки. Имеет гораздо больше смысла в ретроспективе.
if (x * x > y * y)
// then abs(x) > abs(y)
Так как (х-у) (- х-у) = у ^ 2-х ^ 2
Скажите вашему компилятору для оптимизации. В GCC или Clang вы делаете это с помощью -O2
или же -O3
флаги — последний более агрессивен. В MSVC вы можете использовать /O2
или же /Ox
флаги (IIRC; я редко использую этот компилятор). Вы не можете ожидать, что 100000000 итераций будут выполняться быстро без включенных оптимизаций.
Если вы хотите выполнить отладку без включенной оптимизации, но в разумные сроки, попробуйте меньший набор данных; или, как упомянул Mysticial, отладка с включенными оптимизациями и принятие произвольно изменяющихся значений и других непонятных наблюдений в вашем отладчике.
Хорошо, если порядок maxx[]
это не важно, вы могли бы отсортировать его, и я думаю, что это будет быстрее.
Другое дело, что если «а» это то же самое для всех maxx[]
ты мог бы сделать a= abs(a);
а затем просто сравнить с напрямую.
Мне нужно увидеть больше кода, чтобы попытаться помочь вам больше.
Первое, что нужно проверить, это то, что вы на самом деле включаете оптимизацию. Если нет, возможно, ваш компилятор не вставляет вызов, что приводит к достаточным накладным расходам, чтобы вы это заметили.
Если, как я подозреваю, у вас на самом деле включена оптимизация, вам придется использовать алгоритмический подход. Я не могу придумать, что ты можешь сделать abs
чтобы сделать это быстрее.
Так что вам нужно учитывать такие вещи, как:
abs
если это?abs
неизменного значения много раз в цикле?Это не может быть быстрее, но другая логическая версия будет:
// logical replacement for abs(x) > abs(y)
x >= 0 ?
y >= 0 && x > y :
y <= 0 && x < y ;
Так как он использует только компараторы и ответвления, может Быстрее, но без гарантий …
Если нет, попробуйте использовать fabs
вместо этого, поскольку он предназначен для чисел с плавающей точкой.