У меня такой код
петля 10 M:
if( fz != 0.0)
{
fhx += hx/fz;
}
это называется 10 М раз в цикле, нужно быть очень быстрым - я просто должен поймать случай, когда fz не равен нулю, а не делать div с нулевой ошибкой, но это очень редкий случай, действительно, в 10-мегабитных случаях это должно быть ноль, я не знаю один, два или более
могу ли я каким-то образом избавиться от этого 10M ifs и использовать "nan/inf" или, может быть, поймать исключение и продолжить? (если fz равно нулю, мне нужно fhx + = 0.0, я не имею в виду ничего, что только можно продолжить? Возможно ли/эффективно помещать исключение fpu или inf в работу?
(Im, использующий c++/mingw32)
Вы можете, но это, вероятно, не так уж полезно. Маскировка не будет полезна ни при каких обстоятельствах.
Исключения очень медленные, когда они случаются, сначала много микрокодированных сложных вещей должно произойти до того, как процессор даже войдет в обработчик исключений на уровне ядра, а затем он должен передать его процессу сложным и медленным способом. С другой стороны, они ничего не стоят, когда их не бывает.
Но сравнение и ветвь на самом деле ничего не стоят и до тех пор, пока ветвь предсказуема, а отрасль, которая по существу никогда не берется, - это. Конечно, это требует небольшой пропускной способности, чтобы заставить их вообще произойти, но они не находятся в критическом пути. Но даже если бы они были, настоящая проблема здесь была делением на каждой итерации.
Пропускная способность этого деления составляет 1 на 14 циклов в любом случае (на Хасуэлл - хуже на других μarchs), если только fz
является особенно "приятным", и даже тогда он 1 на 8 циклов (опять же на Хасуэле). На 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 нет никакой разницы.