быстрее Math.exp () через JNI? - PullRequest
       24

быстрее Math.exp () через JNI?

10 голосов
/ 16 сентября 2008

Мне нужно очень часто вычислять Math.exp() из Java, возможно ли заставить собственную версию работать быстрее, чем Java Math.exp() ??

Я пробовал только jni + C, но это медленнее, чем просто java .

Ответы [ 15 ]

15 голосов
/ 08 января 2009

Это уже запрашивалось несколько раз (см., Например, здесь ). Вот приблизительное значение Math.exp (), скопированное из этой записи в блоге :

public static double exp(double val) {
    final long tmp = (long) (1512775 * val + (1072693248 - 60801));
    return Double.longBitsToDouble(tmp << 32);
}

Это в основном то же самое, что таблица поиска с 2048 записями и линейной интерполяцией между записями, но все это с помощью трюков с плавающей запятой IEEE. Это в 5 раз быстрее, чем Math.exp () на моей машине, но это может сильно измениться, если вы скомпилируете с -server.

12 голосов
/ 16 сентября 2008

+ 1 для написания собственной реализации exp (). То есть, если это действительно узкое место в вашем приложении. Если вы можете справиться с небольшой неточностью, существует ряд чрезвычайно эффективных алгоритмов оценки показателей, некоторые из которых датируются столетиями. Насколько я понимаю, реализация Java exp () довольно медленная, даже для алгоритмов, которые должны возвращать «точные» результаты.

Да, и не бойтесь писать эту реализацию exp () на чистом Java. JNI имеет много накладных расходов, и JVM может оптимизировать байт-код во время выполнения, иногда даже сверх того, что C / C ++ способен достичь.

6 голосов
/ 16 сентября 2008

Используйте Java.

Кроме того, кешируйте результаты опыта, и тогда вы сможете искать ответ быстрее, чем вычислять их снова.

5 голосов
/ 16 сентября 2008

Вы хотели бы также обернуть любой вызов цикла Math.exp() в Си. В противном случае издержки маршаллинга между Java и C сократят любое преимущество в производительности.

3 голосов
/ 16 сентября 2008

Вы могли бы заставить его работать быстрее, если вы делаете их партиями. Выполнение вызова JNI увеличивает накладные расходы, поэтому вы не хотите делать это для каждого exp (), который вам нужно вычислить. Я бы попробовал передать массив из 100 значений и получить результаты, чтобы посмотреть, помогает ли это производительность.

2 голосов
/ 16 сентября 2008

Настоящий вопрос в том, стало ли это для вас бутылочным горлышком? Вы профилировали свое приложение и обнаружили, что это является основной причиной замедления?

Если нет, я бы порекомендовал использовать версию Java. Старайтесь не выполнять предварительную оптимизацию, поскольку это приведет к замедлению разработки. Вы можете уделять больше времени проблеме, которая может не быть проблемой.

При этом, я думаю, что ваш тест дал вам ваш ответ. Если jni + C медленнее, используйте версию java.

1 голос
/ 31 мая 2014

Commons Math3 поставляется с оптимизированной версией: FastMath.exp(double x). Это значительно ускорило мой код.

Фабьен провел несколько тестов и обнаружил, что он почти в два раза быстрее, чем Math.exp():

 0.75s for Math.exp     sum=1.7182816693332244E7
 0.40s for FastMath.exp sum=1.7182816693332244E7

Вот этот Javadoc:

Вычисляет exp (x), результат функции почти округлен. Он будет правильно округлен до теоретического значения для 99,9% входных значений, в противном случае он будет иметь ошибку 1 UPL.

Метод:

    Lookup intVal = exp(int(x))
    Lookup fracVal = exp(int(x-int(x) / 1024.0) * 1024.0 );
    Compute z as the exponential of the remaining bits by a polynomial minus one
    exp(x) = intVal * fracVal * (1 + z)

Точность: вычисление выполняется с точностью 63 бита, поэтому результат должен быть правильно округлен для 99,9% входных значений, в противном случае ошибка менее 1 ULP.

0 голосов
/ 01 марта 2013

Это может быть неактуально, но, как вы знаете, в новейших выпусках OpenJDK (см. здесь ), Math.exp должен быть встроен (если вы не знаете, что это такое, отметьте здесь ).

Это сделает производительность непревзойденной на большинстве архитектур, поскольку это означает, что виртуальная машина Hotspot заменит вызов Math.exp реализацией exp для конкретного процессора во время выполнения. Вы никогда не сможете побить эти вызовы, так как они оптимизированы для архитектуры ...

0 голосов
/ 16 сентября 2008

Я использую алгоритм подбора, и минимальная ошибка результата подбора намного больше чем точность Math.exp ().

Трансцендентные функции всегда намного медленнее, чем сложение или умножение и хорошо известное узкое место. Если вы знаете, что ваши значения находятся в узком диапазоне, вы можете просто построить справочную таблицу (два отсортированных массива; один вход, один выход). Используйте Arrays.binarySearch, чтобы найти правильный индекс и интерполировать значение с элементами в [index] и [index + 1].

Другой способ - разделить число. Давайте возьмем, например, 3,81 и разделить на 3 + 0,81. Теперь вы умножаете e = 2.718 три раза и получаете 20.08.

Теперь до 0,81. Все значения от 0 до 1 быстро сходятся с известным экспоненциальным рядом

1 + x + x ^ 2/2 + x ^ 3/6 + x ^ 4/24 .... и т. Д.

Возьмите столько терминов, сколько вам нужно для точности; к сожалению, медленнее, если x приближается к 1. Допустим, вы переходите к x ^ 4, тогда вы получите 2.2445 вместо правильного 2.2448

Затем умножьте результат 2.781 ^ 3 = 20.08 на 2.781 ^ 0.81 = 2.2445, и вы получите результат 45,07 с ошибкой в ​​одну часть из двух тысяч (правильно: 45,15).

0 голосов
/ 16 сентября 2008

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

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

Какие ограничения вы можете применить к exp, чтобы получить компромисс производительности?

-Adam

...