Я получил такой код
петля 10 М:
if( fz != 0.0)
{
fhx += hx/fz;
}
это называется 10 M раз в цикле, это должно быть очень быстро — мне нужно только поймать случай, когда fz не равен нулю, чтобы не делить ошибку на ноль, но это очень редкий случай,
действительно, в 10M случаях он должен быть равен нулю, я не знаю ни разу, ни дважды, ни новее
Могу ли я каким-то образом избавиться от этих 10 миллионов ifs и использовать «nan / inf» или, возможно, перехватить исключение и продолжить? (если fz равно нулю, мне нужно fhx + = 0.0, я имею в виду, что просто продолжить
? Можно / эффективно ли ввести в работу исключение fpu или inf?
(Я использую c ++ / mingw32)
Вы можете, но это, вероятно, не так полезно. Маскировка не будет полезна ни в данных обстоятельствах.
Исключения происходят очень медленно, сначала происходит много сложного микрокодирования, прежде чем процессор даже войдет в обработчик исключений уровня ядра, а затем он должен передать его вашему процессу сложным и медленным способом. С другой стороны, они ничего не стоят, когда они не случаются.
Но сравнение и ветвление на самом деле ничего не стоят, пока ветвь предсказуема, что ветвь, которая по сути никогда не берется является. Конечно, для их реализации требуется небольшая пропускная способность, но они не находятся на критическом пути … но даже если бы они были, настоящая проблема здесь — это разделение в каждой итерации.
Пропускная способность этого деления в любом случае равна 1 на 14 циклов (на Haswell — хуже на других µархах), если только fz
особенно «приятно», и даже тогда это 1 на 8 циклов (снова на Haswell). На Core2 это было больше похоже на 19 и 5, на P4 это было больше похоже (типично для P4) на одно деление на 71 цикл, несмотря ни на что.
Хорошо спрогнозированная ветвь и сравнение просто исчезают в этом. На моем 4770K разница между наличием сравнения и разветвлением там или не исчезла в шуме (возможно, если я буду запускать его достаточно много раз, я в конечном итоге получу статистически значимую разницу, но она будет крошечной), причем оба они выиграют случайным образом о половину времени Код, который я использовал для этого теста, был
global bench
proc_frame bench
push r11
[endprolog]
xor ecx, ecx
mov rax, rcx
mov ecx, -10000000
vxorps xmm1, xmm1
vxorps xmm2, xmm2
vmovapd xmm3, [rel doubleone]
_bench_loop:
imul eax, ecx, -0xAAAAAAAB ; distribute zeroes somewhat randomly
shr eax, 1 ; increase to make more zeroes
vxorps xmm0, xmm0
vcvtsi2sd xmm0, eax
vcomisd xmm0, xmm1 ; #
jz _skip ; #
vdivsd xmm0, xmm3, xmm0
vaddsd xmm2, xmm0
_skip:
add ecx, 1
jnz _bench_loop
vmovapd xmm0, xmm2
pop r11
ret
endproc_frame
Другая функция была такой же, но две строки помечены знаком #.
Версия, которая в конечном итоге последовательно выигрывает при увеличении числа нулей, — это версия с ветвлением, что указывает на то, что деление на ноль значительно медленнее, чем ошибочное прогнозирование ветвления. Это без даже используя механизм исключений для создания видимого программистом исключения, это просто из-за стоимости микрокодирования «странное исправление ситуации». Но у вас не так много нулей, так что
TL; DR на самом деле нет разницы.