Недавно Лукас Эдер заинтересовался в своём блоге, способен ли JIT-компилятор Java оптимизировать такой код, чтобы убрать ненужный обход списка из одного элемента:


// ... а тут мы "знаем", что список содержит только одно значение
for (Object object : Collections.singletonList("abc")) {
    doSomethingWith(object);
}

Вот мой ответ: JIT может даже больше. Мы будем говорить про HotSpot JVM 64 bit восьмой версии. Давайте рассмотрим вот такой простой метод, который считает суммарную длину строк из переданного списка:


static int testIterator(List<String> list) {
    int sum = 0;
    for (String s : list) {
        sum += s.length();
    }
    return sum;
}

Многим Java-программистам известно, что этот код полностью эквивалентен следующему:


static int testIterator(List<String> list) {
    int sum = 0;
    Iterator<String> it = list.iterator();
    while(it.hasNext()) {
        String s = it.next();
        sum += s.length();
    }
    return sum;
}

Разумеется, в общем случае в list может оказаться всё что угодно и поэтому JIT-компилятору придётся сгенерировать честные виртуальные вызовы на месте iterator(), hasNext() и next(), что, конечно, не очень быстро. Но что случится, если мы всегда будем вызывать этот метод, подавая ему на вход singletonList? Давайте добавим простенький метод main():


public class Test {
    static int res = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 100000; i++) {
            res += testIterator(Collections.singletonList("x"));
        }
        System.out.println(res);
    }
}

Здесь мы вызываем наш testIterator в цикле достаточное количество раз, чтобы скомпилировать метод JIT-компилятором C2. Как некоторые из вас уже знают, в виртуальной машине HotSpot есть два JIT-компилятора: C1 (клиентский) и C2 (серверный). В 64-битной версии Java 8 с настройками по умолчанию они работают совместно. Сперва метод компилируется с помощью C1 (который компилирует быстро, но создаёт не очень оптимальный код). При этом в код добавляются дополнительные инструкции, которые собирают некоторую статистику (это называется "профилирование"). Это, конечно, замедляет выполнение, но пригождается в дальнейшем. Среди различных профилей собирается профиль типов. В нашем случае JVM внимательно следит, какой тип имеет параметр list при каждом вызове. И тут виртуальная машина замечает, что в 100% случаев на входе был список типа Collections$SingletonList (который возвращает метод singletonList).


Когда количество вызовов метода достигает некоторого порога, метод перекомпилируется компилятором C2, которому доступен собранный профиль. C2 делает разумное предположение, что раз до сих пор всегда был SingletonList, то и далее он будет часто попадаться. А значит, iterator() точно вызовет метод singletonIterator(). Но там уже нетривиальный объект, который, к примеру, содержит поле hasNext, чтобы отследить, что его не вызвали дважды, и кинуть если надо NoSuchElementException. Способен ли C2 с этим побороться?


Чтобы узнать ответ, мы можем попросить JIT-компилятор вывести ассемблер сгенерированный для методов. Для этого нам потребуется установить hsdis. Потом можно воспользоваться удобными инструментами вроде JITWatch или написать JMH-бенчмарк и воспользоваться опцией -perfasm. Но здесь у нас пример простой, поэтому мы обойдёмся без сторонних инструментов и просто запустим виртуальную машину с такими волшебными параметрами:


$ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintAssembly Test >output.txt

Будьте осторожны: вывод этой команды может напугать маленьких детей. Но порывшись в нём, вы найдёте код, сгенерированный для нашего метода testIterator. Вот что сгенерировал C2 на платформе Intel x64 с кучей до 4 Гб:


Ассемблер, можно не вчитываться
  # {method} {0x0000000055120518} 'testIterator' '(Ljava/util/List;)I' in 'Test'
  # parm0:    rdx:rdx   = 'java/util/List'
  #           [sp+0x20]  (sp of caller)
  0x00000000028e7560: mov    %eax,-0x6000(%rsp)
  0x00000000028e7567: push   %rbp
  0x00000000028e7568: sub    $0x10,%rsp         ;*synchronization entry
                                                ; - Test::testIterator@-1 (line 15)

  0x00000000028e756c: mov    0x8(%rdx),%r10d    ; implicit exception: dispatches to 0x00000000028e75bd
  0x00000000028e7570: cmp    $0x14d66a20,%r10d  ;   {metadata('java/util/Collections$SingletonList')}
  0x00000000028e7577: jne    0x00000000028e75a0  ;*synchronization entry
                                                ; - java.util.Collections::singletonIterator@-1
                                                ; - java.util.Collections$SingletonList::iterator@4
                                                ; - Test::testIterator@3 (line 16)

  0x00000000028e7579: mov    0x10(%rdx),%ebp    ;*getfield element
                                                ; - java.util.Collections$SingletonList::iterator@1
                                                ; - Test::testIterator@3 (line 16)

  0x00000000028e757c: mov    0x8(%rbp),%r11d    ; implicit exception: dispatches to 0x00000000028e75c9
  0x00000000028e7580: cmp    $0x14d216d0,%r11d  ;   {metadata('java/lang/String')}
  0x00000000028e7587: jne    0x00000000028e75b1
  0x00000000028e7589: mov    %rbp,%r10          ;*checkcast
                                                ; - Test::testIterator@24 (line 16)

  0x00000000028e758c: mov    0xc(%r10),%r10d    ;*getfield value
                                                ; - java.lang.String::length@1
                                                ; - Test::testIterator@30 (line 17)

  0x00000000028e7590: mov    0xc(%r10),%eax     ;*synchronization entry
                                                ; - Test::testIterator@-1 (line 15)
                                                ; implicit exception: dispatches to 0x00000000028e75d5
  0x00000000028e7594: add    $0x10,%rsp
  0x00000000028e7598: pop    %rbp
  0x00000000028e7599: test   %eax,-0x27b759f(%rip)        # 0x0000000000130000
                                                ;   {poll_return}
  0x00000000028e759f: retq   
  ... // дальше холодные пути

Первое, что бросается в глаза — это краткость кода. С вашего позволения я прокомментирую, что тут происходит:


// Стандартный стековый фрейм - подобным образом начинается всякий JIT-компилированный метод
mov    %eax,-0x6000(%rsp)
push   %rbp
sub    $0x10,%rsp         
// Загружаем идентификатор класса объекта из переменной list (указатель на объект пришёл в метод в регистре rdx).
// Идентификатор класса лежит в объекте по смещению 0x8. Это похоже на вызов list.getClass().
// При этом здесь происходит неявная проверка на null. Если окажется, что передали в метод null,
// процессор сгенерирует аппаратное исключение в связи с обращением по запрещённому адресу.
// Исключение перехватит виртуальная машина и заботливо транслирует его в NullPointerException
mov    0x8(%rdx),%r10d
// Сравниваем list.getClass() с идентификатором класса Collections$SingletonList. Этот идентификатор не меняется
// за время работы JVM и, конечно, JIT его знает, поэтому это просто сравнение с константой
cmp    $0x14d66a20,%r10d
// Если list - это не SingletonList, выпрыгиваем на холодный путь
jne    0x00000000028e75a0
// Читаем приватное поле Collections$SingletonList.element в регистр rbp. Хотя указатели 64-битные, при размере кучи 
// меньше 4 Гб верхние 32 бита всегда нули, поэтому виртуальная машина их не хранит и копирует только 32 нижних бита в ebp
mov    0x10(%rdx),%ebp
// Читаем идентификатор класса элемента и сверяем его с идентификатором класса String (аналогично тому, что выше)
mov    0x8(%rbp),%r11d
cmp    $0x14d216d0,%r11d
// Если элемент списка - не строка, выпрыгиваем наружу на холодный путь (там будет создано и выброшено ClassCastException)
jne    0x00000000028e75b1
// Читаем приватное поле String.value в регистр r10. Это массив char[], в котором хранится сама строка
mov    %rbp,%r10
mov    0xc(%r10),%r10d
// Читаем длину массива в регистр eax, который стандартно используется для передачи возвращаемого значения метода
mov    0xc(%r10),%eax
// Восстановление стекового фрейма
add    $0x10,%rsp
pop    %rbp
// Проверка на safe-point. С её помощь JVM может забрать контроль у скомпилированного кода, например, для сборки мусора.
test   %eax,-0x27b759f(%rip)
// Выход из метода
retq   

Если кому-то всё ещё сложно это понять, давайте перепишем на псевдокоде:


if (list.class != Collections$SingletonList) {
  goto SLOW_PATH;
}
str = ((Collections$SingletonList)list).element;
if (str.class != String) {
  goto EXCEPTIONAL_PATH;
}
return ((String)str).value.length;

Видите? На горячем пути нет ни цикла, ни выделения памяти под итератор, ни одного вызова метода. Всего лишь несколько разыменований и две быстрые проверки (которые всегда были ложны, поэтому предсказание ветвлений в процессоре отработает на ура). JIT-компилятор заинлайнил всё, что можно, понял, что итератор из метода не убегает, избавился от выделения памяти, развернул цикл и даже смог удалить флаг hasNext и связанные с ним проверки, статически доказав, что они не нужны! Сложение и переменная sum также испарились. И тем не менее, метод полностью корректен. Если окажется, что при следующем вызове ему передадут не singletonList, а что-то другое, он просто уйдёт на холодный путь (который, конечно, значительно медленнее). Остальные исключительные ситуации тоже обрабатываются. Можно передать null вместо list или подсунуть в список не строку (слава type erasure) — всё это будет обработано в соответствии с семантикой языка.


Что же произойдёт, если сценарий работы программы изменится? Предположим, через некоторое время мы вообще перестали передавать в этот метод singletonList и передаём теперь всякие другие списки. На медленном пути виртуальная машина продолжает собирать статистику. Если она обнаружит, что медленный путь происходит сильно часто, JIT-компилятор перекомпилирует метод, убрав специальную обработку singletonList и вставив сразу честные виртуальные вызовы. Может ещё, кстати, сделать две ветки, если вы используете только две разные реализации списков. Этим JIT отличается от приложений скомпилированных заранее: исполняемый машинный код вашей программы может меняться, следуя за изменениями в её поведении.

Поделиться с друзьями
-->

Комментарии (129)


  1. stepanp
    18.07.2016 22:33
    -33

    Если бы JIT-компилятор круто оптимизировал, программы на Java работали бы быстро


    1. voddan
      18.07.2016 22:52
      +23

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


      1. cs0ip
        18.07.2016 23:16
        +11

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


        1. voddan
          18.07.2016 23:24
          +2

          Тестов таких не очень много, насколько я понимаю. Сложно найти программы/проекты на Java/C++ которые бы делали одно и тоже. Не так давно я где то видел сравнение tcp/ip библиотек, там библиотеки на Java были в топе, но это не совсем то.


        1. JediPhilosopher
          18.07.2016 23:24
          +3

          Ну я в свое время писал на джаве и на С++ некоторую математику и сравнивал. Там считались всякие хитрые хеши для изображения, по которым выполнялся потом их поиск, нужно было оценить потенциальную пользу от переписывания этой части в нативном коде. Деталей самой математики честно говоря уже не помню, да и NDA, все дела, но там было несколько вполне весомых файлов с кодом, тензоры всякие, свертки, какие-то страшные формулы которые по маске считались для каждого пикселя картинки и т.п.

          Ну так вот, при этом код на С++ оказался впереди на считанные проценты времени. Выигрыш был бы съеден дополнительными расходами на вызов через JNI, поэтому от переноса мы решили отказаться. Это был код который делает много вычислений, но особо не аллоцирует память и в целом в С++ переносится практически один-в-один.

          Так что именно перемалывание циферок у них примерно одинаково. За счет «оптимистичных оптимизаций», приведенных в статье, джава может даже вырываться вперед иногда.


          1. cs0ip
            18.07.2016 23:56

            Это, кстати, достаточно естественно выглядит. Т.к. математика напрямую отображается в команды процессора без особых накладных расходов. Как я понимаю, основные проблемы возникают при работе с памятью. Т.е., допустим, я не могу накопить массив байтов и потом конвертировать его в строку без копирования. И подобные копирования в очень многих местах при работе с java встречаются. В то же время на c/c++/rust подоных копирований можно избежать не теряя читабельность кода и не изобретая свои велосипеды под конкретные случаи.


            1. khim
              19.07.2016 05:34
              +4

              Т.е., допустим, я не могу накопить массив байтов и потом конвертировать его в строку без копирования.
              А как вы это в C++ сделаете?

              В то же время на c/c++/rust подоных копирований можно избежать не теряя читабельность кода и не изобретая свои велосипеды под конкретные случаи.
              Я боюсь что «без велосипедов» не получится. Другое дело, что стиль написания с передачей фрагментов буфера туда-сюда в Java выглядит как-то что исключительно кривое и, скажем так, инородное. А в C++ — это один из типичных стилей написания программ. Но это скорее вопросы культуры и стиля.

              Проблема Java в том, что весь этот код, из которого JIT'ы умеют делать «конфетку» — он нифига не идеоматичен. А идеоматичный код (со всякими фабриками фабрик фабрик, DI через отражение или, что ещё «круче», через XML-конфиг) ни один JIT вам ни во что вменяемое не превратит…


              1. APXEOLOG
                19.07.2016 12:15

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


          1. ErmIg
            19.07.2016 08:51
            +1

            Тут знаете есть небольшое возражение:

            В области обработки изображений для алгоритмов характерен высокий параллелизм и не требуется, как правило, высокая точность (что позволяет часто использовать в вычислениях числа с фиксированной точностью). Эти характеристики позволяют в большинстве случаев очень эффективно задействовать SIMD, которые сейчас поддерживают практически все современные процессоры. Так вот задействование SIMD позволяет в среднем получить дополнительный выигрыш на порядок. С++ позволяет эффективно задействовать SIMD при помощи интринсиков. Как с этим у Java?

            А так да, я сам лично проводил сравнение кода (без SIMD оптимизаций) на С++ и C# — и еще лет 6 назад выигрыш С++ не превышал 30%, что делало не актуальным простое переписывание кода из managed языков на С++).


            1. lany
              19.07.2016 09:00
              +1

              Насколько я знаю, HotSpot JIT может применить SIMD, но на довольно ограниченном наборе паттернов.


              1. ErmIg
                19.07.2016 09:23

                Ну так и в С++ для многих компиляторов есть автовектоизация, которая на простых случаях достаточно эффективно работает. Но как только шаг в сторону — и приходится ручками все допиливать.


                1. lany
                  19.07.2016 09:28

                  В рамках Project Panama двигают нормальные ассемблерные вставки в Java-код, без всякого JNI. Может в Java 10 что-нибудь выкатят.


              1. Quetzal
                19.07.2016 13:03

                На эту тему у Nitsan Wakart есть прекрасная статья (я думаю вы её уже видели).

                Ещё раз спасибо за статью :) Ну и по теме.

                Я, таки, попробовал на ArrayList'e из одного / двух элементов — там таких эффективных оптицизаций нет.

                За то если параметр заменить на String[]testIterator(String[] array) и передавать туда new String[] { «abc» }, то он вообще схлопывается в константу «3», а внешний цикл на 1 000 000 итераций анроллится (хотя я техническое дно в ASM и могу ошибаться).


            1. Viacheslav01
              19.07.2016 15:10
              +2

              Для Windows Store приложений уже опять актуально, т.к. часто вызовы платформы идут через границу Managed и Unmanaged, что может занчительно нагнуть производительность C# кода.
              Опять же на C++ в сравнении с C# в рамках платформы без доступа к unsafe можно не слабо сэкономить на работе с массивами.
              Я при переходе с C# на C++ для расчета данных треков, выиграл чуть больше чем в два раза, правда после перехода на DXMath, который перенес математику на SSE и Neon, выигрыш стал намного более значительным.


            1. igor_suhorukov
              19.07.2016 23:24

              Project Sumatra мог бы значительно поднять производительность jvm, работающей на APU по сравнению с JIT компилятором с SIMD инструкциями и регистрами. Но проект похоже не развивается…


        1. bromzh
          19.07.2016 01:18
          +1

          Например, https://www.techempower.com/benchmarks. Java там стабильно в лидерах.



      1. Jogger
        20.07.2016 18:40
        -1

        Люди не меряют. Люди пользуются. Человек есть мера всех вещей. И люди видят, что программы на java работают медленно. Ну, хорошо, не все это видят, те у кого топовый компьютер — могут и не заметить. Но, в общем-то, факта это не меняет.

        Даже прочитав эту статью у меня возникает недоумение: фраза «Этим JIT отличается от приложений скомпилированных заранее: исполняемый машинный код вашей программы может меняться, следуя за изменениями в её поведении.» подразумевает, что JIT чем-то лучше… но постойте… В приложении, скомпилированном заранее, в процессе работы выполняется только приложение. В случае же с JIT — параллельно работает ещё и компилятор, который к тому же собирает статистику! Даже если приложение за счёт адаптации к изменениям в поведении станет работать быстрее — работа компилятора всё равно съест весь прирост.


        1. lany
          20.07.2016 18:41

          Компилятор много работает на старте приложения. Через несколько минут интенсивной работы доля CPU, съедаемая компилятором, уже невелика. Кроме того редко когда у вас все ядра загружены, а компилятор асинхронно работает.


          1. Jogger
            20.07.2016 21:33

            Вы знаете какой у меня ПК, и как часто у меня загружены все ядра? Прекратите следить за мной!
            К слову, на рабочем компе не редка ситуация «Загружены все ядра», ибо компьютер там слабый. По странному стечению обстоятельств, эта ситуация почему-то совпадает с отладкой java-приложений в IDE, написаном на java.
            Я нисколько не умаляю достоинств JIT — я охотно вам верю, что если бы не он — java тормозила бы на порядок больше. И того, что использовани java зачастую оправдано — я тоже не отрицаю. Но, будем честны, быстродействие — это не сильная сторона java.


            1. webkumo
              20.07.2016 23:30
              +2

              Может будем честны до конца: быстродействие Java на многих классах задач сравнима (чуть хуже/чуть лучше) с производительностью, получаемой на c++?

              PS да, разработка на java требует компьютера посерьёзней, чем простой офисный. И по памяти и по мощности cpu желателен буст. Впрочем всё это нужно для ускорения разработки, которая и без того зачасту быстрее разработки программ на c++.
              PPS я знаю, что теоретически из c++ можно выжать много больше, знаю, что на некоторых классах задач он будет рвать java как тузик грелку. Вообще c++ выбран как пример языка, программы на котором очень часто сравнивают по производительности с программами на java.


              1. khim
                21.07.2016 00:23
                +1

                быстродействие Java на многих классах задач сравнима (чуть хуже/чуть лучше) с производительностью, получаемой на c++?
                Нельзя сравнивать «быстродействие на многих классах задач». Можно сравнивать «определённым способом написанный код».

                Всё упирается в один простой факт: работа с таким массивом
                struct {
                  int x;
                  int y;
                } array1[100];
                
                в несколько раз быстрее, чем с таким
                struct {
                  int x;
                  int y;
                } (*array2)[100];
                


                И всё что делает JIT — пытается всеми правдами и неправдами превратить array2 в array1.

                К огромному сожалению для людей, вынужденных пользоваться программами на Java весь язык, все библиотеки и вообще вся экосистема построены на превращении из array1 в array2. Многократном и повсеместном. Никакой JIT тут не спасёт.

                А главное — в программах где array1 доминирует всё ради чего затеяны все сложности, которые героически решают разработчики JIT'ов не нужны. Если нет бесконечных уровней индирекции и запутанных иерархий абстракий, то не так сложно отследить — кто чем владеет, а значит не нужен GC. Раз не нужен GC и типы заранее известны — то все навороты типа «est %eax,-0x27b759f(%rip)» — тоже не нужны. И так далее.

                Вообще c++ выбран как пример языка, программы на котором очень часто сравнивают по производительности с программами на java.
                И всё всегда сводится к одному вопросу: а почему, собственно, если JIT так крут — то почему все программы на Java тормозят? А если программы всё равно тормозят — то о какой «крутости» вы тут говорите?

                И обе стороны по своему правы. А истина — посередине: JIT-компиляторы, несомненно, круты, но проблема в том, что они, по большей части, решают задачу которой вообще не должно было быть!


                1. bromzh
                  21.07.2016 01:01
                  +1

                  все программы на Java тормозят

                  Список можно? А то уже несколько людей говорят, что всё на java у них тормозит, но почти ни одного названия тормозящих программ нет. Ну и ещё конфигурацию компа, на котором всё это тормозит.


                  А то у меня на рабочем компе вполне могут работать 2 экземпляра IDE, запущен хром и фф. Ничего из этого не тормозит, хотя конфигурация далеко не топовая.


                  1. Jogger
                    21.07.2016 02:50
                    -4

                    Вам перечислить список всех существующих программ на java? Боюсь список получится несколько, эмм, длинноват. А вы можете дать список программ на java, которые не тормозят?


                    1. bromzh
                      21.07.2016 03:57
                      +4

                      Вам перечислить список всех существующих программ на java?

                      Нет, хотел увидеть список программ, которыми вы реально пользовались и которые тормозили при этом.


                      А вы можете дать список программ на java, которые не тормозят?

                      Могу назвать те, которыми пользуюсь я.
                      Например, IDE от JetBrains. После запуска, построения индекса и загрузки кэша работают шустро.
                      Ещё при работающей IDE только что запустил 2 профайлера (JProfiler и YourKit Java Profiler). Каждый из них работал без тормозов: интерфейс отзывчивый, фризов не наблюдал.


                      Больше я особо не припомню java-приложений, которыми пользуюсь. А называть всё подряд и говорить "не тормозит" я не буду. Но если уж такие программы работают шустро, то и более мелкие не должны тормозить.


                      PS Запустил SweetHome 3D для проверки. Работал без тормозов, но я особо не добавлял объектов, так что точно не могу сказать.


                      Система: linux, oracle vm 64bit, 8GB RAM, Core i5 2.2GHz.


                    1. Borz
                      21.07.2016 04:33
                      +3

                      Сталкивался с одним коллегой, который говорил, что его демо прога на C# работает быстрее чем на Java. Суть была в плавной смене цветов в квадратах (на форме девять квадратов) и якобы на Java это медленнее. В ходе разбора выяснилось, что просто на Java он кривой код написал — переписали и визуальной разницы перестали замечать.
                      Вывод: тормозит не JVM, а кривой код на ней


        1. bromzh
          20.07.2016 20:49
          +4

          И люди видят, что программы на java работают медленно

          А какие программы? Просто чтобы сравнивать более объективно, нужно брать продукты примерно одного уровня с идентичными возможностями на разных языках.


          У меня, например, из десктопных java-программ есть только IDE (Idea). Ну да, они тормозят. Точно так же, как и IDE на C++ (Visual Studio). Да и то, тормоза в идеи в основном только при индексации проекта.


          Ещё любят упоминать Minecraft как пример тормозной java-программы. Там тормоза связаны с недостаточной оптимизацией самой игры (по крайней мере, Нотч не особо оптимизировал, как сейчас там дела я не знаю). Ну и тормозило там всё в основном при генерации новых чанков, что вполне понятно. Ну и опять же, как будто подобные игры на C++ не тормозят.


          Так может программы на java тормозят не потому что на java, а потому что они просто либо очень сложные, либо плохо оптимизированны, а язык и платформа тут не вносят существенных тормозов?


          1. Jogger
            20.07.2016 21:28
            -1

            Ну вот давайте я тоже сравню IDE. Запускаю netbeans — тормозит до невозможности работать. Запускаю Visual Studio — ничего не тормозит, единственные тормоза — на этапе компиляции. Я как-то не так сравниваю? Это не единственный подобный случай, просто если уж вы заговорили об IDE — то привожу свой опыт. Но с другими java-программами опыт примерно такой-же. Это случается настолько часто, что когда у меня что-то тормозит — я проверяю, не на java ли оно написано. Не всегда, но часто — угадываю.


            1. zzashpaupat
              21.07.2016 11:32
              +1

              Года 3 назад приходилось править софт, написанный на C#. WPF, XAML и прочие .NET технологии. Так вот стоило открыть пару в меру сложных WPF-форм, как VS и вся рабочая станция вместе с ней начинали дико тупить. Любой достаточно тяжелый софт можно нагрузить так, что начнет тормозить не только он, а и все остальное.


              1. Jogger
                21.07.2016 14:57

                Так у C# те же проблемы что у java.


                1. khim
                  21.07.2016 15:40
                  -2

                  Не совсем. Многие проблемы managed-языков можно сделать менее острыми глубоко интегрировав их в систему (Лисп-машины имели смешную, по современным меркам, производительность, однако были вполне себе отзывчивы за исключением этапа загрузки и «прогрева» системы… Android на Java — это тоже «ужас», а не «ужас-ужас-ужас» — по той же причине) — но в целом, да, проблемы схожи.

                  P.S. Моё мнение обо всём этом managed безумии сложилось во времена WRT.EXE из Borland C++ 2.0. Который нещадно тормозил и требовал «невероятных» (по тем временам) ресурсов (при отвратительной функциональности, надо сказать). Через год вышел Borland Resource Workshop и про тормоза все забыли. Вспомнили когда началась вся эта истерия вокруг Java — и до сих пор слышим мантры про то, какой крутой Java JIT и что тормоза — это фигня, главное — функцинальность. Увы, но тормозят C# и Java всегда, а функциональность… — может быть, может не быть…


          1. PsyHaSTe
            21.07.2016 19:55

            Если я правильно помню, то студия начиная с 2012 (или 2013) переписана на WPF, что нифига не плюсы. Компилятор (привет Rosylin) тоже переписан на шарпы. Из ключевых плюсовых компонент остается только msbuild, но он прямого отношения к студии не имеет, и причиной тормозов как правило не является.


        1. voddan
          20.07.2016 22:21
          +2

          Фразой про необходимость (корректных) измерений я хотел сказать что совсем не очевидно что быстрее — программа на C++ или на Java.

          Многие распространенные мифы про тормознутость Java строятся на (не совсем корректных) прикидках.

          Ну вот давайте и прикинем. Известно что узкое место в работе (нормального) компьютера это доступ в память. Разработчику не жалко потратить десятки а иногда сотни тактов процессора чтобы избежать лишнего доступа в кеши процессора или в оперативку. 90% времени процессор пользовательского компа простаивает в ожидании подгрузки чего-нибудь из памяти.

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

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

          Вот так и получается что действия, которые выполняются относительно редко (95% всей логики), логично интерпретировать. Действия которые выполняются часто и влияют на производительность «распаковывают» из байт-кода в машинные команды различной степени оптимальности.

          Когда Вы запускаете IDE вроде IntelijIdea, 5 минут в начале она подтормаживает, происходит тот самый «прогрев» — инициализация и распаковка кода. После этого она начинает ускорятся, и через полчаса летает (это мои собственные ощущения). Готов поспорить что если гипотетически скомпилировать ее в машинные команды (реально не получится ибо рефлекшн), то эти 5-20 минут разогрева потратятся просто на загрузку разбухшего кода в память и прогрев кешей.


          1. Jogger
            20.07.2016 23:43
            -1

            Вы знаете, меня как пользователя — мало волнуют измерения. Я говорю о субъективных ощущениях, которые возникают при пользовании программой написаной на C/C++ и программой написанной на java. Так вот, эти ощущения таковы, что когда я работаю с программой на java — меня не оставляет ощущение тормознутости. Это не абсолютно измеренное быстродействие, это совокупность факторов «сколько я трачу времени на ожидание завершения очередного действия» + «насколько отзывчив интерфейс» + «насколько это влияет на исполение других программ». Если во время работы с некоторой программой, я обнаруживаю, что реакция на пользовательский ввод происходит с существенной задержкой, мне приходится ждать, когда программа выполнит очередное действие — я говорю, что программа «тормозная». Если при этом и другие программы, до этого работавшие «нормально», стали проявлять те же признаки — я говорю «программа тормозит весь компьютер». Так вот, этими эпитетами я награждаю программы, написанные на java на порядок (а может и на пару порядков) чаще, чем программы, написанные на C/C++. Я не берусь делать выводы о причинах — возможно, это просто нерепрезентативная выборка, и мне не везло, возможно — это недостаток хороших программистов, пишущих на java, из-за низкого уровня вхождения в язык, возможно — это таки следствие реально более низкого быстродействия. А может быть, тут совокупность всех этих факторов, это мне кажется наиболее вероятным.
            Ну и в «и интерпретатор зачастую вместе занимают меньше места чем соответствующий ассемблер.» мне как-то верится с трудом. К сожалению, полноценно это утверждение проверить не удастся, ибо это будет либо компиляция каких-то синтетических тестов, либо и вовсе несравнимые вещи.


            1. khim
              21.07.2016 00:03
              -1

              Я не берусь делать выводы о причинах — возможно, это просто нерепрезентативная выборка, и мне не везло, возможно — это недостаток хороших программистов, пишущих на java, из-за низкого уровня вхождения в язык, возможно — это таки следствие реально более низкого быстродействия. А может быть, тут совокупность всех этих факторов, это мне кажется наиболее вероятным.
              Причина, конечно, не в JIT'е. Прична — в том, что изначально вся архитектура Java'ы сделана так, что железу исполнять программы не просто трудно, а черезвычайно трудно. Как известно все проблемы в информатике можно решить с помощью дополнительного уровня абстракции… за исключением проблемы слишком большого количества абстакций. А проблемы со скоростью и излишним потреблением памяти — это и есть проблемы слишком большого количества абстракций в 99 случаях из 100.

              За то время пока вы сходите в память разименовать один указатель процессор успеет обратить матрицу 10x10… или отсортировать массив на 100 элементов… или ещё что-нибудь подобное посчитать. А у вас тут фабрики фабрик фабрик, DI и отражения… которые все, что характерно, построены на бесконечных индирекциях…

              Можно ли написать на Java код так, чтобы он был быстрее, чем код на C/C++? Да, разумеется. Но для этого вам потребуется программист с опытом работы на FORTRAN или C++, а не Java. А такому программсту, представьте себе, проще и удобнее писать на FORTRAN или C++!


            1. bromzh
              21.07.2016 00:51
              +1

              я говорю «программа тормозит весь компьютер»

              Допустим, в системе запущен браузер, ещё пара мелких приложений. Всё это дело потребляет 3Гб из 4-х. Всё работает без тормозов. Я запускаю IDE, памяти не хватает, всё начинает жутко лагать. Кажется, что ИДЕ затормозила всю систему…
              Теперь другая ситуация: запущена IDE, пара мелких приложений. Память не израсходована до конца, всё летает. Я открываю в браузере кучу вкладок, память кончается, всё тормозит. Кто теперь виноват в тормозах?
              Тут не IDE тормозит всю систему, и не браузер, а совокупность факторов. Лично по моему опыту, всё начинает тормозить, когда память подходит к концу. Да, зачастую java-приложения довольно прожорливы в плане памяти.


              Ещё есть такой момент: количество потребляемой памяти для java-программы можно ограничить через JVM. Если программа по какой-то причине не укладывается в кол-во выделенной ей памяти — она обычно начинает тормозить. А вот кол-во памяти потребляемой программой на C++ ограничить уже не так просто. Она может отъесть очень много ОЗУ. В таком случае может просто случиться так, что JVM не сможет выделить много памяти приложению, и оно будет тупить, при этом всё остальное тормозить не будет.


              Кстати, можете привести пример java-программ для десктопа, которые у вас тормозили? Просто я кроме IDE и сопутствующих утилит особо не запускал. И IDE у меня тормозит только при запуске/индексации. Ну или когда вся память закончилась (но тогда тормозит всё, и дело тут уже не в java).


              1. khim
                21.07.2016 02:45

                Ещё есть такой момент: количество потребляемой памяти для java-программы можно ограничить через JVM.
                Нельзя, не рассказывайте сказок. Можно ограничить количество памяти, которое JVM будет сжирать без всякой причины.

                А вот кол-во памяти потребляемой программой на C++ ограничить уже не так просто.
                ulimit кто-то отменил? Только в случае с C++ — это «ремень безопасности», грамотно написанная программа на C++ не должна потреблять лишней памяти (и не делает этого на практике). Только не надо про браузеры — современные браузеры, увы, это не «просмотрщики гипертекста», как они задумывались, а интерпретаторы-переростки для языка, который пытается доказать, что Java — это всего лишь «ужас», а не «ужас-ужас».

                Кстати, можете привести пример java-программ для десктопа, которые у вас тормозили?
                Тормозит всё, что я запускал. IDEA, Vuze, даже какие-нибудь мелкие утилитки типа Keypass'а (это, правда, C# — но принцип тот же).

                Разумеется у меня на десктопе (64GiB RAM, Dual Xeon, 24 ядра, 48 потоков) — проблем нет. Но стоит мне пересесть на «печатную машинку» моей сестры (2GiB RAM, Atom) — всё, можно умереть.

                Только не надо про «ну чего вы хотите от такой системы»: я хочу чтоб работало. И если для программы на C/C++ мне нужна машинка за $100, а для программы на Java для получения одинакового «фана и экспиреенса» — за $10'000, то это, у нормальных людей, не обременённых глубокими познаниями в IT, и называется «тормозит».


                1. bromzh
                  21.07.2016 04:23
                  +2

                  грамотно написанная программа на C++ не должна потреблять лишней памяти (и не делает этого на практике)

                  Как и на любом другом языке.


                  Тормозит всё, что я запускал. IDEA, Vuze

                  Ну вот, наконец-то назвали вторую тормозящую программу. Конкретно про Vuze не могу ничего сказать. (помню только, что это большой комбайн, включающий в себя кучу всего нужного и не очень).


                  А вот про идею...


                  (2GiB RAM, Atom)
                  я хочу чтоб работало
                  И если для программы на C/C++ мне нужна машинка за $100, а для программы на Java для получения одинакового «фана и экспиреенса» — за $10'000

                  А назовёте программу на C++, которая имеет одинаковый фан и экспириеенс, сравнимый с IDE от JetBrains? И чтобы при этом она работала на таком ноуте (2GiB RAM, Atom).
                  Я вот не знаю такой. Может быть, когда она появится, на таком ноуте она тоже будет тормозить… В любом случае, сравнить Idea пока не с чем.


                  В общем, пока что тормозит только Idea на откровенно слабой для неё системе, и Vuse, а больше названий я не увидел. Но 2 программы это далеко не всё что есть в мире java.


                  1. khim
                    21.07.2016 13:20
                    -1

                    Ну вот, наконец-то назвали вторую тормозящую программу.
                    Как вы думаете — почему мне так сложно это сделать? Потому что я не люблю Java? Нет — потому что я не люблю тормоза. И в результате программ на Java у меня не мало, а очень мало. И они все — начиная от IDE и кончая нашими внутренними системами сборки — страшные монстры жрущие ресурсы «как не в себя».

                    Тут вас просили: приведите лучше примеры реально программы на Java, которая «летает» — а мы сравним её с аналогом на C++. Только не бенчмарк, а реальную программу, пожалуйста.


                    1. bromzh
                      21.07.2016 13:58
                      +1

                      Тут вас просили: приведите лучше примеры реально программы на Java, которая «летает» — а мы сравним её с аналогом на C++.

                      https://habrahabr.ru/post/305894/#comment_9709242


                      Вот только сравнивать-то особо не с чем. IDE от JetBrains по фичам далеко впереди студии, а уж других ИДЕ и подавно. Профайлеры jvm написанные C++ вряд ли есть.


                    1. chabapok
                      22.07.2016 16:21
                      +3

                      Потому, что аналогов не существует. Vuze просто хреноватенько написан, но я им пользусь, потому что мю-торрент в какой-то момент начала втихую ставить майнеры биткоинов.

                      флеш плеер у вас тормозит? Насколько знаю, он активно использует жава код, просто неявно. На самом деле, жаву использует много прог, но вы про это просто не догадываетесь. Они статически прилинковали libjvm — и юзают ее. Они не тормозят — и поэтому вы думаете что это не java.

                      Пример из жизни — json_spirit ел 80% проца, qt json парсер — ест около 15 (и при этом он архитектурно убог, что такое container factory — авторы еще лет 10 знать не будут). java-парсер ест 1...5% (что в пределах погрешности). Правда, пришлось написать свой парсер, с плюшками. Стандартные убоги, с неправильно расставлеными try-catch и неверной архитектурой. Стандартные тоже наверное подтормаживали бы.

                      Так что, на плюсах тоже дохрена тормозящих приложений. Просто считается, что если тормозят плюсы — это хреново написаное, а если тормозит на java — это «тормозит java».


                      1. Jogger
                        23.07.2016 05:00

                        Флеш плеер — одна из самых тормозящих программ, известных миру. Благодаря вам, я наконец узнал, почему.


                1. webkumo
                  21.07.2016 10:01

                  А теперь проведём мысленный эксперимент: поставим на этот ваш «печатную машинку сестры» MS Visual Studio — ну как, не тормозит? Других очень толстых (по объёму предоставляемого функционала) не-java IDE не вспомню сходу… Разработка ПО изначально не приспособлена эффективно работать на дохлых машинах.

                  Про Vuze — на практике знаю, что были нормальные сборки, были тормозные. Это не от языка зависит. Помнится натыкался на пару клиентов вполне себе плюсовых, которые тормозили не слабее.


                  1. khim
                    21.07.2016 13:16

                    А теперь проведём мысленный эксперимент: поставим на этот ваш «печатную машинку сестры» MS Visual Studio — ну как, не тормозит?
                    Visual Studio 6? Нет — не томозит. .NET был частично переписан на CLR для получения «фана и экспириенса» с покупкой нового железа, увы.

                    Разработка ПО изначально не приспособлена эффективно работать на дохлых машинах.
                    Нет — это совренные IDE не рассчитаны на это. Только не надо про reflection и прочее. Visual Basic или старые версии Delphi без CLR точно также позволяли и формочки таскать и свойста типов смотреть и многое другое. Потребляя в разы меньше ресурсов.


                    1. webkumo
                      21.07.2016 13:40
                      +2

                      А вы более древнюю VS могли назвать? Она не тормозит не потому, что вся из себя такая хорошая, а потому, что функционал у неё резко меньший.
                      Давайте прикинем по требованиям: если я правильно помню 200 метров оперативки ей хватало за глаза. Современная IDE от JetBrains в полном фарше на среднем проекте съедает гиг. При том, что у вас всего 2 гига на одноядерном атоме, полюбому, в винде…
                      В общем у меня на такой конфигурации тормозит _всё_ ПО… Java, браузеры, системные утилиты, да даже сама ОС.

                      Современные IDE не рассчитаны на использование питекантропами, например, и что? Они дают функционал. Много функционала. Функционала который ускоряет кодирование, функционала, который помогает бороться с ошибками. За всё нужно платить. Ну а если вам достаточно этих динозавров, так почему бы сразу на vim не уйти?
                      Так что предоставляя НА ПОРЯДКИ худший функционал они потребляют В РАЗЫ меньше ресурсов. ИМХО невыгодный обмен.


                      1. khim
                        21.07.2016 15:20

                        А вы более древнюю VS могли назвать?
                        Мог бы. Они тоже не тормозят :-)

                        Давайте прикинем по требованиям: если я правильно помню 200 метров оперативки ей хватало за глаза.
                        200 метров? Вы, я извиняюсь, ох$нели? 20-30MiB. Больше — если операционка требует больше. На своём Pentium MMX с 64MiB памяти я спокойно ей пользовался под Windows 95 OSR2, на машинках с 16MiB — да, были тормоза, скорее всего на 32MiB она бы «слегка подтормаживала при запуске». Если Windows 2000 поставить или XP, то да, потребуется больше — но тут не в Visual Studio дело.

                        Современная IDE от JetBrains в полном фарше на среднем проекте съедает гиг.
                        Я не знаю что такое «средний проект». Кому и Chromium (на котором Visual Studio, я извиняюсь, «сворачивается в трубочку») — «средний проект». Но на «Hello, world!» уже требуются сотни мегабайт.

                        Она не тормозит не потому, что вся из себя такая хорошая, а потому, что функционал у неё резко меньший.
                        Не надо про «функционал», пожалуйста. Даже если отключить в Visual Studio .NET или IDEA вообще всё что можно — они тормозят. Менюшки медленно открываются, переключение между закладками тормозит, да даже если просто текст набирать — можно тормоза заметить.

                        В общем у меня на такой конфигурации тормозит _всё_ ПО… Java, браузеры, системные утилиты, да даже сама ОС.
                        Мне вас жаль. А у моей сестры её любимый MS Office 2000 не тормозит. И Chrome — тоже. И даже MPC не лагает, представьте себе!

                        Современные IDE не рассчитаны на использование питекантропами, например, и что? Они дают функционал. Много функционала.
                        Давайте не переводить стрелки, а?

                        Исходный тезис был прост: Java == неумеренное потребление ресурсов — в первую очередь памяти, но и CPU тоже.

                        Этот тезис вашими рассказами про «много функционала» ничуть не опровергается. Скорее наоборот.

                        Ну а если вам достаточно этих динозавров, так почему бы сразу на vim не уйти?
                        Кто вам сказал, что я не использую Vim?

                        Так что предоставляя НА ПОРЯДКИ худший функционал они потребляют В РАЗЫ меньше ресурсов. ИМХО невыгодный обмен.
                        Когда как. IDEA я тоже иногда использую (правда реже, чем тот же Vim).

                        Но правда заключается в том, что в теории возможны четыре варианта:
                        1. Программы, которые требуют чудовищное количество ресурсов и мало чего умеют.
                        2. Программы, которые требуют чудовищное количество ресурсов и обладают развитым функционалом.
                        3. Программы, которые не требуют особо много ресурсов и мало чего умеют.
                        4. Программы, которые не требуют особо много ресурсов и обладают развитым функционалом.

                        Вы утверждаете, что программы из квадранта 4 были вытеснены программами из квадранта 2 — что правда. Программ из квадратна 4 мало и со временем они, обычно, отстают от программ из квадранта 2 (к возможностям Оберон-системы IDEA шла годы, хотя сейчас, конечно, она получила много новых фич), но проблема в том, что Java напрочь исключает квадранты 3 и 4.

                        И никакие рассказы про «много функционала» этого не изменят. Да и вообще, мы статья обсуждаем про что? Про то, что «JIT-компилятор оптимизирует не круто, а очень круто»! Ну и хде? Хде эта оптимизация? Почему нам опять поют песни про функционал, а не про то, что это позволяет встроить Java в систему управления холодильников за три с половиной цента?


                        1. grossws
                          21.07.2016 16:57
                          +1

                          Почему нам опять поют песни про функционал, а не про то, что это позволяет встроить Java в систему управления холодильников за три с половиной цента?

                          Эта часть шкалы тоже представлена в J2ME, JavaCard и всяких минималистичных OSGi-профилях. Только функциональность там урезанная, естественно.


                          1. khim
                            21.07.2016 18:41

                            Вот с этого момента — поподробнее. Про JavaCard — давайте не будем: этот высер во-первых имеет мало отношения к Java (вы реально хотите говорить что язык без динамической памяти и «кучи» можно назвать Java'ой?), а во-вторых — оно жрёт примеро вдвое больше ресурсов, чем альтернативные решения без маркетинговой составляющей.

                            J2ME — почил в бозе, слава богу. О покойниках либо хорошо, либо ничего.

                            А где применяются «минималистичные OSGi-профили»? Нет, мне правда интересно. Неужели какое-то из обещаний, которые помогли «продать» Java (напомню что изначально вся петрушка затевалась ради IoT (тогда, правда, это направление проходило под кодовым названием «Smart Appliances»), но, традиционно для Java, из этого ничего не вышло. Неужели кто-то ещё «не наелся»?


                            1. grossws
                              21.07.2016 22:35

                              Часть OSGi профилей можно отнести к эпохе J2ME (всякие CDC environment). Из более тяжелых вещей — Eclipse Kura, например. Оно живёт и развивается, но я не знаю, кто использует в боевом режиме.


                              Другой пример — microej, список клиентов вполне неплох. Ещё на ум приходит Excelsior JET Embedded с их AOT-компилятором.


                              У меня тоже сложилось впечатление, что в основном использование java в embedded — это hype (как и прошлый заход с jazelle), но то что ощутимое число фирм её пилят намекает, что покупатели есть. Всякие entertaiment-системы, HMI и т. п., где очень хочется взять толпу индусов и быстро выкинуть продукт на рынок.


                              При использовании ARM9/ARM11/Cortex-A с разумным количеством памяти использование java выглядит вполне реалистично.


                              1. khim
                                22.07.2016 01:22
                                +1

                                У меня тоже сложилось впечатление, что в основном использование java в embedded — это hype (как и прошлый заход с jazelle), но то что ощутимое число фирм её пилят намекает, что покупатели есть.
                                А я вот в этом — совсем не уверен. Java — это такой мыльный пузырь, вокруг которого — куча шума и очень мало сути.

                                Jazelle — это такая себе типичная Java-история: мы сделаем технологию, которая будет ускорять Java и внедрим её во все наши процессоры, вау! Звучит круто, заманчиво. Но когда начинаешь разбираться, то выясняется, что в результате — вышел пшик: Jazelle призвана исполнять определённый процент операций «в железе» и при этом она реализована во всех, без какого-либо исключения процессорах после ARM7EJ. Круто, да? Но есть одна маленькая беда: процент ускоренных операций может быть нулевым, а начиная с ARMv8 он обязан быть нулевым!

                                И вот так — веде и всюду. Сплошной Изумрудный город. Когда нет возможности сделать всё вокруг из настоящего изумруда — мы раздаём всем жителям и посетителям очки с зелёными стёклами.

                                Как вы думаете — почему я так резко отрицательно отношусь к Java и достаточно спокойно — скажем к C#? С чисто технической точки зрения — это почти одно и то же, но разработчики C# — не лицемерят. Они не пытаются выдать нечто без колёс и мотора за «удешевлённое авто» и нечто без крыльев и не летающее — за самолёт.

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

                                Подход же Java — это продолжать заявлять «что скорость работы Java-приложения мало отличается от скорости работы приложения на C++» (притом что увидеть ложность этого высказывания, в общем, не составляет труда), говорить о том, что нечто без байткода и управления памятью — это по прежнему Java (но только когда это делает Sun/Oracle, конечно — когда то же самое делает Google, то это, разумеется, страшный грех и нужно за это отсудить 100500 миллиардов), называть чёрное белым и вообще делать вид, что небо у нас — зелёное (если так партия сказала). Но признать свою ошибку и назвать белое белым, а чёрное чёрным? Нет, этого сделать никак нельзя!


                        1. webkumo
                          21.07.2016 17:04
                          +1

                          > 200 метров? Вы, я извиняюсь, ох$нели? 20-30MiB. Больше — если операционка требует больше. На своём Pentium MMX с 64MiB памяти я спокойно ей пользовался под Windows 95 OSR2, на машинках с 16MiB — да, были тормоза, скорее всего на 32MiB она бы «слегка подтормаживала при запуске». Если Windows 2000 поставить или XP, то да, потребуется больше — но тут не в Visual Studio дело.

                          Можете не извиняться, вам это не подходит. Ну а конкретную размерность я уже не помню. По Win95 — извините, но она сама жрёт минимум те самые 16 мегабайт — без свапа вы на 32х метрах очень мало что запустите… и покрашитесь с невозможность аллоцировать память. Скорость дисков того времени вам напомнить? Оно не смогло бы не тормозить. Ну а по накладным расходам — спасибо, что сами о них написали. Не придётся тыкать носом. На той вашей сестринской «печатной машинке» какая ОС? Сколько она оперативки выжирает? Ну а в отличие от производительности — с памятью вопросов нет, Java действительно её любит. Тому же Idea очень хочется скушать около гига. И начинается та же свистопляска со свапом… И это, по вашему, java виновата?

                          >Я не знаю что такое «средний проект». Кому и Chromium (на котором Visual Studio, я извиняюсь, «сворачивается в трубочку») — «средний проект». Но на «Hello, world!» уже требуются сотни мегабайт.

                          Средний проект — порядка 100 тысяч строк кода (включая аннотации и всяческие xml типа спрингового контекста). Не на «hw» требуются сотни мегабайт а на функциональность, которая позволяет эффективно работать с крупными проектами (включая кеши различных уровней, например AST, синтаксический анализатор и прочие плюшки).

                          > Не надо про «функционал», пожалуйста. Даже если отключить в Visual Studio .NET или IDEA вообще всё что можно — они тормозят. Менюшки медленно открываются, переключение между закладками тормозит, да даже если просто текст набирать — можно тормоза заметить.

                          Т.е. механизмы (архитектуру) подключения этого функционала вы игнорируете? Кастомизируемость UI тоже побоку? Вы физически не можете отключить все плюшки современных IDE. Только снизить их количество. Ну и на Intel Atom c 2мя гигами оперативы… у меня вот в такой конфигурации браузеры тоже только в путь тормозят… Они, видимо, тоже на managed языках написаны, да?

                          > Мне вас жаль. А у моей сестры её любимый MS Office 2000 не тормозит. И Chrome — тоже. И даже MPC не лагает, представьте себе!

                          А мне жаль вашу сестру. Пользоваться морально устаревшим софтом и хромом в одну-две вкладочки — тот ещё экспериенс… И да, про MPC не надо мне рассказывать. На Athlon XP 2100+ он тормозил на XP-шке при толстых видяхах, с чего он на более слабом Atom'е-то будет летать? Или вы только хардварно-декодируемое видео смотрите?

                          > Давайте не переводить стрелки, а?
                          Исходный тезис был прост: Java == неумеренное потребление ресурсов — в первую очередь памяти, но и CPU тоже.
                          Этот тезис вашими рассказами про «много функционала» ничуть не опровергается. Скорее наоборот.

                          Ути-пути. Вы на всех наезжаете, а на вас даже не дыши? Впрочем, если вы приняли питекантропов на свой счёт — поздравляю, вы лузер. Потому что я специально переписал первый вариант, в котором хотел пройтись по вам, на нейтральный, в котором я поминаю сильно «устаревших» (они бы хоть кнпоку смогли нажать?) персонажей. И да, исходный тезис «Java тормозит». Про память там ни слова. И с памятью, согласен, Java в 1.5-3 раза более прожорливая, чем C. Но если дефицит памяти специально не создавать (повышая объём съедаемого GC времени работы) — java имеют сравнимую с c++ производительность на многих классах задач.

                          > Кто вам сказал, что я не использую Vim?
                          > Когда как. IDEA я тоже иногда использую (правда реже, чем тот же Vim).

                          Кхм… так и напрашиваются слова про диагноз… Хотя тут я скорее всего не прав — вы же не на managed языках пишете? На каком-нибудь с++?

                          > И никакие рассказы про «много функционала» этого не изменят. Да и вообще, мы статья обсуждаем про что? Про то, что «JIT-компилятор оптимизирует не круто, а очень круто»! Ну и хде? Хде эта оптимизация? Почему нам опять поют песни про функционал, а не про то, что это позволяет встроить Java в систему управления холодильников за три с половиной цента?

                          Где? Ну вот возьмите свою банковскую карту — не исключено, что она используется https://ru.wikipedia.org/wiki/Java_Card. Это если говорить про оптимизацию по памяти, о которой вы вдруг посреди спора вспомнили. Стандартная версия Java плохо подходит для embedded устройств. Да и потребности в этом мало. Впрочем бывают исключения из данной ситуации — https://en.wikipedia.org/wiki/Blu-ray#Java_software_interface

                          Если же говорить о производительности (с чего и начался весь срач «Java тормозит») — то современная Java при достаточных ресурсах по памяти, ещё раз повторюсь, мало отличается по производительности от c++.
                          А про функционал вспомнили, когда вы начали припоминать динозавров, утверждая, что они не тормозят.
                          И вы свои квадранты забыли разбить по третьему вектору — требованиям к производительности железа. Это не ресурсы (память).


                          1. khim
                            21.07.2016 18:31

                            Ну а конкретную размерность я уже не помню.
                            Если не помните — то зачем выступаете?

                            По Win95 — извините, но она сама жрёт минимум те самые 16 мегабайт — без свапа вы на 32х метрах очень мало что запустите… и покрашитесь с невозможность аллоцировать память.
                            Вот только не надо грязи. Windows нормально работает в 16MiB. Вот на машинках с 8MiB, которые нам как-то поставили — да, туго было, там только Windows 3.x нормально ходила. А на 32MiB уже можно и Windows98 поставить (да-да, с позиций современного дня у них «почти одинаковые требования… то есть очень мало» — а на практике Windows98 сжирала, за счёт интеграции Active Desktopа почти вдвое больше памяти).

                            Оно не смогло бы не тормозить.
                            Вот прямо даже так? А вам напомнить что Visual Studio 6 — это 1998й год? Год, когда Pentium MMX — это была норма (Pentium II только-только появился и мало у кого был), а 64MiB памяти — максимум, который поддерживал славный 430TX? Или вы хотите сказать, что тогда прям у всех разработчиков стояла самая топовая конфигурация, какая тогда была возможна?

                            На той вашей сестринской «печатной машинке» какая ОС?
                            Windows XP, однако. Зачем там больше?

                            Тому же Idea очень хочется скушать около гига. И начинается та же свистопляска со свапом… И это, по вашему, java виновата?
                            Вы же сами вроде ответили:
                            Ну а в отличие от производительности — с памятью вопросов нет, Java действительно её любит.
                            Да, конечно Java виновата — тут даже вопросов нет. Заметьте, что «скушать около гига» — это изрядная нагрузка и на процессор, не только на память, байтики не сами прыгают…

                            Ну и на Intel Atom c 2мя гигами оперативы… у меня вот в такой конфигурации браузеры тоже только в путь тормозят…
                            С выключенным JavaScript'ом и вырезанным CSS? Позвольте мне не поверить…

                            Они, видимо, тоже на managed языках написаны, да?
                            Увы. В современных браузерах изрядная часть функционала написана на языке который, похоже, существует для того, чтобы в ответ на «но хуже Java быть уже ничего не может, ведь так» «оптимистично» ответить «нет — может, ещё как может».

                            На Athlon XP 2100+ он тормозил на XP-шке при толстых видяхах, с чего он на более слабом Atom'е-то будет летать? Или вы только хардварно-декодируемое видео смотрите?
                            Вот давайте не путать божий дар с яичницей. Понятно, что если вы в видеопроигрыватель засунете 4K видео, которые процессор физически не может переварить — то тут уже ничего не сделать. Я про интерфейс. Когда я смотрю видео на этой самой «печатной машинке» — да, оно может дёргаться и замирать. Но перемотка — работает без «залипаний», выход в меню — тоже не тормозит. И окошки нормально таскаются. А вот на телефоне со сравнимой конфигурацией — я наблюдаю именно такой «фан и экспириенс»: постоянные «лаги» и «залипания». Во многом — потому что Java сжирает все ресурсы, а 10x запаса по мощности, чтобы это скомпенсировать у телефона нет.

                            Ненешние флагманы, впрочем, наконец-то решили эту проблему — грубой силой: четырёх-восьмиядерные процессоры и гигабайты памяти.

                            Ну вот возьмите свою банковскую карту — не исключено, что она используется https://ru.wikipedia.org/wiki/Java_Card
                            Как же вы любите «лезть в бутылку».

                            Вы по своей ссылке-то ходили нет? Цитирую: многие возможности языка Java не поддерживаются в Java Card, например, типы char, double, float и long, ключевое слово transient, перечислимые типы (enum), многомерные массивы, финализаторы, клонирование объектов, потоки.

                            На практике — всё ещё хуже, Wikipedia недоговаривает. Я-то с этой платформой работал, а вы — сюдя по тому, что на это позорище ссылаетесь — нет. Финалайзеров там нет потому что работы с динамической памятью там нет в принципе. В резльтате получаем чудо-юдо, где все массивы и объекты — глобальные (удалять их нельзя никак, а время жизнь кардлета начинается не тогда, когда вы карточку в банкомат засовываете, а когда вы оный кардлет прошиваете — кончается, соответственно, когда его удаляют), никакие Java-библиотеки использовать нельзя (много вы знаете библиотек, не создающих никаких объектов за всё время своей жизни?).

                            Какое отношением это поделие вообще отшение имеет к Java? Если смотреть на него с инженерной, а не маркетинговой, точки зрения?

                            Если же говорить о производительности (с чего и начался весь срач «Java тормозит») — то современная Java при достаточных ресурсах по памяти, ещё раз повторюсь, мало отличается по производительности от c++.
                            Мало отличается — это, по вашему, во сколько раз медленнее? В два раза? В десять? Ну если подобное отличие для вас ерунда — то да, наверное. А я таки руководствуюсь мнением Кнута (да-да, того самого, который изрёк знаменитое изречение, которое очень любят цитировать любители Java, про «преждемвременную оптимизацию»): «В устроявшихся инжинерных дисциплинах улучшение на 12%, достижимое без черезмерных затрат, никогда не считается несущественным и я верю что такая же точка зрения должна быть применена в программировании» (Knuth, Donald (December 1974). «Structured Programming with go to Statements». ACM Computing Surveys 6 (4): 268.)

                            И, как я уже сказал, я пока не видел ничего, написанного на Java, что укладывалось бы в эти 12%.


                            1. webkumo
                              21.07.2016 19:12

                              > Вот только не надо грязи. Windows нормально работает в 16MiB. Вот на машинках с 8MiB, которые нам как-то поставили — да, туго было, там только Windows 3.x нормально ходила. А на 32MiB уже можно и Windows98 поставить (да-да, с позиций современного дня у них «почти одинаковые требования… то есть очень мало» — а на практике Windows98 сжирала, за счёт интеграции Active Desktopа почти вдвое больше памяти).

                              На моей практике на 8Мб W95 вообще не завелась. На 12ти — да. Кстати устанавливалась 6 часов… Intel80386DX + сопр. Можно я буду его тормозом называть за это? Нет? А почему? Вы же Java называете тормозом, не давая ей достаточно ресурсов.

                              > Вот прямо даже так? А вам напомнить что Visual Studio 6 — это 1998й год? Год, когда Pentium MMX — это была норма (Pentium II только-только появился и мало у кого был), а 64MiB памяти — максимум, который поддерживал славный 430TX? Или вы хотите сказать, что тогда прям у всех разработчиков стояла самая топовая конфигурация, какая тогда была возможна?

                              И вы прям помните, как на 32ти метрах оно летало? И вот ну совсем не тормозило? Извините, но у меня страшное подозрение, что вы лукавите. Впрочем, если это было единственное приложение и достаточно мелкий проект — возможно так и было.

                              > Windows XP, однако. Зачем там больше?

                              Ради обновлений безопасности? Ради совместимости с современным железом (внешние устройства)? Ради совместимости с современным ПО? (Хотя с такой конфигурацией последний вопрос не самый актуальный… это действительно печатная машинка, незачем туда ПО ставить).

                              > Да, конечно Java виновата — тут даже вопросов нет. Заметьте, что «скушать около гига» — это изрядная нагрузка и на процессор, не только на память, байтики не сами прыгают…

                              Если память не дико фрагментированная — хрен там, а не изрядная нагрузка на процессор. Java аллоцирует под себя память максимально доступными блоками (т.е. будет доступно подряд гиг и ей он будет нужен — она его целиком и сожрёт). Да и это накладные расходы запуска приложения, а не его работы.
                              И заметьте, если вы не соблюдаете блок «системные требования», то виновато не ПО, которое тормозит/падает, а вы. Надо это чётко понимать.

                              > С выключенным JavaScript'ом и вырезанным CSS? Позвольте мне не поверить…

                              Позвольте мне спросить — зачем вам браузер?

                              > Увы. В современных браузерах изрядная часть функционала написана на языке который, похоже, существует для того, чтобы в ответ на «но хуже Java быть уже ничего не может, ведь так» «оптимистично» ответить «нет — может, ещё как может».

                              Позвольте мне не согласиться. JS хоть и имеет множество косяков — остаётся вполне вменяемым языком, на котором можно сделать любой web-UI. Заметьте, всё более «эффективное» почему-то мрёт в вебе. Поэтому называть его плохим языком — я бы не спешил.

                              > Как же вы любите «лезть в бутылку».

                              Не больше, чем вы.

                              > Какое отношением это поделие вообще отшение имеет к Java? Если смотреть на него с инженерной, а не маркетинговой, точки зрения?

                              Не поверите — вы мне не сообщили ровным счётом ничего нового. А поделие это — подмножество языка, эффективно работающее с памятью. Как вы и хотели.

                              > Мало отличается — это, по вашему, во сколько раз медленнее? В два раза? В десять? Ну если подобное отличие для вас ерунда — то да, наверное. А я таки руководствуюсь мнением Кнута (да-да, того самого, который изрёк знаменитое изречение, которое очень любят цитировать любители Java, про «преждемвременную оптимизацию»): «В устроявшихся инжинерных дисциплинах улучшение на 12%, достижимое без черезмерных затрат, никогда не считается несущественным и я верю что такая же точка зрения должна быть применена в программировании» (Knuth, Donald (December 1974). «Structured Programming with go to Statements». ACM Computing Surveys 6 (4): 268.)

                              Мало отличается — это мало отличается. На уровне плюс-минус десять процентов. И я не руководствуюсь ничьим мнением. Но вот согласно мнению вами любимого Кнута — Java использовать стоит. Т.к. улучшение сильно больше 12% — время разработки (по сравнению с c++) сокращается в 2-3 раза. Вы просто вбили себе в голову, что java тормозная… но ведь нет! Она может решать некоторые классы задач на вполне нормальной скорости. Инженерная аналогия — стоит ли строить мост в 4 полосы за n+m денег (стройка + инженерные расчёты) и t времени или лучше построить 2 двухполосных моста за n+m*2/3 денег, каждый из которых можно построить за t/3 времени? По сути в этой аналогии достигается экономия на расчётах (проще и частично перекликаются), экономия на времени (можно одновременно строить) и потенциальная инфраструктурная плюшка — легче развязывать потоки машин + легче обслуживать (в крайнем случае один мост можно временно закрыть).

                              > И, как я уже сказал, я пока не видел ничего, написанного на Java, что укладывалось бы в эти 12%.

                              А я видел мало тормозящих Java-программ. И много — не тормозящих. И в большинстве случаев тормоза случались на слабых по объёму памяти машинах.


                              1. khim
                                22.07.2016 01:48

                                На моей практике на 8Мб W95 вообще не завелась. На 12ти — да. Кстати устанавливалась 6 часов… Intel80386DX + сопр. Можно я буду его тормозом называть за это?
                                Да, конечно — по сравнению с Windows 3.x это был монстр и тормоз. Но на 8MiB он таки заводился — если у вас этого не получилось то это скорее говорит о кривых руках, чем о тормознутости Windows 95. Официальные требования вообще о 4M говорят. Вы точно уверены, что вас память не подводит и это была Windows 95, не Windows 95 OSR2 (реально требовавшая уже 8MiB) или Windows 98 (которой было нужно ажно 16MiB)?

                                Нет? А почему?
                                В том-то и дело, что нипочему. Я — человек простой и называю чёрное чёрным, а белое — белым, представьте себе. Windows95 — это очень тяжёлая операционка, если сравнить её, скажем, с Windows 3.x (Windows 3.0 вполне ходила на 1MiB, хотя для Windows 3.1 этого, уже, в общем, стало не хватать — официальные требования не врут) — но вполне себе лёгкая, если сравнивать её с Windows NT (эта вообще каких-то космических ресурсов требовала). Не очень понятно, почему вы считаете, что я буду это отрицать.

                                В обоих случаях известно за что вы платите: в случае с Windows 95 — за возможность написания 32-битных многопоточных программ (ну и за красивые кнопочки, конечно, но красивые кнопочки и в Windows 3.x можно было нарисовать, в общем-то), в Windows NT — за возможность изолировать программы друг от друга (с некоторыми оговорками, правда, ради скорости и уменьшения потребления ресурсов Windows NT 4.0 сделала большой шаг назад).

                                Хотите ли вы за эти красивости платить повышенными требования к железу — решать вам, но назвать их «незначительно отличающимися» я не могу: даже требования Windows 95 и Windows 98 прилично отличаются друг от друга (официальные, кстати — тоже), а Windows NT — так вообще тормоз…

                                То же самое и с Java: да, я знаю, что она позволяет использовать труд условных «неквалифицированных индусов» и тем самым ускорить разработку. Но от этого она не перестаёт быть тормозным, тяжёлым, монстром. Это просто разные вещи, не нужно их в одну кучу валить.


                                1. webkumo
                                  22.07.2016 10:31
                                  +1

                                  > Да, конечно — по сравнению с Windows 3.x это был монстр и тормоз. Но на 8MiB он таки заводился — если у вас этого не получилось то это скорее говорит о кривых руках, чем о тормознутости Windows 95. Официальные требования вообще о 4M говорят. Вы точно уверены, что вас память не подводит и это была Windows 95, не Windows 95 OSR2 (реально требовавшая уже 8MiB) или Windows 98 (которой было нужно ажно 16MiB)?

                                  За редакцию не скажу (хотя никаких дополнительных символов после «95» я в названии не видел). Но точно 95. 98ая на 386 как-то не хотела устанавливаться.

                                  > В том-то и дело, что нипочему. Я — человек простой и называю чёрное чёрным, а белое — белым, представьте себе. Windows95 — это очень тяжёлая операционка, если сравнить её, скажем, с Windows 3.x (Windows 3.0 вполне ходила на 1MiB, хотя для Windows 3.1 этого, уже, в общем, стало не хватать — официальные требования не врут) — но вполне себе лёгкая, если сравнивать её с Windows NT (эта вообще каких-то космических ресурсов требовала). Не очень понятно, почему вы считаете, что я буду это отрицать.

                                  Да вы не «простой человек» и не «называете чёрное чёрным». Вы действуете как фанатик. И машину окрашенную с одной стороны чёрным, а с другой — почти белым называете чёрной. И из-за своего отношения (а не реальных проблем языка и платформы) вы упорно не хотите слушать оппонентов, измором пытаетес всех убедить, что «java тормозит», передёргиваете слова (был спор о производительности, вы откуда-то вытащили потребление памяти)… Вот поэтому я и считаю, что вы будете отрицать.

                                  > Хотите ли вы за эти красивости платить повышенными требования к железу — решать вам, но назвать их «незначительно отличающимися» я не могу: даже требования Windows 95 и Windows 98 прилично отличаются друг от друга (официальные, кстати — тоже), а Windows NT — так вообще тормоз…

                                  Заметьте, вы ни разу не сказали «тормоза» по отношению к W95. А ведь там та же ситуация — за большие возможности приходится платить большим потреблением. Только Java при достатке памяти на многих классах (специально для отмечаю — не на всех! НО на многих!) задач имеет производительность сравнимую с c++.

                                  > То же самое и с Java: да, я знаю, что она позволяет использовать труд условных «неквалифицированных индусов» и тем самым ускорить разработку. Но от этого она не перестаёт быть тормозным, тяжёлым, монстром. Это просто разные вещи, не нужно их в одну кучу валить.

                                  Да плевать на индусов. В c++ они тоже работают. И драйверы на C пишут. Причём тут индусы вообще? Речь именно об ускорении разработки при получении сравнимой производительности… платить приходится объёмами памяти — да. И если вы назовёте её тяжёлым монстром — никто не возмутится. Если вы скажете, что на каких-то классах задач она тормоз — тоже все промолчат. А вот общая классификация как «тормозной тяжёлый монстр» — это извините, наглая ложь. И вы фанатично(!) пытаетесь это сказать (даже не доказать). Кстати, в соседнеё теме про планируемые изменения в c++ вас тоже легко узнавать по горячности и абсолютности утверждений.


          1. khim
            20.07.2016 23:43
            +3

            Разработчику не жалко потратить десятки а иногда сотни тактов процессора чтобы избежать лишнего доступа в кеши процессора или в оперативку.
            А давайте не обобщённо «десятки и сотни», а более предметно. Возьмём данные отсюда (они, правда, немного устарели, но порядок величин показывают верно): L1 — 3-4 тактов, L2 — 10-15 тактов, доступ в память — ~200 циклов (на серверных процессорах есть ещё L3 — он где-то посередине).

            То есть: память — это сотни тактов, L2-L3 — десятки, L1 — единицы.

            А теперь фокус: байткод и интерпретатор зачастую вместе занимают меньше места чем соответствующий ассемблер.
            Это ни разу не фокус, этим пользовались ещё разработчики Excel'я в далёком 1985м году, чтобы уместить своё творение на минимальное число дискет. Размер — это действительно экономит. А вот получить этого выигрыш в скорости — штука «посильнее „Фауста“ Гете».

            Кеш L1 (самый важный) — он маленький. По нынешним меркам — просто крошечный. PowerPC 1991 года — 16K, Pentium MMX 1997 года — 32K, но даже в самом современном Skylake — всего лишь 64K, увы. Вы серьёзно хотите сказать что у вас туда уместится и приложение и JIT и скомпилированный код? Который при этом ещё и будет быстрее того, что породил компилятор C++?

            Достигается это тем что JVM байт-код это хорошо упакованные высокоуровневые инструкции, которым могут соответствовать десятки команд ассемблера.
            Правильно. Но есть одна проблеммка: интерпретировать его долго, мы тут не зря JIT'ы обсуждаем. А вот скомпилированный JIT'ом код — это не только, собственно, вычисления, но ещё и дополнительные проверки и логика на случай «изменения направлений движения» и прочее. И занимает этот код больше, чем то, что породил C++. Ненамного, но больше. А ваш этот суперлаконичный код где-то сидит — это тоже сверх того кода, который нужно, собственно, исполнить.

            Вот так и получается что действия, которые выполняются относительно редко (95% всей логики), логично интерпретировать.
            Потому что скорость работы этого кода ни на что не влияет — да, именно так. Это и в программах на C++ так и в Java и где угодно. Но это не очень важно — важна скорость работы «горячего» кода.

            Готов поспорить что если гипотетически скомпилировать ее в машинные команды (реально не получится ибо рефлекшн), то эти 5-20 минут разогрева потратятся просто на загрузку разбухшего кода в память и прогрев кешей.
            А это — уже от архитектуры приложений зависит. Chrome имеет сравнимое количество кода — но «прогреваться» после запуска ему не нужно.

            Да и вообще — вы просто посчитайте. Даже очень плохонький винчестер даёт скорость загрузки порядка 5-10MiB в секунду при дикой фрагментации диска. Вы всерьёз хотите сказать, что у вас Idea загружает в себя несколько гигабайт кода?

            В общем JIT и Java — это такой «путь китайского комсомола»: сначала насоздаём себе трудностей, а потом будем героически их преодолевать.

            Достижения JIT'остроителей впечатляют, да, но не следует забывать что все усилия их разработчиков привели лишь к тому, что мы получили скорость «почти такую же как при использовании технологий 30-летней давности».

            Такой себе Space Shuttle: то, что летает — это чудо, это реально офигительный подвиг, разработчики сотворили почти немыслимое. А вот то, что это чудо вообще кто-то задумал, не то, что реализовал — это ошибка. Большая ошибка.


            1. sumanai
              21.07.2016 10:28

              > на серверных процессорах есть ещё L3 — он где-то посередине
              L3 уже давно пришёл на десктопы. У меня его 6МБ, и мой процессор далеко не топовый и современный.


    1. rfq
      18.07.2016 23:18
      -7

      А они и работают быстро. Отстают от С++ в 1.5...2 раза: http://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=java&lang2=gpp


      1. chabapok
        19.07.2016 01:34
        +15

        Сходил я по вашей ссылке, посмотрел. Ясное дело, не все.
        Посмотрел я мандельброт. Ну что сказать, автор некорректно написал java-код — и поэтому, предположительно, поймал на нем эффект частой миграции данных между кэшами (в данном случае — это true sharing). Конкретно проблемы с этой строчкой: while((y=yCt.getAndIncrement())<out.length) Вы представьте: каждое getAndIncrement() должно подтянуть кэшлайн с новым значением себе. Ясное дело — это около out.length кэш-промахов. Причем, там тяжелый случай кэш-промаха, предположительно. В отличии от OMP, которое разделило диапазоны до старта цикла.
        Ну и плюс, запущено без прогрева. Никто не обещал, что до прогрева оно будет летать.
        Ну и плюс автор вывод c++ направил в файл (по крайней мере, возможность предусмотрел — третьим параметром идет имя файла), а вывод java направил в консоль. Консоль — штука не очень быстрая и непредсказуемо-тормозная, так что он померял в итоге непонятно что. Естественно, я его тест переписал, направив и java тоже в файл. по-сути, заенил одну строчку на: OutputStream stream = new java.io.FileOutputStream(«xyz»);

        Результаты:

        # time java -server -XX:+TieredCompilation -XX:+AggressiveOpts Mandelbrot 16000 xyz

        real 0m6.211s
        user 0m22.394s
        sys 0m0.117s

        (^^^^это i586 версия)

        # time /usr/local/jdk1.8.0_91/bin/java -server -XX:+TieredCompilation -XX:+AggressiveOpts Mandelbrot 16000 xyz

        real 0m5.774s
        user 0m20.944s
        sys 0m0.151s

        (^^^^ это x64)

        # time ./mandelbrot.gpp-9.gpp_run 16000 xyz

        real 0m7.330s
        user 0m27.380s
        sys 0m0.039s

        и это результат когда java с атомиками работает. Если переписать без них — можно добиться еще большего. Это на amd, кстати.


        1. chabapok
          19.07.2016 02:03
          +7

          проба этого же теста на Intel® Core(TM)2 CPU E7500 @ 2.93GHz
          На компе стоит i586 операционка, поэтому OMP версию пришлось перекомпилить с ключиком -m32. Так же, java там 1.8.0_51 (тоже i586, ясное дело), обновляться до 1.8.0_92 было влом.
          Результаты:

          # /tmp/3$ time ./mandelbrot.gpp-9.gpp_run 16000 xyz

          real 0m31.277s
          user 1m1.756s
          sys 0m0.132s

          # time java -server -XX:+TieredCompilation -XX:+AggressiveOpts Mandelbrot 16000 xyz

          real 0m27.763s
          user 0m54.036s
          sys 0m0.300s


        1. lany
          19.07.2016 06:59
          +7

          Интересно, зачем вы с -XX:+AggressiveOpts запускаете? Это сознательный выбор или просто вам кто-то когда-то сказал, что так что-то будет быстрее?


          На 64-бит -server и -XX:+TieredCompilation работает по дефолту, а -client вообще игнорируется.


          1. chabapok
            19.07.2016 12:49
            +1

            Да.
            Я — не использую, использовал автор исходников. Там строка запуска (а в случае с++ версии — строка запуска компилятора тоже) под исходником находится. Было 2 ночи, собирался спать, думал по-минимуму — копипастил по-макимуму.

            Наверное, если бы результат вышел плохой, это заставило бы задуматься и что-то сделать, но результат оказался хорошим — и я на этом остановился.


            1. lany
              19.07.2016 12:55
              +3

              В вашем случае -XX:+AggressiveOpts скорее всего ни на что не влияет. Просто это довольно дурная опция. В разных версиях JVM она может включать разные штуки, которые не всегда нужны (не зря же они выключены по дефолту). Поэтому не стоит пользоваться ей слепо, не зная, что конкретно она включает в вашей JVM и нужно ли это вам.


        1. igouy
          19.07.2016 20:57
          +2

          > Ну что сказать, автор некорректно написал java-код

          benchmarksgame.alioth.debian.org/play.html#contribute

          > Ну и плюс, запущено без прогрева. Никто не обещал, что до прогрева оно будет летать.

          benchmarksgame.alioth.debian.org/sometimes-people-just-make-up-stuff.html#jvm-startup-time

          > Консоль — штука не очень быстрая и непредсказуемо-тормозная, так что он померял в итоге непонятно что.

          /dev/null

          benchmarksgame.alioth.debian.org/how-programs-are-measured.html


          1. chabapok
            19.07.2016 22:26
            +4

            Нет, писать ему багрепорты и править его код я не стану. Это вы ему отправляйте ссылку на хабр, пускай читает критику и правит.

            в любом случае, у меня результаты получаются не такие, как у него.
            Сейчас попробовал его fannkuch-redux (java тоже некорректно написана), запускал раз 10 оба примера. Вердикт — они по времени работают одинаково, 11...12сек, и я бы не сказал, что имеется перевес в чью-то пользу. Наверное, можно было бы усреднить и получить результат, но нет смысла. т.к. все равно в java версии использованы атомики, а плюсовая разделена на независимые треды заранее. т.е. сравнение некорректно.

            Не знаю, почему он получил видимую разницу. У него старое железо(«q6600 — проц для широких масс вдвое дешевле» — гугл), а такое железо как раз плохо может работать при конкурентном доступе к данным — то есть как раз с атомиками. Наверное, поэтому. Скорей всего, если написать тесты одинаково, то разница была бы в пределах погрешности.


            1. 31415
              20.07.2016 05:56
              +3

              Судя по тексту ответа он и написан автором Benchmark Game.
              А вообще забавно, конечно — как только в интернетах начинают обсуждать Benchmark Game, его автор сразу же появляется в комментариях. Как он это делает — ума не приложу :)


              1. igouy
                20.07.2016 20:14
                +1

                > Как он это делает — ума не приложу :)

                :-)

                https://www.google.com/search?q=benchmarksgame#q=benchmarksgame&tbs=qdr:d


                1. 31415
                  20.07.2016 20:44

                  Ahaha! Welcome to Soviet Russia, Isaac :)
                  Now you'll have to learn russian for the sake of comfortable discussion :)


                  1. igouy
                    20.07.2016 21:16

                    Спасибо!


    1. youROCK
      18.07.2016 23:25
      +3

      Скорее проблема в том, что Java в начале «тормозит», и во время разработки программы обычно до срабатывания JIT дело не доходит. То есть, разработчики привыкают к тому, что их программы «тормозят». Потом добавляют какую-нибудь сложную бизнес-логику, кучу SQL-запросов и т.д. Во время разработки, поскольку всё итак тормозит, проблема обычно не заметна. А когда выкладывают на продакшен, то оказывается, что JIT программу особо не ускоряет, потому что она не проектировалась с фокусом на скорость.
      То есть, несмотря на то, что в теории программы должны быть быстрые, по факту они бывают достаточно быстрыми только в определенных случаях (например, игры обычно неплохо оптимизированы, или тот же Hadoop).


  1. voddan
    18.07.2016 23:33
    +3

    Про магию JVM лучше всего слушать Шипилева, рекомендую например вот это: https://youtu.be/nHiEKXpG_4M
    Примерно в середине доклада вроде рассказывает про спекуляции с виртуальными методами тоже (тема статьи)


    1. jbaruch
      19.07.2016 19:35
      +7

      Я думаю, что lany – выбор не хуже в данном случае.


      1. lany
        20.07.2016 06:28
        +6

        Ну в вопросах производительности JVM мне до Performance Tsar, конечно, очень далеко. Но он же не может написать все статьи на эту тему, поэтому иногда пишут другие люди :-) Шипилёв читал черновик этой статьи, сильно не придирался :-)


  1. sunless
    19.07.2016 00:02
    +1

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

    Читайте и смотрите классику: вот свежий часовой доклад от человека, который задизайнил JIT компилятор хотспота в свое время: https://www.infoq.com/presentations/java-vs-c-performance


    1. khim
      19.07.2016 05:24
      +8

      Есть сценарии, которых бесконечно много, но можно выделить основные, типа «векторная математика, графика, работа с бд», и так далее.
      Сценариев, конечно, «бесконечно много», но, в общем, их не так сложно рассклассифицировать.

      Начать нужно с конца. Работать всем нашим сценариям предстоит, представьте себе, на CPU. А у CPU есть кеш. 64K, как говорит нам Wikipedia. Который либо поступает в распоряжение программы, либо делится между JIT'ом и программой.

      То есть если у нас программа имеет статический профиль (а «суперчудеса» все JIT'ы демонстрируют на статическом профиле), то удел JIT'а — получить результат чуть хуже, чем у приличного статического компилятора. Если же в программе типов много и они меняются часто, то скорость всего хозяйства получается — «ниже плинтуса».

      То есть хотя теоретически JIT и может выиграть у компилятора C++, но происходит это примерно так же часто, как солнечные затмения: хорошо если раз за свою жизнь увидите. Можно, конечно, сесть на самолёт и слетать куда-нибудь… тогда чаще. Так часто бенчмарки делают: смотрим на 100500 задач, отбираем парочку где наш JIT особо хорош… вуаля: есть статья. Но если вам нужна не статья в журнал, а программа, то увы: на практике вы этих чудес не увидите.

      Тем не менее современные JIT'ы уже дошли, наконец, до состояния, когда вся эта конструкция (убогий, безумно тормозной, байткод — плюс JIT, который призван всё это компенсировать) неплохо работает. При двух непременных условиях, впрочем:
      1. Вы должны работать с данными без всякого полиморфизма (иначе JIT не сможет «развернуться»).
      2. Вы должны не забывать о том, что если вам нужно загрузить и обработать гигабайт-другой данных, то ваш сервер неплохо бы оснастить несколькими гигабайтами памяти — примерно так втрое больше, чем вам реально нужно. А иначе вам GC всю малину испортит.
      2a. Есть, правда, выход: вывести GC «за скобки». Выделить столько массивов простых типов, сколько вам нужно, работать там со смещениями — и вуаля: скорость примерно как у C/C++! Вот только зачем вам при таком подхое Java вообще? Понятно, конечно, что настоящий программист программист может писать фортрановские программы на любом языке… но зачем? Пишите фортрановские программы на фортране — и да пребудет с вами сила!


      1. sunless
        19.07.2016 07:29
        +5

        у CPU есть кеш. 64K, как говорит нам Wikipedia. Который либо поступает в распоряжение программы, либо делится между JIT'ом и программой.


        JIT не работает долго на прогретой программе (а джавы сереверные программы работают часто неделями). Реальный профиль работы CPU примерно как на слайде 23 вот этой презентации: http://www.slideshare.net/ZeroTurnaround/vladimir-ivanovjvmjitcompilationoverview-24613146 GC — да — кушает процессор — особенно concurrent, это плата на managed свойства языка, как спору AOT-JIT почти не имеет отношения.

        То есть если у нас программа имеет статический профиль (а «суперчудеса» все JIT'ы демонстрируют на статическом профиле)


        Ровно наоборот. Как раз статический профиль можно через PGO механизмы подать в gcc/clang и получить идеальный профиль. JIT способен отработать изменения поведения программы и рекомпелироваться. Например, если код, который был прогрет и нормально работал, вдруг начинает бросать exceptions. Кроме того, JVM перемещает скомпелированный код, что позволяет часто взаимодействующему коду находиться вместе, сокращая TLB misses. Наконец, уникальная возможность — можно прицепиться к программе, могда метод с брейкпоинтом уйдет в интерпретатор, после отключения bp обратно рекомпелируется,

        1. Вы должны работать с данными без всякого полиморфизма (иначе JIT не сможет «развернуться»).


        С умеренным полиморфизмом (а это 2-3 имплементора виртуальной функции) JIT справится, а не хуже AOTа, дальше — беда, как правило — для всех.

        ваш сервер неплохо бы оснастить несколькими гигабайтами памяти — примерно так втрое больше, чем вам реально нужно


        Это почти справедливо, но это расходы GC, а не JITа. Это другая тема. Я плохо знаком с .NЕT миром, но там есть AOT + GC. Всё тоже самое с точки зрения расхода памяти. И да, не втрое, а примерно +30% для долгоживущих объектов и в 2 раза для «молодого поколения». Это длинная тема.

        Есть, правда, выход: вывести GC «за скобки». Выделить столько массивов простых типов, сколько вам нужно, работать там со смещениями — и вуаля: скорость примерно как у C/C++


        Это справедливо. Работа со структурами, с массивами структур, с вложенными массивами — самое слабое место Java. Ждём десятку — arrays 2.0, value types и и.п. Но к JIT это не имеет отношения, это слабость языка. JIT в .NET прекрасно работает с массивами структур.

        Есть еще одно слабое место именно JIT-a — это автовекторизация. JIT может найти возможность использовать векторные инструкции в простых паттернах, как и GCC/clang, но легко соорудить пример, с которыми ни один компилятор не справится, оптимизация «вручную» победит. AES шифрование например (по этой причине шифрование и хэширование делается интринсиками, а не компилятором).


        1. khim
          19.07.2016 14:03
          +1

          Ещё раз прочитайте что вы написали

          JIT не работает долго на прогретой программе (а джавы сереверные программы работают часто неделями)
          JIT способен отработать изменения поведения программы и рекомпелироваться"
          . Либо первое, либо второе, а одноверменно — примерно как наступление солнечного затемения.

          Да, если у вас паттерн использования резко сменился (скажем если к вам вместо обычных запросов вдруг начали посылать один, весьма специфический, в надежде вас за'DDOS'ить), то вы можете что-то отыграть — но как часто это случается?

          Это почти справедливо, но это расходы GC, а не JITа. Это другая тема.
          Это та же тема: «великораспильные языки» против «старой школы».

          Объясняю свою позицию: когда-то давно, много лет назад, когда IBM PC 4.77MHz уже немного устаревала, но для школы годилась я столкнулся с «типа крутым редактором» (интересно — как он назывался? сейчас уже не вспомнить, но помню что там были разные фишки типа спеллчекера). И была у него одна проблема: на большинстве машин в классе со 256KiB он не работал, требовал «полных» 640KiB. А ещё он осталавливался на полсекунды каждые примерно секунд пять. На вопрос в духе «а почему собственно» поступил ответ в духе «ту эта, у нас тут немного больше памяти требуется, а ещё задержки от сборщика мусора и байткода — но вы не волнуйтесь, скоро мы всё починим». С тех пор идут годы — и заклинание «вы не волнуйтесь, скоро мы всё починим» в отношении managed языков не меняется. Почему я всю эту конструкцию считаю шарлатанством и выкидыванием денег не ветер.

          P.S. Есть ещё второй подобный пример, кстати. Когда C++ (и STL) только появились, то многие высказывали недовольство тем, как там сделано метапрограммирование: вместо того, чтобы дать возможность программисту точно описать — чего он, собственно, хочет в C++ принято порождать программы, которые, если компилировать их без оптимизации, «в лоб», будут порождать терабайты кода — в надежде на то, что компилятор всё «подчистит». И там, тоже, до сих пор это аукается. Я принимаю и Java и C++ (свои задачи они, в общем, решают — хоть и с оговорками), хотя меня до сих пор терзает вопрос: а если бы все миллиарды, которые мы вбухали в попытку (частично успешную) обойти проблемы, которые мы же сами себе и создали — как бы изменится мир? Теперь уже не узнать…

          AES шифрование например (по этой причине шифрование и хэширование делается интринсиками, а не компилятором).
          Там слишком сложный паттерн, чтобы его мог хоть один компилятор в мире распознать и в AES-NI завернуть. Автовекторизация в JIT'ах пока слабее, чем clang'е или gcc — но это, я думаю, временно. Ничего смертельно сложного там нет.

          Работа со структурами, с массивами структур, с вложенными массивами — самое слабое место Java. Ждём десятку — arrays 2.0, value types и и.п. Но к JIT это не имеет отношения, это слабость языка.
          А я, в общем, и обсуждаю в основном язык.

          JIT, это знаменитое:
          Один пришивает карман, один — проймочку, я лично пришиваю пуговицы. К пуговицам претензии есть?
          Так вот у меня претенции не к пуговицам, а к тому, что путём увеличения их размера и количества пытаются сделать носибельным фасон костюма, который, по хорошему, вообще не должен был бы использоваться!


          1. sunless
            19.07.2016 14:34
            +2

            Ещё раз прочитайте что вы написали

            JIT не работает долго на прогретой программе (а джавы сереверные программы работают часто неделями)

            JIT способен отработать изменения поведения программы и рекомпелироваться"

            . Либо первое, либо второе, а одноверменно — примерно как наступление солнечного затемения.

            Да, если у вас паттерн использования резко сменился (скажем если к вам вместо обычных запросов вдруг начали посылать один, весьма специфический, в надежде вас за'DDOS'ить), то вы можете что-то отыграть — но как часто это случается?


            Здесь нету противоречия. JIT стартут вместе с JVM, работает в N потоков, пока не закончится очередь CompileQueue и JVM не придет в некое стабильное прогретое состояние. Компиляция одного метода занимает доли секунд, а приложение работают, часто, неделями. Если требуется рекомпиляция, то основной проигрыш от того, что во время рекомпиляции метод интерпретируется, что на порядок медленнее, но собоственно компиляция проходит быстро и безболезненно. Я как раз готовлю доклад про деоптимизации, так вот довольно сложно даже написать такой тест, чтобы проблема была ощутимо заметна.

            А я, в общем, и обсуждаю в основном язык.

            Мы же обсуждаем статью «JIT-компилятор оптимизирует не круто, а очень круто»? Здесь мог бы быть любой из более чем сотни языков, которые работают на JVM. Мог бы быть даже .NET + CLR — уверен, что JIT в нем работает примерно также, при этом и «убогого байткода» там нет, и других недостатков, вроде отсутствия массивов структур и контроля для лейаутом


      1. chabapok
        19.07.2016 13:58
        +4

        Вы в своем рассуждении не учли:
        — если профиль динамический — он (по факту, хоть и не собирается) будет динамический и на с++ тоже, поэтому глупо ждать какой-то существенной просадки «ниже плинтуса» производительности в java — но при этом считать что с++ будет по прежнему «летать».

        — JIT, в отличии от «приличного статического компилятора» может компилить, используя особенности платформы, на которой работает, и за счет этого тоже что-то выигрывать.

        — в 90ых было очевидно, кто тормозит. Сейчас jit компилятор хорош. По сути, g++ — это компилятор, jit — это тоже компилятор. Поэтому, сейчас эти расклады ненастолько очевидны. В jit было вкинуто очень много человеко-часов, а g++ — гнутая софтина, в которой некоторое время (4.9.0) вообще жили критичные баги и всем было пофиг. И нет возможности корректно сравнить jit и c++ на реальных проектах. Даже если сделать 1 проект на два раза на разных технологиях — решающую роль может оказать квалификация/специализация/предпочтения программиста. Поэтому, говорить «происходит это примерно так же часто, как солнечные затмения» — необоснованно. Вообще, на сегодня известен факт: в 99% случаев программа тормозит не в том месте, где думает программист.

        — в моих тестах выше по каментам подход «смотрим на 100500 задач, отбираем парочку» не использовался. Надо будет по свободе провести тесты с остальными примерами автора.

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

        — насчет вопроса «зачем вам при таком подхое Java вообще?» Очевидно, обработка гигабайт данных — это, в большинстве случаев, не 100% программы, а скорей 1%. java — для оставшихся 99%, которым ненужно так много памяти.

        Итог: я думаю, у вас предвзятое отношение к jit, оно у вас сформировалось исторически — потому, что действительно когда-то было оправдано так считать. Но платформа развивается.

        Есть еще интересное социальное явление: когда люди повторяя мантру «жава тормозит», пишут некорректный и тормозной код на плюсах. Я такое реально встречал на практике, например был у нас прецедент: json_spirit либа ела 80% проца. Перешли на java (и плюс размер данных увеличился раза в 3) — кушает 1..5%. Qt-шный json-парсер тоже, кстати, много проца ест. Не 80% конечно, но заметно больше. Так что ненадо жить представлениями 90ых. Отрасль развивается.


        1. khim
          19.07.2016 14:21
          +1

          Есть еще интересное социальное явление: когда люди повторяя мантру «жава тормозит», пишут некорректный и тормозной код на плюсах. Я такое реально встречал на практике, например был у нас прецедент: json_spirit либа ела 80% проца.
          С этим спорить не буду — на C++ тоже можно «Г» написать.

          Отрасль развивается.
          Если бы. Отрасль ходит по кругу. Или, скорее — движется по спирали идущей вниз. Вы не поверите, но у меня есть дома вот такая машинка. И на ней — есть GUI (совершенно не тормозящий… ещё бы, целых 66Mhz), редакторы, браузер и прочее (браузер, правда, запускается небыстро, спорить не буду). И он никогда не «уходит в себя» на час! А мой X1 Carbon — уходит! Да, я знаю, AGA поддерживает, в типичном случае, 640x480x256 цветов, а мой X1 — 2560x1440x16777216. Разница в потребной памяти — 48 раз. Но, казалось бы, современные технологии должны компенсировать? Фиг вам. Вот в этом-то и беда. Не туда мы куда-то идём…


          1. bromzh
            19.07.2016 14:30
            +2

            Ох, опять… Вы на этих машинах одни и те же приложения запускаете? Скорее всего нет. По возможностям старые программы могут посоперничать с современными? Сомневаюсь.
            Как-то глупо было бы, если железо развивалось, а софт не использовал это железо на максимум.


            1. khim
              19.07.2016 15:48
              +1

              По возможностям старые программы могут посоперничать с современными?
              В том-то и дело, что могут! По бздынь-бдыщ-какие-изящные-пимпочки — не могут, а именно по возможностям — могут легко. Какой-нибудь MultiEdit по своим возможностям не сильно отстаёт от славного SlickEdit'а — хотя да, он далеко не так красив, но, чёрт побери, он работал в 640KiB! На 4.77Mhz!

              Да, существуют программы, которые имеют то, чего принципиально было нельзя сделать на компьютерах 20-летней давности (хотя это ещё поискать нужно: та же Amiga телестудмиями использовалась вплоть до появления TVHD), но подавляющее большинство — требуют ресурсов на порядки (то есть не в 10 раз, а скорее в 100) больше, чем им реально нужно.

              Я понимаю почему так выгодно делать с точки зрения зарабатывания денег — но только после этого не надо рассказывать после этого как у нас всё эффективно и хорошо.

              Как-то глупо было бы, если железо развивалось, а софт не использовал это железо на максимум.
              Ещё глупее — делать вид, что все эти JIT-ухищрения приведут хоть к какому-то выхлопу, который увидит пользователь. Нет, они просто позволят разработчикам ввести ещё парочку уровней индирекции, которые сожрут весь выигрыш…


              1. bromzh
                19.07.2016 16:33
                +2

                а именно по возможностям — могут легко

                Емакс вон тоже может и на старых тачках запускаться. Однако, голый емакс мало кого устраивает, а вот дополнения уже нагружают систему. Я сомневаюсь, что MultiEdit потянет на 640KiB и 4.77Mhz все возможности, которые я использую в своём редакторе.


                Ещё глупее — делать вид, что все эти JIT-ухищрения приведут хоть к какому-то выхлопу, который увидит пользователь.

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


                Так что лучше я буду выглядеть глупо и верить, что JIT ускорит мой код. Тем более, что я сам видел это неоднократно.


          1. chabapok
            19.07.2016 14:38
            +1

            В редакторах наверняка нет каких-то полезных (ну или ненужных %) ) фич, в браузере нет всяикх канвасов-вебсокетов, и тд и тп.
            «морду» я и на zx-spectrum кое-как умудрялся делать — и в 48кб помещалось. Естественно, там никто даже не задумывался о том, чтобы работать с таким кол-вом данных, как это есть сейчас. И всякие эти разговоры, что раз в 4к-демо могли впихнуть столько — то у нас вот тут много занимает, они не учитывают, что в 4k-демо подгоняется ТЗ под программу, а в реальной жизни приходится делать наоборот.

            Ладно, это нас на философию потянуло. Я лишь говорил о том, что jvm из 90ых — это не jvm-2016. Туда вкинуто много труда, вылизано очень много всего. По сути, даже в 90ых сравнивали технологию, которой всего пару лет, с гораздо более старым и сформировавшимся gcc.


            1. khim
              19.07.2016 15:40

              Естественно, там никто даже не задумывался о том, чтобы работать с таким кол-вом данных, как это есть сейчас.
              Работали с теми данными, которые были тогда. Я лично редактировал файлы в ~300KiB на кружке по программированию на MSX-2 славным редактором mim. Недавно попробовал открыть лог аналогичного размера каким-то редактором под Android — он умер… вернее его система убила.

              Мощности современного телефона и MSX-2 сами сравните или вам данные из Wikipedia подтянуть?


              1. webkumo
                19.07.2016 17:53

                Вы тролль похоже… Вам «Мягкое», вы «кислое»…

                Впрочем посмотрим на ваш пример:
                — что за телефон (объём и параметры оперативной памяти, объём съедаемый ОС, разрешение экрана)?
                — что за приложение?
                Ну а в целом ответ-то на него такой: вы знаете на MSX-2 тоже можно было наговнокодить. Это было чуть-чуть сложнее из-за того, что уровней абстракций в коде было меньше и кода было меньше (из-за значительно меньшего функционала).
                А простенькое приложение FBReader грузит двухмегабайтный fb2 файл из zip-архива. Спокойно и без проблем.


  1. AxisPod
    19.07.2016 05:11
    +2

    Оптимизация на синтетике, круто конечно, но какова вероятность, что в реальном проекте такое случится, когда вам понадобится в реальном проекте, чтобы всегда приходил список из одного элемента?


    1. khim
      19.07.2016 05:26
      -2

      Ну дык Java же! Вернуть переменные из функции нельзя — вот и приходится извращаться… Обычно правда, используют массив из одного элемента, а не список…


      1. Borz
        19.07.2016 09:01
        -2

        да ладно?

        public int getMyInt(){}
        


        1. khim
          19.07.2016 13:41
          -1

          Давайте не делать вид, что вы не знаете о чём речь — а то получается спор ни о чём. Да, у функции есть результат — но иногда нужны и выходные переменные. Которых в Java нет. И все про это знают. И знают как обходить. А вот JIT знает и как эту неэффективность изводить теперь. Круто, конечно, но, может быть, проще было язык поприличнее сделать изначально?


          1. Maccimo
            19.07.2016 15:49

            Кому нужны и зачем?
            Вы уверены, что не пытаетесь «писать на Фортране» лишь потому, что так привыкли делать, а вовсе не потому, что это действительно «нужно»?

            > Круто, конечно, но, может быть, проще было язык поприличнее сделать изначально?

            Это всего лишь эмоции, а не аргумент.
            Руководствоваться эмоциями при выборе инструмента — не лучшая идея.


          1. Endeavour
            19.07.2016 16:43
            +1

            Пример из статьи относится к итерации (и ее уничтожению JITом) итератором по списку из одного элемента.
            При возврате переменной костылем в виде массива из одного элемента нет ни циклов, ни итераторов для уничтожения JITом.


          1. sleeply4cat
            19.07.2016 18:59
            +7

            Можете объяснить джависту, о чём вы спорите? :)


            1. chabapok
              19.07.2016 20:18

              Насколько я понял, они хотят, чтобы функция возвращала сразу несколько объектов.


            1. oprofen
              19.07.2016 20:56
              +6

              Мне кажется спор о передаче переменной по ссылке.


            1. playermet
              19.07.2016 22:07
              +4

              Не джавист, но думаю что он хочет передачу переменной в качестве аргумента метода по ссылке.


          1. lany
            20.07.2016 05:30
            +5

            Сделано нормально именно сейчас. У языка Java есть очень крутое свойство: локальную переменную метода может изменить только непосредственно сам этот метод и никто другой. Это существенно упрощает жизнь и спасает от кучи багов, особенно при многопоточном программировании. Не говоря уже о том, как помогает оптимизировать код JIT-компилятору и работать статическим анализаторам. Вы хотите передачу по ссылке? А если вызываемый метод запустит новый тред и обновит переменную уже в нём? Вызвав метод и передав туда локальную переменную, вы не будете знать, когда она изменится. Вся надёжность накроется медным тазом.


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


    1. lany
      19.07.2016 07:04
      +5

      Хороший вопрос. В реальном приложении этот метод полностью заинлайнится в точку вызова, причём в разные точки по-разному. Там, где singletonList, он свернётся в чтение поля. Там, где другой тип, будет более сложный код. В один пост подробный анализ этой ситуации уже не лез :-)


    1. pbludov
      19.07.2016 09:15

      Возможно, ноги у этой оптимизации растут из чего-то вроде

      logger.log("message {0}", oneArg);
      

      где logger.log принимает на вход строку и массив объектов.


  1. mamkaololosha
    19.07.2016 15:07
    +3

    Это тоже самое, что написать int main() { return 0; } и сказать, что С++ всё еще самый быстрый по скорости. Такого кейса, как у автора, в жизни вообще быть не может. Уж извините.


  1. interprise
    19.07.2016 20:56

    Кто может объяснить, вот этот момент
    // Проверка на safe-point. С её помощь JVM может забрать контроль у скомпилированного кода, например, для сборки мусора.
    test %eax,-0x27b759f(%rip)

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


    1. sunless
      19.07.2016 21:02
      +2

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


      Этот адрес находится на read-protected странице. Происходит прерывание, обработчик которого и взведет java треды на сейфпоинт. Это такой быстрый if. То есть быстрый он тогда, когда идти на safepoint не надо, а если надо — то там и так будет пауза, потомы накладные расходы, связанный с отработкой прерывания минимальны.


      1. sunless
        19.07.2016 21:07
        +3

        То есть не так. Если рантайм хочет, чтобы все треды пошли на сейфпоинт — он делает эту страницу read-protected. В остальное время чтение возможно. JVM устанавливает свой обработчик прерываний. При чтении происходит прерывание, обработчик которого и взведет java треды на safepoint. Это такой быстрый if. То есть быстрый он тогда, когда идти на safepoint не надо, а если надо — то там и так будет пауза, потому накладные расходы, связанные с отработкой прерывания, несущественны.


        1. interprise
          20.07.2016 06:29

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


      1. firsttimecxx
        20.07.2016 08:37

        Можно узнать, а каким образом жвм живёт после «сегфолта»? Продолжает работать, только уже из обработчика сигнала?


        1. lany
          20.07.2016 08:41

          Продолжает, конечно. Что с ней сделается-то? А что происходит с компьютером, если программа падает с сегфолтом? Продолжает ведь работать. С точки зрения процесса нет особой разницы, обработчик прерывания в коде операционной системы или в коде текущего приложения (виртуальной машины).


          1. firsttimecxx
            20.07.2016 09:07

            После обработчика она будет продолжена с того места, где оно упало. И будет бесконечно пытаться прочитать не читаемое. Наверное я криво сформулировал вопрос.

            Как продолжить программу дальше? Разрешить рид перед завершением? А как тогда потом поймать другие?


            1. senia
              20.07.2016 09:23
              +4

              Паркуются все потоки, выполняется нужная работа (например сборка мусора), разрешается чтение, все потоки запускаются снова.
              Когда снова надо будет запарковать потоки — снова сменить разрешение на страницу памяти.


              1. sunless
                20.07.2016 11:37
                +2

                кармы нету голосовать, но всё именно так. В разных JVM реализовано по-разному, где-то это глобальная страница, где-то это per thread. Вцелом такой механизм называется page polling.


              1. firsttimecxx
                20.07.2016 13:42

                Т.е. ждёт «сегфолта» у каждого потока на такой точки? Но в целом понятно — типичный стопворлд о котором я даже не предполагал. Бывает.


                1. sunless
                  20.07.2016 14:11

                  Если вы запустите хотспот под gdb, то он почти сразу начнет «подать» с такими сегфолтами, сразу надо настраивать gdb, чтобы он игнорировал эти прерывания. Но не в дебаггере этого не видно.

                  И это не обязательно stop the world. Так реализуют, хоть и не в openjdk, механизм per thread safepoint, когда надо гарантировать, что каждый thread зайдет в сервисную рутину, но после того может продолжить работу, не дожидаясь остальных.


                1. senia
                  20.07.2016 17:57
                  +1

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


            1. apangin
              20.07.2016 13:24
              +2

              Обработчик сигнала не обязан вернуться ровно на ту же инструкцию. В хендлер передаётся ucontext с указателем на сохранённые значения всех регистров. Достаточно заменить значение, соответствующее регистру IP, чтобы по выходу из обработчика сигнала управление передалось на следующую инструкцию или в любое другое место. Например, при разыменовании нулевого указателя хендлер передаёт управление коду, бросающему NullPointerException.


              1. firsttimecxx
                20.07.2016 13:52

                Спасибо. Когда-то давно я пытался что-то подобно делать со сменой ip, но у меня не получалось. Возможно не осилил. Оно точно работает? Хотя как время будет — попробую перепроверить.


                1. apangin
                  20.07.2016 14:05
                  +1

                  Точно. Делается примерно так:

                      ucontext->uc_mcontext.gregs[REG_RIP] = new_return_address;
                  

                  На x86 — REG_EIP, на amd64 — REG_RIP.


  1. 31415
    20.07.2016 21:05

    У меня 3 вопроса.
    1. Почему в из Hotspot до сих пор не выкинули вообще интерпретатор? Почему Google в V8 сделала это, а Oracle — нет?
    2. Какая производительность чистого интерпретатора в Hotspot — кто-нибудь измерял, забавы ради?
    3. Почему в явасрачах сразу же не фиксируется базовая установка: Java — для бэкенда, для «серверных» сценариев нагрузки, когда Hotspot выполняет большую часть времени небольшую, «горячую» часть программы, может власть всё заоптимизировать и закешировать? Иногда, когда такой тип нагрузки встречается в «настольных» приложениях, Java работает вполне сносно, если не считать относительно долгий запуск и повышенные требования к памяти. Но почему-то мало кому приходит в голову писать на Java браузеры, например, или медиа-кодировщики. Хотя, казалось бы, тут Java прямо напрашивается, т.к. сложность проектов зашкаливает. А причина проста — характер нагрузок просто не позволит Hotspot развернуться, ну и память тоже. Хотя, учитывая сколько памяти ест Хром — может даже Java эффективнее с ней работала.


    1. Regis
      21.07.2016 11:55

      1. Почему в из Hotspot до сих пор не выкинули вообще интерпретатор?
      Например потому что куски кода, которые отрабатывают всего один раз часто быстрей проинтерпретировать, чем скомпилировать + сохранить + выполнить.


      1. 31415
        22.07.2016 09:37

        Это могло быть правдой, если бы Google не доказал, что «черновая» компиляция — простая замена базовых логических блоков на соответствующий машинный код — почти ничего не стоит. И чтобы блок кода попал под C2, он должен выполниться далеко не 2 раза, а сильно больше.


        1. chabapok
          22.07.2016 17:11
          +2

          Вы пробовали смотреть top или smem -t когда играете в какой-нибудь sither io?

          У меня кушается проца 250% и памяти 4 процесса: 370+170+140+150 = 830м.
          jvm столько будет жрать на довольно больших программах (например нетбинс жрет около 700m), хром — ест это просто потому, что хочет. И c ff ситуация обстоит примерно так же. Интерпретатор жрет мало, позволяет выбрасывать редкоиспользуемый код, или вовсе не производить его. Поэтому, java жрет по сравнению с подходом «компильнуть вообще все» меньше памяти и меньше проца.

          Попробовала бы какая-нибудь idea постоянно 250% кушать. Все бы говорили, что idea тормозит и жрет проц, но для хрома это ок, никому даже в голову не приходит сказать, что это плохо. Всем, кто не жава «разрешено» жрать много.


          1. 31415
            22.07.2016 19:31

            Простите, я не понимаю — какое отношение непонятно как написанная и неизвестная мне игра имеет к моему вопросу? Даже если не учитывать, что я не играю в игры.
            Java ест столько, сколько дадут. Как и Chrome. С FF ситуация обстоит совершенно не так же.
            Черновой компилятор V8 ест мало, работает быстро. Интерпретатор в Java ничего не выкидывает — это делает HotSpot.
            Помимо V8 в Chrome есть кому память съесть. Нет никаких доказательств, что подход Chrome хоть чем-то хуже подхода JVM.
            Зато есть немало доказательств, что непрогретый Java-код мягко говоря не оптимален — вот, например: https://github.com/floatdrop/v8-vs-java-bench.
            Как и вечные жалобы на многочисленные бенчмарки: вы всё врёти! у вас ява не прогрета!


            1. chabapok
              22.07.2016 20:38
              +2

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

              Ваш пример, кстати, падает где-то в js после теста с++ и java. То есть, Автор не в состоянии написать непадающий код, и непонимает что он меряет — но уже делает выводы о непригодности и тормознутости технологии, которой пользуются весь мир. Сразу видно — большой специалист.

              То, что вы дали — канонический пример непонимания, что вообще происходит, и как работают программы. Там не интерпретатор меряется, а время старта самой vm. Наличие или отсутствие тела у метода main не влияет на конечный результат. (и если починить чтоб не падало, оно скорей всего не будет влиять ни в js ни в ruby ни в python)

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


              1. 31415
                22.07.2016 22:22

                Ок, игра на JS в хроме. Тормозит. Было бы странно, если бы не тормозила :)
                Читаем изначальный вопрос. Думаем. Догадываемся, что он не про скорость работы, а про скорость чернового компилятора.
                Который ни разу много не ест и не тормозит. Тормозит всё потом уже, и память ест потом. Уже когда и основной компилятор отработает, когда кеши заполнятся, когда данные прогрузятся все.
                Но при этом даже черновой компилятор V8 в несколько (предположительно) раз быстрее интерпретатора.
                И это видно в тех тестах, когда из-за малого количества итераций ни у V8, ни у HotSpot основные компиляторы отработать не успевают: JVM с треском проигрывает.
                А что тот тест падает — не удивительно, учитывая дату последнего коммита. И не именно в нём дело.
                Про время старта JVM убедительно написано тут, просвещайтесь: http://benchmarksgame.alioth.debian.org/sometimes-people-just-make-up-stuff.html#jvm-startup-time


                1. chabapok
                  23.07.2016 19:16
                  +3

                  Сам компилятор может не ест и не тормозит — но производимый код, очевидно, плох: и ест и тормозит. Иначе и быть не может. Долгий компилятор — хороший(может быть) код, быстрый компилятор — плохой(всегда будет) код. В V8 компилятор быстрый. В jvm — несколько разных компиляторов, что позволило сделать быстрый старт (все равно на старте часто бывает, что user-код сканирует classpath — т.е. идет активная работа с диском) — и быструю работу потом. Отличии от того же шарпа, где медленный старт — и относительно-медленная (как у С1) работа.

                  Вам написали — если выполнять код надо однократно, то быстрей всего его интерпретировать. И по памяти это, очевидно, меньше. «компилятор V8 всех порвет» — это домыслы автора. Что такое интерпретатор в jvm? Это просто switch-case, причем даже слегка заоптимайзеный при помощи таблицы опкодов и goto. Там нету никакой магии. Он не может быть медленней v8-компилятора.

                  Тогда почему в том тесте померяны тормоза? А вот почему.

                  Мы уже выяснили выше, что люди, на которых ссылались, не умеют корректно измерять величины, писать тесты производительности, и не понимает, что именно меряют, и почему результаты такие. Это не V8 у него работает быстрей jvm-интерпретатора, а V8 у него стартует и завершается быстрей, чем у него стартует и завершается jvm. Убедится в этом, как я написал выше, можно, убрав тело метода main. На время исполнения всей программы это особо не отразится — что свидетельствует о том, что сама интерпретация тела пренебрежимо мала по сравнению с загрузкой-шатдауном jvm.

                  Допустим даже v8 хоть в миллион раз быстрей стартует. Лично меня это особо не волнует. Старт jvm все равно визуально-моментален, но мне важно, чтобы потом оно качественно работало, а не поглощало память и проц, как это делает V8.

                  Кстати, а что не так с датой последнего коммита? Если она старая — оно по-вашему может неработать? Эпично! В java такой фигни нет! :)))

                  jvm стартует немножко дольше потому, что делает больше действий, которые позволяют ей в будущем работать лучше. Т.е. считайте это уклоном в сторону «медленней компилятор — лучше код». Кроме того, у автора тестов мог быть медленный винт. У java большой рантайм, ~64мб еще и позипован. Медленный винт вполне мог оказать решающую роль. Это действительно не очень хорошо, но масштаб трагедии — понт. Скажем так, хотспот проигрывает — но не с треском, а с потрескиванием головок винчестера. Кстати, ждем девятку, там это должны улучшить, т.к. растащат функционал по модулям. Но все равно, из зипа jvm загружает только то, что реально использует. И все равно, в сколь-нибудь серьезных приложениях значительная часть рантайма используется.


    1. lany
      21.07.2016 12:08
      +1

      Вообще вы можете запустить HotSpot с флагом -XX:-UseInterpreter. Вы можете сами потестировать, будет ли ваше приложение работать от этого быстрее. По моим прикидкам обычно интерпретатор медленнее, чем C2-код в 20-100 раз. Разумеется, всегда можно придумать тест, где цифра сильно изменится в любую сторону. Java-таки не для бэкэнда, это универсальный язык программирования.


      1. apangin
        21.07.2016 20:37
        +3

        Немного не так. -XX:-UseInterpreter не отключает интерпретатор. Для форсированного выполнения скомпилированного кода служит флаг -Xcomp, который эквивалентен -XX:-UseInterpreter -XX:-BackgroundCompilation -XX:-ClipInlining -XX:Tier3InvokeNotifyFreqLog=0 -XX:Tier4InvocationThreshold=0

        Это будет хорошим приближением работы в полностью компилируемом режиме. Но на самом деле, HotSpot не умеет работать вообще без интерпретатора.


        1. lany
          21.07.2016 20:42

          Спасибо за поправку! А отдельно -XX:-UseInterpreter без всего остального что будет значить?


          1. apangin
            21.07.2016 21:20
            +1

            При ресолвинге одного из invoke* байткодов либо при вызове из натива метод будет помещаться в очередь на компиляцию. При этом, если background компиляция не отключена, метод спокойненько продолжит исполняться в интерпретаторе.


    1. khim
      21.07.2016 13:32
      -3

      Почему Google в V8 сделала это, а Oracle — нет?
      Потому что они работают с разными входными данными. V8 нужно в любом случае скомпилировать программу (поступает-то она в виде декста, так что всё равно нужно распарсить этот текст, преобразовать в внутреннее представление и т.д. и т.п.). Можно компилировать в промежуточный байткод, можно — прямо в машинный (по-быстрому, без оптимизаций).

      Хотя, учитывая сколько памяти ест Хром — может даже Java эффективнее с ней работала
      Не работала бы. Вот прямо сейчас у меня запущено 70 процессов Хрома. Запуск сравнимого числа JVM приведёт к тому, что жалких 8GiB памяти не хватит — даже если там будут крутиться «Hello, World»'ы. Безопасно запускать много процессов в одной JVM — тоже нельзя (Oracle это признал, когда отказался даже от попыток сделать вид, что HostSpot способен обеспечить безопасность, и превратил Java в ActiveX--).

      В общем я не знаю ни одной задачи, которую Java обещала бы решить — и при этом успешно решила (только не надо про «умные IDE»: если бы столько денег, сколько вбухали в разработку IDE для Java на Java вбухали бы в IDE на C++ для C++ — результат был бы не хуже).


      1. 31415
        22.07.2016 09:42

        Есть множество реализаций ЯП, которые интерпретируют код построчно или поблочно, без какого-либо промежуточного представления. Собственно, все изначальные реализации JS именно так и работали.

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