Огромная разница в производительности для пустого цикла между Java 32 и 64 бит

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

Но мне нужно, чтобы это заняло больше времени, поэтому я вставил этот код в простое вычисление:

for (int i = 0; i < 1000000; i++)
{
    // Nothing.
}

В течение долгого времени я писал и компилировал код на 64-битной машине и тестировал его на ряде 32-битных машин.

Затем я запустил его на 64-битной машине и заметил огромную разницу в производительности.

С тем же кодом для полностью аналогичной 64 машины требуется <100 мс, что для 32 машины ~52000 мс (2 виртуальные машины на одном хосте).

Я тестировал на Windows и Ubuntu на разных компьютерах, и, используя один и тот же файл.class, я все еще получаю эту огромную разницу между 32 и 64 битами.

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

import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args)
{
    long start = System.currentTimeMillis();
    int j = 2;
    ArrayList<Integer> res = new ArrayList<Integer>();
    for (int k = 0; k < 50000; k++)
    {
        Collection<Integer> partres = work(k);
        if (partres != null)
            res.addAll(work(k));
    }
    long end = System.currentTimeMillis();
    System.out.println("Done in " + (end-start) + " ms.");
}
public static Collection<Integer> work(Integer j) {
    for (int i = 0; i < 1000000; i++)
    {
        // Nothing.
    }
    if (isPrime(j))
    {
        ArrayList<Integer> res = new ArrayList<Integer>();
        res.add(j);
        return res;
    }
    else
        return null;
}
static boolean isPrime(int n) {
    if (n == 2) return true;
    if (n%2==0) return false;
    for(int i = 3; i * i <= n; i += 2) 
        if(n%i==0)
            return false;
    return true;
}
}

И вот файл.class, к которому я его скомпилировал.

Теперь мой вопрос.

Я знаю, что при использовании 64-битной машины наблюдается повышение производительности, но это не объясняет эту огромную разницу. Так кто-нибудь имеет представление, почему это происходит?

2 ответа

Решение

На окнах он будет использовать -client JVM по умолчанию для 32-битных и -server для 64-битной JVM. Серверная JVM более агрессивна в удалении кода, который ничего не делает. например, пустые петли. Вы обнаружите, что такой цикл занимает примерно одинаковое количество времени, независимо от ограничения количества, поскольку он зависит от количества времени, которое требуется для обнаружения и устранения цикла. Попробуйте добавить второй синхронизированный цикл к тому же методу, и вы обнаружите, что это занимает почти нет времени, независимо от того, какое максимальное значение вы установили (при условии, что это не бесконечный цикл). Это потому, что метод будет скомпилирован ко времени второго цикла. начинается.

http://docs.oracle.com/javase/1.5.0/docs/guide/vm/server-class.html

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

64-битная Java всегда использует JIT-компилятор -server, тогда как ваша 32-битная JVM, вероятно, использовала JIT-компилятор -client.

Когда С2 ака. Компилятор -server видит что-то вроде этого:

for (int i = 0; i < 1000000; i++)
{
  // Nothing.
}

Он заметит, что цикл ничего не делает, и удаляет его! Ваш цикл, который ничего не делает, будет оптимизирован в ничто.

Чтобы помешать этой оптимизации, вам придется заставить цикл делать что-то - он может XOR всех тех iНапример, вместе - и использовать результат. Тогда цикл будет выглядеть как настоящая работа для компилятора, и код будет сохранен.

Другие вопросы по тегам