2015-02-20 8 views
5

Согласно documentation, в math.h есть функция fma(). Это очень приятно, и я знаю, как работает FMA и для чего его использовать. Однако я не уверен, как это реализовано на практике? Меня больше всего интересуют архитектуры x86 и x86_64.Как реализован fma()

Есть ли инструкция с плавающей точкой (не вектор) для FMA, возможно, как определено в IEEE-754 2008?

Используется ли инструкция FMA3 или FMA4?

Есть ли внутренняя цель, чтобы убедиться, что используется реальная FMA, когда на точность полагаются?

+2

В x86 и x86_64 gcc испускает инструкции fma, если ему разрешено ('-mfma' или' -mfma4' или '-march = something', где' something' является fma-поддерживающим процессором). В Linux вы можете посмотреть 'sysdeps/ieee754/dbl-64/s_fma.c' в glibc, чтобы получить представление о том, как выглядит резервная функция библиотеки. – tmyklebu

ответ

7

Фактическая реализация зависит от платформы, но говорить очень широко:

  • Если вы сообщите ваш компилятор для целевой машины с инструкциями аппаратного FMA (PowerPC, ARM с VFPv4 или AArch64, Intel Haswell или AMD Bulldozer и далее), компилятор может заменить вызовы на fma(), просто отбросив соответствующую инструкцию в ваш код. Это не гарантируется, но в целом это хорошая практика. В противном случае вы получите вызов в математическую библиотеку и:

  • При работе на процессоре с аппаратным FMA эти инструкции должны использоваться для реализации функции. Однако, если у вас установлена ​​более старая версия вашей операционной системы или более старая версия математической библиотеки, она может не воспользоваться этими инструкциями.

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

  • Результат функции fma() всегда должен быть правильно закруглен (т. Е. «Реальная fma»). Если это не так, это ошибка в математической библиотеке вашей системы. К сожалению, fma() является одной из наиболее сложных функций математической библиотеки для правильной реализации, поэтому многие реализации имеют ошибки. Пожалуйста, сообщите об этом поставщику вашей библиотеки, чтобы они исправлены!

Есть ли внутренняя, чтобы убедиться, что реальный FMA используется, когда точность полагаться?

Учитывая хороший компилятор, это не обязательно; достаточно использовать функцию fma() и сообщить компилятору, какую архитектуру вы планируете использовать. Однако компиляторы не идеальны, поэтому вам может понадобиться использовать _mm_fmadd_sd() и связанные с ними функции на x86 (но сообщать об ошибке вашему поставщику компилятора!)

+2

«Возможность объяснять кругосветное походит на тур де Франс: один ждет его в течение долгого времени, и он быстро проходит». –

+0

@PascalCuoq IEEE-754 использует раунд даже по умолчанию, если я не ошибается. Почему круглый к нечетному релевантному в этом контексте? В настоящее время я внедряю библиотеку с несколькими точками, поэтому я немного знаком с внутренними разработками, но я не слышал о том, чтобы раунд был нечетным, особенно важным. Очень поэтично, молодцы! –

+0

@theswine Если у вас есть формат с удвоенной шириной FMA, на которую вы стремитесь, вы можете сделать умножение без ошибок. Скажем, что вы реализуете 'fmaf' с двойной точностью' double'. У вас остается проблема добавления значения 'double'' (double) a * (double) b' и 'float c' и округлить это дополнение до ближайшего' float'. Эта операция обычно недоступна, но может быть реализована как добавление «double» в round-to-odd, а затем округление от «double» до «float» в раунде до ближайшего. Невозможность использования округлых значений для промежуточного результата приводит к проблемам с двойным округлением. –

3

Один из способов реализации FMA в программном обеспечении - это разделение значительных на высокие и бит.Я использую Dekker's algorithm

typedef struct { float hi; float lo; } doublefloat; 
doublefloat split(float a) { 
    float t = ((1<<12)+1)*a; 
    float hi = t - (t - a); 
    float lo = a - hi; 
    return (doublefloat){hi, lo}; 
} 

После того, как вы разделяете поплавок можно вычислить a*b-c с одним округлением, как этот

float fmsub(float a, float b, float c) { 
    doublefloat as = split(a), bs = split(b); 
    return ((as.hi*bs.hi - c) + as.hi*bs.lo + as.lo*bs.hi) + as.lo*bs.lo; 
} 

В основном это вычитает c от (ahi,alo)*(bhi,blo) = (ahi*bhi + ahi*blo + alo*bhi + alo*blo).

Я получил эту идею от функции twoProd в статье Extended-Precision Floating-Point Numbers for GPU Computation и от функции mul_sub_x в Agner Fog's vector class library. Он использует другую функцию для расщепления векторов поплавков, которые расщепляются по-разному. Я попытался воспроизвести скалярную версию здесь

typedef union {float f; int i;} u; 
doublefloat split2(float a) { 
    u lo, hi = {a}; 
    hi.i &= -(1<<12); 
    lo.f = a - hi.f; 
    return (doublefloat){hi.f,lo.f}; 
} 

В любом случае с помощью split или split2 в fmsub хорошо согласуется с fma(a,b,-c) из математической библиотеки в Glibc. По какой-то причине моя версия значительно быстрее, чем fma, за исключением машины, имеющей аппаратное обеспечение (в этом случае я использую _mm_fmsub_ss).

+0

Хорошие ссылки. Я знаю работу Шевчука и Священника. В этом вопросе меня больше интересовало, какие инструкции имеются в текущих наборах инструкций. Я думаю, '_mm_fmadd_ss' в значительной степени подводит итог. –

+0

Ваша версия может быть быстрее, поскольку она не обрабатывает специальные числа (особенно бесконечности). Возможно, я ошибаюсь, но кажется, что multiply/add with infinity заставит алгоритм Деккера генерировать NaN. Я ожидал бы, что время выполнения будет корректно вести себя, следовательно, штраф скорости. –

+0

Для набора x86 существует гораздо больше, чем '_mm_fmadd_ss' (и' _mm_fmadd_ps' мне все же интересно), если вы хотите, чтобы все они переходили на [IntrinsicsGuide] (https://software.intel.com/sites/посадочная машина/IntrinsicsGuide /), а в разделе Технологии выберите FMA. –

2

Условное обозначение FMA Boson по алгоритму Деккера, к сожалению, неверно. В отличие от двухпроцессов Деккера, в более общем случае FMA величина c неизвестна относительно термов продукта, и, следовательно, могут иметь место неправильные отмены.

Итак, хотя два продукта Dekker могут быть значительно ускорены с помощью аппаратного FMA, вычисление суммы ошибок для двух продуктов Dekker составляет , а не - надежная реализация FMA.

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

+0

Заметьте, что он делает 'fmsub'. Предполагая, что величины положительные, я бы сказал, что его реализация работает. Во всяком случае, яркий комментарий от кого-то с 11 xp, хорошая работа. –

+1

Да, нет, ты прав. Если 'c' очень мал, то он затухает округлением при вычитании из' ahi * bhi', и это совсем не помогает. Ему нужно будет сформировать более длинное расширение и начать добавлять из самого маленького элемента, используя, по существу, то, что известно как суммирование Кахана.Несмотря на то, что результат округляется до плавания, это упорядочение по-прежнему имеет значение, поскольку оно может повлиять на направление округления. –

+0

Я написал краткое замечание о сумме Кахана, не вполне достаточной здесь, а затем понял, что вы действительно имели в виду делать _both_, сортируя ввод по величине, а затем добавляя с суммой Кахана. Я полностью согласен с тем, что комбинация даст корректно округленный результат FMA , – aki

 Смежные вопросы

  • Нет связанных вопросов^_^