Шаблон делу не поможет, т.к. после компиляции он выльется всё в тот же сишный код. Тогда как суть дела вот в чем: функции fmin(), fmax() и fma() появились потому, что в AVX/AVX2 появились новые процессорные инструкции, делающие эти операции "за один такт". Эта ситуация аналогична появлению в языке операторов +=, *= и им подобных из-за того, что у процессора были такие команды для работы с памятью. К тому же выражение a+=b+c строго не эквивалентно выражению a=a+b+c, т.к. компилятору запрещено "упростить" второе выражение до первого, поскольку он обязан выполнять сложения слева направо. В этом смысле меня огорчает, что люди забывают про сишные операторы типа +=, ошибочно полагая, что компилятор сам упростит выражения, написанные в духе Бэйсика.
Вот и сейчас, с появлением fma(), появились новые возможности. Например, в реализации функции poly():
double poly( double x, int n, double *coef) // compute polynomial
{
if( !n) return 0;
double y = coef[n];
while(--n >= 0) y = fma(y, x, coef[n]);
return y;
}
При такой реализации цикл крутился бы с максимальной скоростью.
Однако при том важном условии, если fma() реализована на языке, как прямая подстановка кода!
Но если мы возьмем стороннюю библиотеку с функцией fma(), реализованной, как подпрограмма, то это выльется в то, что компилятор будет вынужден сперва залить три числа в параметры вызова (большая удача, если не на стек!), а затем вызвать функцию из библиотеки со всеми сопровождающими такой вызов потерями (запихиванием адреса возврата в стек, переход на другой адрес, а по окончании возврат по адресу из стека с приведением стека в прежнее состояние). При этом реализация poly() сразу же станет настолько неэффективной, что ее быстрее было бы считать без помощи fma().
Практический пример - вычисление функции erfcx() через полином 23-порядка:
x variant 1 variant 2 difference
...
21.0 0.0268358 0.0268358 -6.9042e-016
22.0 0.0256186 0.0256186 -3.81639e-016
23.0 0.0245069 0.0245069 -2.18575e-016
24.0 0.0234875 0.0234875 -1.249e-016
25.0 0.0225496 0.0225496 -7.28584e-017
26.0 0.0216836 0.0216836 -4.51028e-017
100000 раз в цикле:
1) erfcx(x): 8960 ms
2) erfcx(x): 80 ms
Результат вычислений практически одинаковый, но время отличается в 100 раз.