почему код kotlin так долго при первом выполнении [duplicate]

50
задан Tim B 17 January 2014 в 15:27
поделиться

6 ответов

Это классическая проблема бенчмаркинга Java. Hotspot / JIT / etc будет компилировать ваш код во время его использования, поэтому он будет быстрее во время выполнения.

Сначала запустите цикл по крайней мере 3000 раз (10000 на сервере или на 64-битной основе) - затем сделайте свои измерения.

62
ответ дан Tim B 18 August 2018 в 14:36
поделиться
  • 1
    @Santosh Опция, которую вы ищете, --XX: CompileThreshold Никогда не устанавливайте ее на 0 или 1. Это количество вызовов методов после того, как метод подходит для JIT. По умолчанию это 3000, а опция -server отменяет ее до 10000 – gyorgyabraham 18 December 2013 в 17:12
  • 2
    Из документов Oracle кажется, что значение по умолчанию для клиента равно 1500, возможно, 3000 было пересмотрено в некоторой новой версии :) Также, когда вы используете 64-битную JRE, параметр сервера является неявным значением по умолчанию – gyorgyabraham 18 December 2013 в 17:43

Скорее всего, код все еще компилировался или еще не скомпилирован в момент запуска первого цикла.

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

Чтение: Динамическое компиляция и измерение производительности .

6
ответ дан Boann 18 August 2018 в 14:36
поделиться

Вы знаете, что что-то не так, потому что Bytes.toBytes вызывает c.getBytes внутренне:

public static byte[] toBytes(String s) {
    try {
        return s.getBytes(HConstants.UTF8_ENCODING);
    } catch (UnsupportedEncodingException e) {
        LOG.error("UTF-8 not supported?", e);
        return null;
    }
}

Источник берётся из здесь . Это говорит о том, что вызов не может быть быстрее, чем прямой вызов - в лучшем случае (т. Е. Если он встанет в очередь) он будет иметь одинаковые сроки. В общем, вы ожидаете, что это будет немного медленнее из-за небольших накладных расходов при вызове функции.

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

19
ответ дан dasblinkenlight 18 August 2018 в 14:36
поделиться
  • 1
    Bytes.toBytes() вызывает c.getBytes("UFT-8") внутренне, но с кодировкой в ​​качестве входа. String s getByte() имеет немного накладных расходов, чем getByte("UTF-8") и, следовательно, медленнее. Короче Bytes.toBytes() работает быстрее. – Santosh 18 December 2013 в 15:46

«Второй» цикл выполняется быстрее, поэтому

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

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

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

, например

for(int i = 0; i < 3; i++) {
    long time1 = doTest1();  // timed using System.nanoTime();
    long time2 = doTest2();
    System.out.printf("Test1 took %,d on average, Test2 took %,d on average%n",
        time1/RUNS, time2/RUNS);
}
14
ответ дан Peter Lawrey 18 August 2018 в 14:36
поделиться
  • 1
    Это не касается того факта, что второй цикл всегда быстрее, независимо от того, какой код в нем. – OrangeDog 18 December 2013 в 19:32
  • 2
    @OrangeDog Хммм, это именно то, что я адресую и как это исправить. Я не знаю, как сделать это яснее. – Peter Lawrey 18 December 2013 в 21:55

Просто может быть так, что вы выделяете столько объектов для объектов с вашими вызовами getBytes (), что запускает сборщик мусора JVM и очищает неиспользуемые ссылки (выводя мусор).

5
ответ дан reindeer 18 August 2018 в 14:36
поделиться

Немногие другие наблюдения

  • Как указано в @dasblinkenlight выше, Hadoop Bytes.toBytes(c); внутренне вызывает String.getBytes("UTF-8")
  • Вариантный метод String.getBytes(), который принимает набор символов поскольку входной сигнал быстрее, чем тот, который не принимает никакого набора символов. Поэтому для данной строки getBytes("UTF-8") будет быстрее, чем getBytes(). Я тестировал это на своей машине (Windows8, JDK 7). Выполните две петли, одна с getBytes("UTF-8") и другая с getBytes() последовательно в равных итерациях.
        long ts;
        String c = "sgfrt34tdfg34";
    
        ts = System.currentTimeMillis();
        for (int k = 0; k < 10000000; k++) {
            c.getBytes("UTF-8");
        }
        System.out.println("t1->" + (System.currentTimeMillis() - ts));
    
        ts = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) { 
            c.getBytes();
        }
        System.out.println("t2->" + (System.currentTimeMillis() - ts));
    

это дает:

t1->1970
t2->2541

, и результаты одинаковы, даже если вы меняете порядок выполнения цикла. Чтобы уклониться от любых оптимизаций JIT, я бы предложил запустить тесты по отдельным методам, чтобы подтвердить это (как это предложил @Peter Lawrey выше)

  • Таким образом, Bytes.toBytes(c) всегда будет быстрее, чем String.getBytes()
1
ответ дан Santosh 18 August 2018 в 14:36
поделиться
  • 1
    Спасибо за все ваши комментарии, я уже знаю немного больше. – Guille 18 December 2013 в 17:09
Другие вопросы по тегам:

Похожие вопросы: