Каковы преимущества точности и производительности использования Math.fma?
Я заметил только существование Math.fma(a, b, c)
в Java 9, которое вычисляет a*b + c
(для double
и float
значения).
Возвращает комбинированное добавление трех аргументов; то есть возвращает точный продукт первых двух аргументов, суммированных с третьим аргументом, а затем округленный один раз до ближайшего float. Округление выполняется с округлением до ближайшего режима округления. Напротив, если a * b + c оценивается как регулярное выражение с плавающей запятой, задействованы две ошибки округления, первая для операции умножения, вторая для операции сложения.
Итак, похоже, что улучшает точность, делая 1 округление вместо 2. Правильно ли это? Это обусловлено возможностями ЦП, или мы можем рассчитывать на это всегда?
Я предполагаю, что это может быть реализовано с использованием специальных инструкций CPU. Это так? И если да, можем ли мы ожидать преимуществ производительности? Мне интересно узнать о фактических преимуществах с текущими платформами/процессорами, а также о гипотетических будущих преимуществах.
Изменить (пытаясь сделать его немного менее широким): я не забочусь о очень подробных ответах: да/нет, чтобы несколько элементов, чтобы исправить/подтвердить мое понимание, плюс несколько указателей, было бы достаточно для меня, чтобы отметить ответ, как принято. Меня действительно интересуют как аспекты точности, так и производительности,
и я думаю, что они идут вместе...
Ответы
Ответ 1
Да, FMA повышает точность по той причине, о которой вы говорили.
JVM использует инструкции CPU FMA, если они доступны. Однако FMA недоступен повсюду. Например, процессоры Intel x86 до Haswell этого не имеют. Это означает, что большинство процессоров Intel в настоящее время не имеют FMA.
Если CPU FMA недоступен, Java использует медленное решение очень: оно выполняет FMA с использованием java.math.BigDecimal
(то есть текущего решения - оно может измениться в будущем, но я уверен, что это будет всегда медленнее по сравнению с CPU FMA).
Ответ 2
Я нахожусь на mac с 5-м поколением i7. Когда я это сделаю:
sysctl -n machdep.cpu.brand_string
Я вижу, что мой процессор Intel(R) Core(TM) i7-5557U CPU @ 3.10GHz
и что cu поддерживает FMA
, вы можете видеть, что:
sysctl -a | grep machdep.cpu | grep FMA
и в результате я получаю строку, в которой присутствует эта строка. Теперь посмотрим, использует ли JVM это.
Эти методы (один для double
и один для float
) аннотируются с помощью @HotSpotIntrinsicCandidate
, что означает, что JIT
может заменить их фактическими инструкциями процессора, если они доступны, но это будет означать, что метод должен быть достаточно горячим - называется несколько раз и что JVM-зависимая вещь.
Я пытаюсь имитировать это с помощью:
public static void main(String[] args) {
double result = 0;
for (int i = 0; i < 50_000; ++i) {
result = result + mine(i);
}
System.out.println(result);
}
private static float mine(int x) {
return Math.fma(x, x, x);
}
И я запустил это с помощью:
java -XX:+UnlockDiagnosticVMOptions
-XX:+PrintInlining
-XX:+PrintIntrinsics
-XX:CICompilerCount=2
-XX:+PrintCompilation
org.so/FMATest
Там будет куча строк, но один из них:
@ 6 java.lang.Math::fma (12 bytes) (intrinsic)
Это означает, что JVM действительно использовал встроенный метод для инструкций FMA.