Эффективность арифметики int и float в Java

Я пишу приложение, которое использует алгоритм Дейкстры для поиска минимальных путей в графе. Веса узлов и ребер в графе - это float числа, поэтому алгоритм выполняет много арифметических операций с числами с плавающей запятой. Могу ли я улучшить время бега, если переведу весь вес в ints? Являются ли арифметические операции int в Java быстрее, чем операции с плавающей запятой?

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


РЕДАКТИРОВАТЬ:

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

Моя структура данных - это взвешенный ориентированный граф. Учитывая набор листовых узлов, мне нужно найти наименьшее дерево, которое соединяет эти узлы и показывает ответ пользователю. Веса назначаются весовой функцией, частично основанной на методе tf / idf. Пользователь не знает, какие веса я присваиваю узлам и ребрам, он просто хочет видеть ответы, относящиеся к заданному им запросу. Так что точных результатов не требуется, только возможность перечислить ответы по их весам. Просто собственное использование весовой функции (как я уже упоминал, она основано на tf / idf) дает веса с плавающей запятой, поэтому до сих пор я использовал числа с плавающей запятой.

Я надеюсь, что это добавляет некоторую предысторию вопроса.


person jutky    schedule 28.07.2010    source источник
comment
Я понял, что умножение целых чисел немного быстрее, примерно на 13%, но сравнение двух целых чисел происходит медленнее примерно на 22%.   -  person jutky    schedule 28.07.2010
comment
Я не совсем уверен, но для Дейкстры было бы достаточно просто операций сложения и сравнения. И для этих операций он не должен сильно различаться для float или int. Я действительно удивлен, что целочисленное сравнение будет на 22% медленнее. Могу я узнать, какие тесты вы проводили?   -  person tafa    schedule 28.07.2010
comment
Я создаю массив случайных целых чисел и массив случайных чисел с плавающей запятой, а затем измеряю, сколько времени требуется для выполнения операций сравнения и умножения этих чисел. Может быть, простая итерация по массиву занимает серьезную часть времени, поэтому я хотел узнать, есть ли литература по этой теме.   -  person jutky    schedule 28.07.2010


Ответы (7)


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

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

А что касается оптимизации компилятора - это реальная и действительная часть оптимизации производительности.

Если использование типа A теоретически быстрее, чем использование типа B, но ваш компилятор может оптимизировать тип B, чтобы он был быстрее в реальном сценарии, тогда это ценное свидетельство, а не источник разочарования.

person PaulJWilliams    schedule 28.07.2010
comment
Я просто хотел узнать, стоит ли улучшенная производительность, которую я могу выиграть, потраченного времени, чтобы изменить довольно значительную часть приложения. Но, похоже, я не могу знать это заранее, и лучший способ проверить это - реализовать две версии алгоритма и измерить время работы. - person jutky; 28.07.2010

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

как плавать

float f = 15 * 0.987;

как int

int i = 15 * 987 / 1000;

Дополнительное деление означает, что операция int может занять больше времени.

person Peter Lawrey    schedule 28.07.2010
comment
в алгоритме Дейкстры я просто суммирую и сравниваю вес путей, поэтому операция разделения для меня довольно сложная. Откуда вы знаете, что для простых операций ints быстрее, это обычная сцена, или вы можете указать мне на литературу по этой теме. - person jutky; 28.07.2010
comment
вам нужно посмотреть на собственный код, сгенерированный JVM, и сравнить тактовые циклы. Однако обе операции выполняются довольно быстро по сравнению с затратами на промахи в кэше и системные вызовы. Скорее всего, выбор типа данных не будет иметь большого значения. - person Peter Lawrey; 29.07.2010

На моей машине целочисленное вычитание примерно в 2,5 раза быстрее, чем двойное. Однако целочисленное умножение всего в 1,5 раза быстрее, чем двойное умножение.

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

// test whether int subs are faster than double subs
public void compareIntAndFloatSubtraction(){

    int N = 100000;  // input array size
    int k = 100000;  // number of mathematical operations performed on each element

    // generate random data
    int[] ints = new int[N];
    double[] doubles = new double[N];
    Random r = new Random(1l);
    for (int i = 0; i < N; i++) {
        ints[i] = r.nextInt();
        doubles[i] = r.nextDouble();
    }

    // measure integer subtractions
    long before = System.currentTimeMillis();
    for (int i = 1; i < N; i++) {
        for (int j = 0; j < k; j++) {
            ints[i] -= ints[i-1];  // referring to another element might prevent from optimization also
        }
    }
    System.out.println(String.format("time needed for int subs [ms]: %s", System.currentTimeMillis()-before));

    // measure double subtractions
    before = System.currentTimeMillis();
    for (int i = 1; i < N; i++) {
        for (int j = 0; j < k; j++) {
            doubles[i] -= doubles[i-1];
        }
    }
    System.out.println(String.format("time needed for double subs [ms]: %s", System.currentTimeMillis()-before));

}
person cw'    schedule 11.06.2014
comment
Спасибо за тест. Запуск его для тестов на вычитание на моей машине (Xeon, 64-бит, Windows, Java 1.8) дал: время, необходимое для int subs [мс]: 7704, время, необходимое для двойных подпрограмм [мс]: 10869. Меня больше интересовало большее чем и меньше операций, потому что я тестировал его для использования при построении TreeMap. Результат операций сравнения / * if (doubles [i] ‹doubles [i-1]) tmp ++; if (удваивает [i] ›удваивает [i-1]) tmp ++; * / were: время, необходимое для int cmp [мс]: 8479, время, необходимое для двойного cmp [мс]: 15925. - person Henry; 24.10.2015
comment
К вашему сведению, я снова запустил тест для длинного типа данных. Результат для вычитания: время, необходимое для длинных подпрограмм [мс]: 7513, время, необходимое для двойных подпрограмм [мс]: 10898. И результат для сравнений: время, необходимое для длинных cmp [мс]: 15768, время, необходимое для двойных cmp [ ms]: 16012. Для longs кажется, что производительность аналогична для проверок равенства. - person Henry; 24.10.2015
comment
Вы не можете выполнить тест в одном и том же исполнении для разных типов значений, JVM будет горячим - person Marcos Vasconcelos; 30.08.2017

Если вы просто хотите сравнить веса, вы должны предпочесть int вместо float.

person Truong Ha    schedule 28.07.2010
comment
Тот же комментарий, что и к другому ответу: это обычная сцена, или вы можете указать мне на литературу по этой теме. - person jutky; 28.07.2010

Как правило, вам не следует беспокоиться о выборе между int и float по соображениям производительности.

Вот выдержка из Приложения к Java Puzzlers:

Арифметика с плавающей точкой неточна. Не используйте числа с плавающей запятой там, где требуются точные результаты; вместо этого используйте целочисленный тип или BigDecimal . Предпочитайте double float.

Если у вас нет действительно веской причины, вы обычно должны предпочесть double float, если вам нужно использовать операцию с плавающей запятой. Если желателен точный результат, используйте BigDecimal ; это будет медленнее, поскольку это не примитив, но если профилирование не покажет, что это неприемлемо, часто это лучший вариант.

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

Если вам на самом деле не нужны операции с плавающей запятой, то непременно используйте вместо них int или long.

person polygenelubricants    schedule 28.07.2010
comment
См. Также stackoverflow. com / questions / 2550281 / и stackoverflow.com/questions/2010252/ - person polygenelubricants; 28.07.2010
comment
Я добавил немного предыстории к вопросу. надеюсь, что это проясняет некоторые вещи. Спасибо за ссылки. - person jutky; 28.07.2010

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

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

На других платформах среда выполнения может быть оптимизирована для OpenCL (я не верю, что кто-то делает это прямо сейчас, но это может случиться :). Я понятия не имею, что быстрее всего работает на такой платформе и при каких условиях. Возможно, OpenCL оптимизирован для работы с целыми числами.

В этих обстоятельствах я бы сделал вывод, что на данном этапе нецелесообразно оптимизировать тип данных (float или int), а просто оптимизировать читаемость кода.

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

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

person extraneon    schedule 28.07.2010

Я так не думаю.

Число с плавающей запятой составляет 4 байта. И Int в java тоже 4 байта.

Почему бы не использовать дату (java.util.Date), чтобы узнать время работы?

Вы можете определить граф, который имеет 100000 узлов. Затем рассчитайте это.

person rainisic    schedule 28.07.2010
comment
Возможно, вы захотите использовать слово «байт» вместо «бит». Целое число размером 4 бит может содержать только шестнадцать различных значений ... - person Donal Fellows; 28.07.2010
comment
Извините ... У меня плохой английский, поэтому я использовал неправильное слово (мой родной язык не английский) На самом деле, я думаю, что если скорость выше, чем float, это из-за оборудования. В физике, может быть, проще достичь int, чем float. - person rainisic; 28.07.2010