Чем реально «разогревать» потоки при многопоточной обработке?

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

Код (от Питера Лоури), иллюстрирующий это, выглядит следующим образом:

for (int i = 0; i < 20; i++) {
    ExecutorService es = Executors.newFixedThreadPool(1);
    final double[] d = new double[4 * 1024];
    Arrays.fill(d, 1);
    final double[] d2 = new double[4 * 1024];
    es.submit(new Runnable() {
    @Override
    public void run() {
        // nothing.
    }
    }).get();
    long start = System.nanoTime();
    es.submit(new Runnable() {
    @Override
    public void run() {
        synchronized (d) {
            System.arraycopy(d, 0, d2, 0, d.length);
        }
    }
    });
    es.shutdown();
    es.awaitTermination(10, TimeUnit.SECONDS);
    // get a the values in d2.
    for (double x : d2) ;
    long time = System.nanoTime() - start;
    System.out.printf("Time to pass %,d doubles to another thread and back was %,d ns.%n", d.length, time);
}

Результаты:

Time to pass 4,096 doubles to another thread and back was 1,098,045 ns.
Time to pass 4,096 doubles to another thread and back was 171,949 ns.
 ... deleted ...
Time to pass 4,096 doubles to another thread and back was 50,566 ns.
Time to pass 4,096 doubles to another thread and back was 49,937 ns.

т.е. он становится быстрее и стабилизируется около 50 нс. Почему это?

Если я запущу этот код (20 повторений), затем выполню что-то еще (скажем, постобработку предыдущих результатов и подготовку к очередному раунду многопоточности) и потом выполню тот же Runnable на том же ThreadPool еще 20 повторов, он уже будет разогретым , в любом слючае?

В моей программе я выполняю Runnable только в одном потоке (на самом деле по одному на каждое процессорное ядро, которое у меня есть, это программа с интенсивным использованием ЦП), а затем поочередно много раз выполняю другую последовательную обработку. Кажется, что программа не становится быстрее. Может быть, я мог бы найти способ согреться…


person ursoouindio    schedule 04.03.2011    source источник


Ответы (2)


Не потоки так сильно разогреваются, как JVM.

JVM имеет то, что называется компиляцией JIT (Just In Time). Во время работы программа анализирует, что происходит в программе, и оптимизирует ее на лету. Он делает это, беря байт-код, который запускает JVM, и преобразовывая его в собственный код, который работает быстрее. Это можно сделать оптимальным для текущей ситуации способом, поскольку он делает это путем анализа фактического поведения во время выполнения. Это может (не всегда) привести к отличной оптимизации. Даже больше, чем некоторые программы, которые компилируются в нативный код без таких знаний.

Вы можете прочитать немного больше на http://en.wikipedia.org/wiki/Just-in-time_compilation

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

person rfeak    schedule 04.03.2011
comment
Спасибо за такое объяснение, @rfeak. Но как вы думаете, сможет ли компилятор оптимизировать мою программу? (пожалуйста, прочитайте последний абзац, который я добавил к вопросу) - person ursoouindio; 04.03.2011
comment
JIT-компилятор может сделать очень много и влияет только на процессорное время. Если ваш последовательный процесс включает какие-либо операции ввода-вывода, компилятор мало что может сделать. Я бы предложил профилировать вашу программу, чтобы увидеть, где тратится время, а затем атаковать самые большие узкие места там, если вам нужно больше производительности. - person rfeak; 04.03.2011
comment
на самом деле, у него нет IO. Я задаю начальные условия и программа работает сама. Я решаю некоторые специальные дифференциальные уравнения. - person ursoouindio; 05.03.2011
comment
@usoouindio - если это связано только с ЦП, JIT-компилятор должен показать некоторое преимущество. Тем не менее, я остаюсь при своем предложении. Если вам нужно больше скорости, используйте профилировщик, чтобы найти узкие места и атаковать их. - person rfeak; 07.03.2011

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

  • Диспетчер памяти может повторно использовать уже выделенное пространство объекта (например, чтобы выделение кучи заполнило доступную память до тех пор, пока не будет достигнуто максимальное количество памяти — свойство Xmx)

  • Рабочий набор доступен в аппаратном кэше

  • Повторяющиеся операции могут создавать операции, которые компилятор может упростить для оптимизации выполнения.

person Johan Sjöberg    schedule 04.03.2011
comment
Эти причины не зависят от Java или другого языка? - person ursoouindio; 04.03.2011
comment
И да и нет. Многие языки программирования повторно используют объектное пространство для перераспределения, в то время как оптимизация JIT (компиляция точно в срок) специфична в основном для языков JVM/.NET. Однако аппаратный кеш является общим для всех платформ. - person Johan Sjöberg; 04.03.2011