Производительность Java Math.tanh () - PullRequest
5 голосов
/ 02 марта 2011

У меня есть Java-программа, которая выполняет много вызовов функции Math.tanh ().Из любопытства я хотел сделать сравнение с C ++.Поэтому я написал две небольшие программы, одну на Java и одну на C ++, для тестирования.

Код Java:

public class TestTanh { 

    public static void main(String[] args) {

        double t1 = -1.0;
        double t2 = 1.0;
        double step = 1e-8;

        double z = 0.0;
        for(double t=t1; t<=t2; t += step) {
            double y = Math.tanh(t);
            z += y;
        }
        System.out.println("Sum = " + z);
    }
}

и код C ++:

#include <iostream>
#include <cmath>

using namespace std;

int main() {

    double t1 = -1.0;
    double t2 = 1.0;
    double step = 1e-8;

    double z = 0.0;
    for(double t=t1; t<=t2; t += step) {
        double y = tanh(t);
        z += y;
    }
    cout << "Sum = " << z << "\n";
}

Компиляция и запуск программ Я получил следующее:

$ time java TestTanh
Sum = -0.41281032759865655

real    0m18.372s
user    0m17.961s
sys     0m0.109s

и

$ time ./test_tanh
Sum = -0.41281

real    0m4.022s
user    0m3.641s
sys     0m0.004s

Почему для выполнения Java-программы требуется примерно в 5 раз больше времени?Может ли это быть связано с тем, что JIT сначала выполняет компиляцию?Или реализация tanh в Java медленнее, чем в C ++?

Это простой тест, который может иметь тривиальное объяснение, но я искал в Интернете и не нашел ответа.Моя версия Java

$ java -version
java version "1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04-307-10M3261)
Java HotSpot(TM) 64-Bit Server VM (build 17.1-b03-307, mixed mode)

При использовании функции tanh в более крупной программе, содержащей другие основные арифметические операции, разница между Java и C ++ стала меньше (теперь около 2,3).Программа все еще вызывает tanh несколько раз, но теперь в цикле есть и другие операции.Я также попробовал класс FastMath из Apache Commons , но он был на самом деле медленнее (нужны какие-то особые настройки?).Результат для этой программы с идентичными параметрами:

C ++

real    0m18.031s
user    0m18.007s
sys     0m0.007s

Java с lang.Math

real    0m40.739s
user    0m40.032s
sys     0m0.088s

Java с org.apache.commons.math.util.FastMath

real    0m46.717s
user    0m46.583s
sys     0m0.372s

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

Ответы [ 5 ]

3 голосов
/ 02 марта 2011

Согласно this , OpenJDK 6 (и, я полагаю, JDK 6 от Sun) использует строгую математику, которая жертвует производительностью ради правильности.Это может быть вашей проблемой.Я уверен, что ни одна приличная JVM не тратит 18 секунд на запуск.Вы должны использовать библиотеку Math с учетом производительности или изменить JVM.

2 голосов
/ 02 марта 2011

Это может происходить или не происходить из-за того, что результат в Java достаточно точно определен.Следующее может стоить времени, если процессор не делает это точно так же:

  • Если аргумент равен нулю, то результатом является ноль с тем же знаком, что и аргумент.
  • Если аргумент имеет положительную бесконечность, то результат равен +1,0.
  • Вычисленный результат должен быть в пределах 2,5 Ульпс от точного результата.

В C вы знаете только, что получается что-то вроде tanh(x).Существуют стандарты, и есть стандартные соответствующие компиляторы, но используете ли вы такой компилятор?

Как сказано в других ответах, это не тот способ, которым следует проводить тесты.Для сравнения только программ на Java я рекомендую вам взять caliper и попробовать с ним.

1 голос
/ 20 марта 2011

Для большинства компиляторов C ++ tanh - это встроенная функция ( встроенная функция ), что означает отсутствие служебных вызовов при вызове функции, поскольку компилятор вместо этого добавляет специальные инструкции ассемблера библиотечного вызова.

Некоторые виртуальные машины Java также поддерживают встроенные функции, например, String.length () кажется встроенным в Sun JVM. Для Java это означает, что компилятор горячей точки заменяет вызов функции специальными инструкциями ассемблера (во время выполнения). Это немного отличается от C и C ++, где это делает компилятор (перед запуском программы).

Однако в Java Math.tanh не кажется внутренним. Поэтому он медленнее.

1 голос
/ 02 марта 2011

При выполнении таких тестов производительности вы всегда должны учитывать «период прогрева». Поэтому вам не следует начинать измерения, прежде чем выполнять вычисления пару сотен или тысяч раз. Таким образом, компилятор Java Hotspot скомпилирует то, что он считает часто выполняемым кодом, а собственный двоичный файл поместит свои наиболее часто используемые переменные в регистры процессора.

Я предполагаю, что почти 100% разницы в вашем результате обусловлено медленным временем запуска JVM. Java использует возраст для запуска по сравнению с нативно скомпилированной программой.

Было бы интересно увидеть измерение, фактически выполненное в коде после "периода прогрева".

0 голосов
/ 02 марта 2011

Основной метод вызывается только один раз, поэтому JVM может не скомпилировать его в собственный код. Прочтите вначале Как написать правильный микро-тест на Java?

Если после того, как микро-бенчмарк хорошо написан, разница все еще велика, то возможной причиной является JNI накладные расходы. Метод Math.tanh() и другие были реализованы в JVM как собственный код. В документации для java.lang.StrictMath говорится, что они используют библиотеку fdlibm , написанную на C, так что вы можете хорошо использовать эту библиотеку в своих тестах, чтобы вы не будет сравнивать две разные библиотеки C.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...