Подтвердить что ты не робот

У меня проблема с производительностью Java, которую я не понимаю

Я написал немного кода, чтобы сделать многомерный массив, а не массив массивов, чтобы я мог сохранить некоторую память. Затем я провел несколько тестов, чтобы сравнить скорость с обычным массивом массивов Java (int [] []), поскольку я не хочу, чтобы моя программа работала медленнее, даже если она сохраняет некоторую память. То, что я видел в тестах времени, меня смутило. Вот типичные результаты для тестового прогона. Время для одного и того же кода. Обратите внимание, как последние два намного больше, чем первые четыре.

время: 58343722 нс
времени: 59451156 нс
время: 51374777 нс

время: 61777424 нс
время: 813156695 нс
время: 782140511 ns

Теперь первое, что я подумал, это то, что сборщик мусора, что пинает. Я поднял ограничение памяти до 5 ГБ (-Xmx5g), чтобы сборщик мусора вызывающе не запустился. Ничего не изменилось. Я перемещал вещи вокруг, но шаблон остается.

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

Я нашел одно изменение, которое приведет к таким результатам:

время: 58729424 нс
времени: 59965426 нс
время: 51441618 нс

время: 57359741 нс
время: 65362705 нс
время: 857942387 ns

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

Честно говоря, я в замешательстве. Я не могу объяснить это поведение. Может кто-то пролил свет на то, что происходит?

Вот код:

package multidimensionalarraytests;

import java.lang.reflect.Array;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MultidimensionalArrayTests {
    static ArrayInt2Dv1 array=new ArrayInt2Dv1(10000,10000);

    public static void main(String[] args) {
        System.out.println("ignore the warmup");
        test();
        test();
        combined();
        combined();

        System.out.println("running tests");
        test();
        test();
        test();
        System.out.println();
        combined();
    }

    static long test(){
        int value=1;
        long start,stop,time;

        System.out.print("time: ");
        start=System.nanoTime();
        for(int x=0;x<array.length1;x++){
            for(int y=0;y<array.length2;y++){
                array.set(x, y, value);
                value=array.get(x, y);
            }   
        }
        stop=System.nanoTime();
        time=(stop-start);
        System.out.println(time+" ns");
        return time;
    }

    static void combined(){
        int value=1;
        long start,stop,time;

        System.out.print("time: ");
        start=System.nanoTime();
        for(int x=0;x<array.length1;x++){
            for(int y=0;y<array.length2;y++){
                array.set(x, y, value);
                value=array.get(x, y);
            }   
        }
        stop=System.nanoTime();
        time=(stop-start);
        System.out.println(time+" ns");

        //try {Thread.sleep(1);} catch (InterruptedException ex) {}

        System.out.print("time: ");
        start=System.nanoTime();
        for(int x=0;x<array.length1;x++){
            for(int y=0;y<array.length2;y++){
                array.set(x, y, value);
                value=array.get(x, y);
            }   
        }
        stop=System.nanoTime();
        time=(stop-start);
        System.out.println(time+" ns");

        //try {Thread.sleep(60000);} catch (InterruptedException ex) {}

        System.out.print("time: ");
        start=System.nanoTime();
        for(int x=0;x<array.length1;x++){
            for(int y=0;y<array.length2;y++){
                array.set(x, y, value);
                value=array.get(x, y);
            }   
        }
        stop=System.nanoTime();
        time=(stop-start);
        System.out.println(time+" ns");     
    }
}

и

package multidimensionalarraytests;

public class ArrayInt2Dv1 {
    int [] array;

    public final int length1;
    public final int length2;

    public ArrayInt2Dv1(int length1, int length2){
        this.length1=length1;
        this.length2=length2;
        array=new int[length1*length2];
    }

    public int get(int x,int y){
        return array[x*length2+y];
    }

    public void set(int x,int y,int value){
        array[x*length2+y]=value;
    }
}

--- Edit ---

Вывод в Windows 7 с параметрами -Xms5g -Xmx5g -XX: + PrintCompilation -verbose: gc -XX: CICompilerCount = 1 -Xbatch

time:     299    1    b        multidimensionalarraytests.ArrayInt2Dv1::set (15 bytes)
    302    2    b        multidimensionalarraytests.ArrayInt2Dv1::get (14 bytes)
    303    1 %  b        multidimensionalarraytests.MultidimensionalArrayTests::test @ 31 (114 bytes)
    358    1 %           multidimensionalarraytests.MultidimensionalArrayTests::test @ -2 (114 bytes)   made not entrant
60671451 ns
    359    3    b        multidimensionalarraytests.MultidimensionalArrayTests::test (114 bytes)
time:     365    2 %  b        multidimensionalarraytests.MultidimensionalArrayTests::test @ 31 (114 bytes)
58104484 ns
time:     425    3 %  b        multidimensionalarraytests.MultidimensionalArrayTests::combined @ 31 (330 bytes)
69008251 ns
time: 806898159 ns
time: 845447596 ns
   2146    4    b        multidimensionalarraytests.MultidimensionalArrayTests::combined (330 bytes)
time: 52493169 ns
time: 804304528 ns
time: 845500191 ns
running tests
time: 51290771 ns
time: 51922285 ns
time: 51264108 ns

time: 52258679 ns
time: 842229025 ns
time: 871403625 ns

В Linux (Ubuntu на VirtualBox на том же компьютере) с теми же параметрами:

    283   1   b   java.lang.String::hashCode (60 bytes)
    285   2   b   sun.nio.cs.UTF_8$Encoder::encodeArrayLoop (490 bytes)
    287   3   b   java.lang.String::charAt (33 bytes)
    287   4   b   java.lang.String::indexOf (151 bytes)
    297   5   b   java.io.UnixFileSystem::normalize (75 bytes)
   2850   6   b   java.lang.String::lastIndexOf (156 bytes)
ignore the warmup
time:    5885   7   b   multidimensionalarraytests.ArrayInt2Dv1::set (15 bytes)
   5948   8   b   multidimensionalarraytests.ArrayInt2Dv1::get (14 bytes)
   5949   1%  b   multidimensionalarraytests.MultidimensionalArrayTests::test @ 31 (114 bytes)
11529998483 ns
  17565   9   b   multidimensionalarraytests.MultidimensionalArrayTests::test (114 bytes)
time: 1619622928 ns
time:   19718   2%  b   multidimensionalarraytests.MultidimensionalArrayTests::combined @ 31 (330 bytes)
475786382 ns
time: 288586857 ns
time: 315560700 ns
  20789  10   b   multidimensionalarraytests.MultidimensionalArrayTests::combined (330 bytes)
time: 460577230 ns
time: 311525066 ns
time: 312343429 ns
running tests
time: 310261854 ns
time: 298826592 ns
time: 304689920 ns

time: 315416579 ns
time: 299473245 ns
time: 290211350 ns
4b9b3361

Ответ 1

Попробуйте -XX:+PrintCompilation Это должно показать, что весь метод оптимизирован после того, как первый цикл повторяет 10000 раз. Проблема в том, что цикл второй/трети оптимизирован без статистической/встречной информации. Иногда это не имеет значения, иногда более поздние циклы намного медленнее, и если вы меняете порядок циклов, более поздние петли улучшаются, а первый цикл становится медленнее.

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