Почему Java может быть быстрее, чем C ++?

80

Иногда Java превосходит C ++ в тестах. Конечно, иногда C ++ выигрывает.

Смотрите следующие ссылки:

Но как это вообще возможно? Меня поражает, что интерпретируемый байт-код может быть быстрее, чем скомпилированный язык.

Может кто-нибудь объяснить, пожалуйста? Спасибо!

Deets McGeets
источник
2
Вы можете взглянуть на shootout.alioth.debian.org/u32/…, чтобы увидеть, какие проблемы быстрее выполняются на java / c ++ ... Смотрите схему проблем, а не эти конкретные проблемы ...
c0da
2
Видите, почему у Java была репутация медленного? много деталей по этой теме.
Петер Тёрёк
11
Это против закона (раздел 10.101.04.2c) для создания Java VM , которая выполняет быстрее , чем исполняемый бинарный файл , полученный с C ++.
Матин Улхак
1
@muntoo Что, черт возьми, ты имеешь в виду? Раздел 10.101.04.2с чего?
Хайленд Марк
3
@HighlandMark Вы не являетесь участником круга. Вы не должны знать, что происходит внутри круга. Круг абсолютен - законы круга превосходят законы природы. Вы не можете ни бросить вызов, ни поставить под сомнение круг. Я круг, а круг это я.
Матин Улхак

Ответы:

108

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

Во-вторых, значительное число задействованных критериев выглядит довольно предвзятым (я не могу сказать, по намерению или по некомпетентности). Например, несколько лет назад я посмотрел на исходный код, связанный с одной из опубликованных вами ссылок. У него был такой код:

  init0 = (int*)calloc(max_x,sizeof(int));
  init1 = (int*)calloc(max_x,sizeof(int));
  init2 = (int*)calloc(max_x,sizeof(int));
  for (x=0; x<max_x; x++) {
    init2[x] = 0;
    init1[x] = 0;
    init0[x] = 0;
  }

Так как callocобеспечивает нулевую память, использование forцикла для обнуления снова бесполезно. За этим последовало (если память служит) заполнение памяти другими данными в любом случае (и никакой зависимости от обнуления), поэтому все обнуление было совершенно ненужным в любом случае. Замена приведенного выше кода на простое malloc(как любой здравомыслящий человек использовал бы для начала) улучшило скорость версии C ++, достаточную для того, чтобы превзойти версию Java (с довольно большим запасом, если память будет работать).

Рассмотрим (для другого примера) methcallтест, использованный в записи блога в вашей последней ссылке. Несмотря на название (и как вещи могут даже выглядеть), версия этого языка на C ++ не особо измеряет затраты на вызовы методов вообще. Часть кода, которая оказывается критической, находится в классе Toggle:

class Toggle {
public:
    Toggle(bool start_state) : state(start_state) { }
    virtual ~Toggle() {  }
    bool value() {
        return(state);
    }
    virtual Toggle& activate() {
        state = !state;
        return(*this);
    }
    bool state;
};

Критическая часть оказывается state = !state;. Рассмотрим, что происходит, когда мы изменяем код для кодирования состояния intвместо bool:

class Toggle {
    enum names{ bfalse = -1, btrue = 1};
    const static names values[2];
    int state;

public:
    Toggle(bool start_state) : state(values[start_state]) 
    { }
    virtual ~Toggle() {  }
    bool value() {  return state==btrue;    }

    virtual Toggle& activate() {
        state = -state;
        return(*this);
    }
};

Это незначительное изменение улучшает общую скорость примерно на 5: 1 . Несмотря на то, что эталонный тест предназначался для измерения времени вызова метода, в действительности большая часть того, что он измерял, была временем преобразования между intи bool. Я, безусловно, согласен с тем, что неэффективность, показанная оригиналом, вызывает сожаление - но учитывая, как редко он возникает в реальном коде, и легкость, с которой его можно исправить, когда / если он возникает, мне трудно думать это так много значит.

В случае, если кто-то решит повторно запустить соответствующие тесты, я должен также добавить, что есть почти одинаково тривиальное изменение в версии Java, которая производит (или, по крайней мере, когда-то произведено) - я не запускал тесты повторно с недавняя JVM, чтобы подтвердить, что они все еще делают) довольно существенное улучшение в версии Java также. В версии Java есть NthToggle :: activ (), который выглядит следующим образом:

public Toggle activate() {
this.counter += 1;
if (this.counter >= this.count_max) {
    this.state = !this.state;
    this.counter = 0;
}
return(this);
}

Изменение этого параметра для вызова базовой функции вместо this.stateнепосредственного управления дает довольно существенное улучшение скорости (хотя этого недостаточно, чтобы не отставать от модифицированной версии C ++).

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

Мой собственный опыт показывает, что при одинаково опытных программистах, уделяющих одинаковое внимание оптимизации, C ++ будет превосходить Java чаще, чем нет - но (по крайней мере, между этими двумя) язык редко будет иметь такое же большое значение, как программисты и дизайн. Упомянутые критерии говорят нам больше о (не) компетентности / (не) честности их авторов, чем о языках, на которые они ориентированы.

[Править: Как подразумевается в одном месте выше, но никогда не указывалось так явно, как мне следовало бы, результаты, которые я цитирую, - это те результаты, которые я получил, когда тестировал это ~ 5 лет назад, используя реализации C ++ и Java, которые были актуальны в то время , Я не перезапускал тесты с текущими реализациями. Однако взгляд указывает, что код не был исправлен, поэтому все, что изменилось бы, это способность компилятора скрыть проблемы в коде.]

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

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

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

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

Джерри Гроб
источник
26
+1, полный кв. В частности, «язык редко будет иметь такое же большое значение, как программисты и дизайн» - вы часто будете сталкиваться с проблемами, когда вы можете оптимизировать алгоритм, например, улучшить big-O, что даст гораздо больший импульс, чем мог бы лучший компилятор.
шнайдер
1
«В случае, если кто-то решит повторно запустить соответствующие тесты ...» НЕ! Еще в 2005 году эти старые задачи были отброшены и заменены задачами, которые теперь показаны в тестовой игре. Если кто -то хочет , чтобы повторно запустить некоторые программы, пожалуйста , повторно запустить текущие программы для текущих задач , показанных на тесты игры домашней страницы shootout.alioth.debian.org
igouy
@igouy: Некоторые люди могут просто захотеть подтвердить / опровергнуть результаты проведенных ими тестов, с минимальным количеством исправлений, необходимых, чтобы хотя бы дать им минимальные отношения с реальностью. В то же время вы в основном правы: рассматриваемые тесты настолько плохи, что просто исправление наиболее очевидных ошибок мало чем поможет.
Джерри Коффин
И именно поэтому, еще в 2005 году, они были отброшены и заменены задачами, которые теперь показаны в тестовой игре. Люди, которые не знают ничего лучшего, перезапускают эти старые программы.
Игуи
13
+1 Мне не нравятся люди, пишущие на C ++ в стиле C или Java, а затем утверждающие, что Java лучше. Отказ от ответственности: я не называю какой-либо язык выше, но написание дрянного кода C ++ в стиле, который может идеально подходить для другого языка, не делает оба языка сопоставимыми.
Крис говорит восстановить Монику
112

Свернутый вручную C / C ++, выполненный экспертом с неограниченным временем , будет, по крайней мере, таким же быстрым или быстрым, как Java. В конечном счете, сама Java написана на C / C ++, поэтому вы, конечно, можете делать все, что делает Java, если вы готовы приложить достаточно усилий для разработки.

Однако на практике Java часто выполняется очень быстро по следующим причинам:

  • JIT-компиляция - хотя классы Java хранятся в виде байт-кода, они (обычно) компилируются в собственный код JIT-компилятором при запуске программы. После компиляции это чистый нативный код, поэтому теоретически можно ожидать, что он будет работать так же хорошо, как и скомпилированный C / C ++, после того, как программа будет работать достаточно долго (т.е. после того, как будет завершена JIT-компиляция)
  • Сборка мусора в Java чрезвычайно быстрая и эффективная - Hotspot GC, вероятно, является лучшей универсальной реализацией GC в мире. Это результат многолетних экспертных усилий Sun и других компаний. Практически любая сложная система управления памятью, которую вы запускаете в C / C ++, будет хуже. Конечно, вы можете написать довольно быстрые / легкие базовые схемы управления памятью на C / C ++, но они не будут столь же универсальными, как полноценная система GC. Поскольку большинству современных систем требуется сложное управление памятью, Java имеет большое преимущество в реальных ситуациях.
  • Лучшее нацеливание на платформу - откладывая компиляцию до запуска приложения (JIT-компиляция и т. Д.), Компилятор Java может воспользоваться тем фактом, что он знает точный процессор, на котором он выполняется. Это может обеспечить некоторые очень полезные оптимизации, которые вы не смогли бы выполнить в предварительно скомпилированном коде C / C ++, который должен быть нацелен на набор команд процессора «наименьший общий знаменатель».
  • Статистика времени выполнения - поскольку JIT-компиляция выполняется во время выполнения, она может собирать статистику во время выполнения программы, что обеспечивает лучшую оптимизацию (например, зная вероятность того, что выбрана конкретная ветвь). Это может позволить компиляторам Java JIT генерировать лучший код, чем компиляторы C / C ++ (которые должны заранее «угадать» наиболее вероятную ветвь, предположение, которое часто может быть неверным).
  • Очень хорошие библиотеки - среда выполнения Java содержит множество очень хорошо написанных библиотек с хорошей производительностью (особенно для серверных приложений). Часто они лучше, чем вы могли бы написать сами или получить легко для C / C ++.

В то же время C / C ++ также имеет некоторые преимущества:

  • Больше времени для продвинутых оптимизаций - компиляция C / C ++ выполняется один раз и поэтому может потратить значительное время на продвинутые оптимизации, если вы настроите это для этого. Нет теоретической причины, по которой Java не может сделать то же самое, но на практике вы хотите, чтобы Java JIT-компилировал код относительно быстро, поэтому JIT-компилятор стремится сосредоточиться на «более простых» оптимизациях.
  • Инструкции, которые нельзя выразить в байт-коде - хотя байт-код Java является полностью универсальным, есть некоторые вещи, которые вы можете сделать на низком уровне, которые вы не можете сделать в байт-коде (арифметика с непроверенными указателями - хороший пример!). Используя эти приемы, вы можете получить некоторые преимущества в производительности.
  • Меньше «безопасных» ограничений - Java проделывает дополнительную работу, чтобы гарантировать безопасность и надежность программ. Примерами являются проверки границ массивов, определенные гарантии параллелизма, проверки нулевых указателей, безопасность типов при приведениях и т. Д. Избегая их в C / C ++, вы можете получить некоторое повышение производительности (хотя, возможно, это может быть плохой идеей!)

В целом:

  • Java и C / C ++ могут достигать одинаковых скоростей
  • C / C ++, вероятно, имеет небольшое преимущество в экстремальных условиях (неудивительно, что разработчики игр AAA все еще предпочитают его, например)
  • На практике это будет зависеть от того, как различные факторы, перечисленные выше, сбалансированы для вашего конкретного применения.
mikera
источник
9
Объявление "больше времени для оптимизаций в C ++": это одна из настроек, которые делает виртуальная машина Oracle при выборе виртуальной машины сервера: она допускает более высокую стоимость запуска для обеспечения более высокой производительности в долгосрочной перспективе. Клиентская виртуальная машина, однако, настроена на оптимальное время запуска. Так что это различие существует даже в Java.
Иоахим Зауэр
8
-1: компилятору C ++ может потребоваться гораздо больше времени (буквально часов для большой библиотеки), чтобы создать очень оптимизированный двоичный файл. JIT-компилятор Java не может занимать столько времени, даже «серверная» версия. Я серьезно сомневаюсь, что компилятор Java JIT сможет выполнять оптимизацию всей программы так же, как компилятор MS C ++.
Quant_dev
20
@quant_dev: конечно, но разве это не то, что я сказал в своем ответе как преимущество C ++ (больше времени для продвинутой оптимизации)? Так почему -1?
Микера
13
Сборка мусора не является преимуществом скорости для Java. Это только преимущество в скорости, если вы программист на C ++, который не знает, что вы делаете. Если все, что вы проверяете, это то, как быстро вы можете выделить, то да, сборщик мусора победит. Общая производительность программы, однако, все еще может быть улучшена путем ручного управления памятью.
Билли Онил
4
... Но с C ++ вы всегда можете теоретически создать "JIT-подобный слой", который выполняет аналогичные оптимизации веток во время выполнения, сохраняя при этом грубую скорость программы на C ++. (Теоретически. :()
Матин Улхак
19

Среда выполнения Java не интерпретирует байт-код. Скорее, он использует то, что называется сборкой Just In Time . В основном, когда программа запускается, она берет байт-код и преобразует его в собственный код, оптимизированный для конкретного процессора.

GrandmasterB
источник
На практике да. В принципе, это зависит от того, что в ранних виртуальных машинах Java использовались интерпретаторы байт-кода, и вы, вероятно, все еще сможете найти виртуальные машины с интерпретацией байт-кода, если будете достаточно внимательны.
Steve314
10
@ Steve314: но чисто интерпретирующие виртуальные машины не будут теми, которые превосходят C ++, поэтому они на самом деле не имеют отношения к этому вопросу.
Иоахим Зауэр
JIT-компилятор также может динамически оптимизировать для конкретного использования кода, что невозможно для кода, который компилируется статически.
голубой
2
@starblue, ну, в некоторой степени это возможно при статической компиляции - см. профилированную оптимизацию.
SK-logic
19

При прочих равных условиях можно сказать: нет, Java никогда не должна быть быстрее . Вы всегда можете реализовать Java на C ++ с нуля и, таким образом, добиться как минимум такой же высокой производительности. На практике, однако:

  • JIT компилирует код на компьютере конечного пользователя, позволяя оптимизировать его под конкретный процессор, на котором он работает. Несмотря на то, что при компиляции возникают накладные расходы, она может окупиться для интенсивных приложений. Часто реальные программы не компилируются для процессора, который вы используете.
  • Компилятор Java может быть лучше в автоматической оптимизации, чем компилятор C ++. Или нет, но в реальном мире не всегда все идеально.
  • Поведение производительности может варьироваться в зависимости от других факторов, таких как сборка мусора. В C ++ вы обычно вызываете деструктор сразу же после завершения работы с объектом. В Java вы просто освобождаете ссылку, задерживая фактическое уничтожение. Это еще один пример различия, которого нет ни здесь, ни там, с точки зрения производительности. Конечно, вы можете утверждать, что вы могли бы реализовать GC на C ++ и покончить с этим, но реальность такова, что мало кто делает / хочет / может.

Кроме того, это напоминает мне о дебатах, касающихся C в 80-х / 90-х годах. Всем было интересно, "может ли C когда-нибудь быть таким же быстрым, как сборка?" По сути, ответ был: нет на бумаге, но на самом деле компилятор C создал более эффективный код, чем 90% программистов на ассемблере (ну, когда он немного повзрослел).

Даниэль Б
источник
2
Что касается GC, дело не только в том, что GC может задержать уничтожение объектов (что не должно иметь значения в долгосрочной перспективе); Дело в том, что с современными GC выделение / освобождение недолговечных объектов в Java чрезвычайно дешево по сравнению с C ++.
Петер Тёрёк
@ PéterTörök да, ты прав, хорошая мысль.
Даниэль Б
9
@ PéterTörök Но в C ++ недолговечные объекты часто помещаются в стек, что, в свою очередь, намного быстрее, чем любая куча GC-ed, которую может использовать Java.
Quant_Dev
@quant_dev, вы забыли еще один значительный эффект GC: компактификация. Поэтому я не был бы уверен, какой путь быстрее.
SK-logic
3
@DonalFellows Что заставляет вас думать, что мне нужно беспокоиться об управлении памятью в C ++? Большую часть времени я не делаю. Есть простые шаблоны, которые вам нужно применить, которые отличаются от Java, но это все.
Quant_Dev
10

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

...

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

http://www.ibm.com/developerworks/java/library/j-jtp09275/index.html

Landei
источник
Это лишь малая часть всей картины, но, тем не менее, она актуальна.
Йоахим Зауэр
2
Мне нравится, каково это: java для новичков, доверяйте волшебному GC, он знает лучше.
Морг.
1
@Morg: Или вы можете прочитать это так: Java предназначена для людей, которые любят делать что-то, вместо того, чтобы тратить свое время на хитрость и ручное управление памятью.
Landei
4
@Landei Думаю, ваш комментарий был бы более правдоподобным, если бы на Java была написана приличная и долговременная широко используемая кодовая база. В моем мире настоящие ОС написаны на C, postgreSQL написан на C, как и наиболее важные инструменты, которые действительно было бы непросто переписать. Java была (и это даже официальная версия) для того, чтобы дать возможность менее опытным людям программировать в стадах и при этом достигать ощутимых результатов.
Морг.
1
@ Морг Я нахожу очень странным, как вы, кажется, сосредоточены только на ОС. Это просто не может быть хорошей мерой по нескольким причинам. Во-первых, требования к ОС принципиально отличаются от большинства других программ, во-вторых, у вас есть принцип большого пальца Panda (кто хочет переписать полную ОС на другом языке, кто хочет написать свою ОС, если есть работающие и даже бесплатные альтернативы?) и третье другое программное обеспечение использует функции ОС, поэтому нет необходимости когда-либо писать драйвер диска, диспетчер задач и т. д. Если вы не можете предоставить более убедительные аргументы (не полностью основанные на ОС), вы звучите как ненавистник.
Landei
5

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

Как отметил @Jerry Coffin, во многих случаях простые изменения могут сделать код намного быстрее, но часто может потребоваться слишком много грязных настроек на одном языке или другом, чтобы повысить производительность. Это, вероятно, то, что вы увидите в хорошем тесте, который показывает, что Java работает лучше, чем C ++.

Кроме того, хотя обычно это не так важно, есть некоторая оптимизация производительности, которую JIT-язык, такой как Java, может сделать, чего не может C ++. Среда выполнения Java может включать улучшения после компиляции кода, что означает, что JIT потенциально может создавать оптимизированный код, чтобы использовать преимущества новых (или, по крайней мере, других) функций ЦП. По этой причине 10-летний двоичный файл Java может потенциально превзойти 10-летний двоичный файл C ++.

Наконец, полная безопасность типов в более широкой картине, в очень редких случаях, может предложить экстремальные улучшения производительности. Singularity , экспериментальная ОС, написанная почти полностью на языке C #, имеет намного более быстрое межпроцессное взаимодействие и многозадачность из-за того, что нет необходимости в аппаратных границах процессов или дорогостоящих переключениях контекста.

Рей Миясака
источник
5

Автор: Тим Холлоуэй на JavaRanch:

Вот примитивный пример: назад, когда машины работали в математически определенных циклах, инструкция перехода обычно имела 2 разных момента времени. Один для того, когда ветвь был взят, другой для того, когда ветвь не была взята. Обычно дело без веток было быстрее. Очевидно, это означало, что вы могли оптимизировать логику, основываясь на знании того, какой случай был более распространенным (при условии, что то, что мы «знаем», не всегда то, что на самом деле имеет место).

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

Источник: http://www.coderanch.com/t/547458/Performance/java/Ahead-Time-vs-Just-time

Тьяго Негри
источник
3
И еще раз, это неправильно / не полностью. Статические компиляторы с профилируемым профилированием могут распознать это.
Конрад Рудольф
2
Конрад, статические компиляторы могут перевернуть логику, основываясь на текущей рабочей нагрузке? Как я понимаю, статические компиляторы генерируют код один раз, и он остается неизменным всегда.
Тьяго Негри
2
Текущая нагрузка, нет Но типичная нагрузка. Оптимизация по профилю анализирует, как ваша программа работает при обычной нагрузке, и соответственно оптимизирует горячие точки, как это делает HotSpot JIT.
Конрад Рудольф
4

Это связано с тем, что последний шаг генерации машинного кода происходит прозрачно внутри JVM при запуске Java-программы, а не явно при создании программы C ++.

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

Просто такая вещь, как автоматическое включение геттера, так что JUMP-RETURN не нужен, чтобы просто получить значение, ускоряет процесс.

Однако, вещь, которая действительно позволила быстрые программы, лучше очищать впоследствии. Механизм сборки мусора в Java работает быстрее, чем ручной, не содержащий malloc в C. Во многих современных реализациях без malloc используется сборщик мусора.


источник
Обратите внимание, что этот встроенный материал делает запуск JVM больше и медленнее, пока лучший код не сможет наверстать упущенное.
1
«Многие современные реализации без использования malloc используют сборщик мусора». В самом деле? Я хотел бы знать больше; У вас есть какие-либо ссылки?
Шон Макмиллан
Спасибо. Я пытался найти способ сказать, что JVM больше не содержит просто своевременный компилятор, компилирующий исполняемый код, а является компилятором горячей точки, который профилирует исполняемый код и в результате оптимизирует его. Я одноразовый компилятор как C ++ изо всех сил пытается соответствовать этому.
Хайленд Марк
@SeanMcMillan, недавно я видел анализ производительности реализаций без malloc, где упоминалось, что самый быстрый из них использовал сборщик мусора внизу. Я не могу вспомнить, где я это читал.
Был ли это BDW консервативный GC?
Деми
4

Краткий ответ - это не так. Забудьте об этом, тема так же стара, как огонь или колесо. Java или .NET не является и не будет быстрее, чем C / C ++. Это достаточно быстро для большинства задач, когда вам вообще не нужно думать об оптимизации. Как формы и обработка SQL, но на этом все и заканчивается.

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

В действительности, простые вещи, такие как выделение памяти в стеке или просто использование memzones, просто убьют Java / .NET на месте.

Мусоросборный мир использует что-то вроде memzone со всеми учетными записями. Добавьте memzone к C, и C будет быстрее прямо на месте. Специально для тех тестов «высокопроизводительный код» Java и C, которые идут так:

for(...)
{
alloc_memory//Allocating heap in a loop is verrry good, in't it?
zero_memory//Extra zeroing, we really need it in our performance code
do_stuff//something like memory[i]++
realloc//This is lovely speedup
strlen//loop through all memory, because storing string length is soo getting old
free//Java will do that outside out timing loop, but oh well, we're comparing apples to oranges here
}//loop 100000 times

Попробуйте использовать переменные на основе стека в C / C ++ (или размещение новых), они переводятся в sub esp, 0xff, это отдельная инструкция x86, лучше, чем в Java - вы не можете ...

Большую часть времени я вижу те стенды, где сравнивают Java с C ++, это заставляет меня идти, wth? Неправильные стратегии выделения памяти, саморазвивающиеся контейнеры без резервов, множество новых. Это даже не близко к ориентированному на производительность коду C / C ++.

Также хорошо читать: https://days2011.scala-lang.org/sites/days2011/files/ws3-1-Hundt.pdf

кодировщик
источник
1
Неправильно. Совершенно неправильно. Вы не сможете превзойти компактирующий GC с вашим ручным управлением памятью. Наивный подсчет ссылок никогда не будет лучше, чем правильный mark'n'sweep. Как только дело доходит до сложного управления памятью, C ++ становится тормозом.
SK-logic
3
@ SK-Logic: Неправильно, с мемзонами или распределением в стеке НЕТ выделения памяти или ее освобождения ВСЕ. У вас есть блок памяти, и вы просто пишете в него. Пометьте блок как свободный с помощью изменяемой переменной, такой как защита от параллелизма InterlockedExchange и т. Д., И следующий поток просто сбрасывает свои данные в предварительно выделенный блок, вообще не обращаясь к ОС за памятью, если видит, что она свободна. Со стеком это еще проще, за исключением того, что вы не можете сбросить 50 МБ в стек. И время жизни этого объекта только внутри {}.
Кодер
2
@ SK-logic: компиляторы - это прежде всего правильность, а потом производительность. Поисковые системы, базы данных, торговые системы в реальном времени, игры - вот что я считаю критически важным для производительности. И большинство из них полагаются на плоские структуры. И так или иначе, компиляторы в основном написаны на C / C ++. С пользовательскими распределителями, я думаю. Опять же, я не вижу проблем с использованием элементов дерева или списка поверх rammap. Вы просто используете размещение новых. В этом нет особой сложности.
Кодер
3
@ SK-logic: Это не намного быстрее, каждое приложение .NET / Java, которое я видел, всегда оказывалось медленным и настоящим боровом. Каждое переписывание управляемого приложения в код SANE C / C ++ приводило к созданию более чистого и легкого приложения. Управляемые приложения всегда тяжелы. См. VS2010 против 2008. Те же структуры данных, но VS2010 - HOG. Правильно написанные приложения на C / C ++ обычно загружаются за миллисекунды и не зацикливаются на заставках, а также занимают гораздо меньше памяти. Единственным недостатком является то, что вы должны кодировать с учетом аппаратного обеспечения, и многие люди не знают, как это в настоящее время. Это только ориентиры, где удалось иметь шанс.
Кодер
2
ваши неподтвержденные доказательства не в счет. Правильные ориентиры показывают реальную разницу. Особенно странно, что вы имеете в виду приложения с графическим интерфейсом, связанные с громоздкими и неоптимальными библиотеками с графическим интерфейсом. И, что более важно - теоретически предел производительности намного выше для правильно реализованного ГХ.
SK-logic
2

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

Язык не «медленный», программист написал медленную программу. Очень редко программа, написанная наилучшим образом на одном языке, превосходит (для любой практической цели) программу, выполняющую то же самое с использованием наилучшего способа на альтернативном языке, если только автор исследования не пытается размять свой конкретный топор.

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

mattnz
источник
2
Теоретически, «идеальная» виртуальная машина JITting должна превосходить статически скомпилированный код, приспосабливая ее оптимизации к динамически собираемой информации профилирования. На практике JIT-компиляторы еще не настолько умны, но, по крайней мере, способны генерировать код такого же качества, что и с их большими и более медленными статическими аналогами.
SK-logic
2

Смотрите следующие ссылки ... Но как это вообще возможно? Меня поражает, что интерпретируемый байт-код может быть быстрее, чем скомпилированный язык.

  1. Предоставляют ли эти сообщения в блоге достоверные доказательства?
  2. Эти посты в блоге предоставляют убедительные доказательства?
  3. Эти сообщения в блоге даже предоставляют доказательства о "интерпретированном байт-коде"?

Кит Ли говорит вам, что есть "очевидные недостатки", но ничего не делает с этими "очевидными недостатками". Еще в 2005 году эти старые задачи были отброшены и заменены задачами, которые теперь показаны в тестовой игре .

Кит Ли говорит вам, что он «взял эталонный код для C ++ и Java из устаревшей версии Great Computer Language Shootout и провел тесты», но на самом деле он показывает результаты только для 14 из 25 этих устаревших тестов .

Кит Ли теперь говорит вам, что он не пытался что-либо доказать с помощью поста в блоге семь лет назад, но тогда он сказал: «Мне надоело слышать, как люди говорят, что Java работает медленно, когда я знаю, что это довольно быстро ...», что говорит о том, что тогда было что-то, что он пытался доказать.

Кристиан Фельде говорит вам: «Я не создавал код, просто перезапустил тесты». как будто это освобождает его от какой-либо ответственности за его решение обнародовать измерения задач и программ, выбранных Китом Ли.

Измерения даже 25 крошечных крошечных программ обеспечивают окончательное доказательство?

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

igouy
источник
-1

Меня удивляет, насколько распространено это странное понятие «интерпретируемый байт-код». Вы когда-нибудь слышали о JIT-компиляции? Ваш аргумент не может быть применен к Java.

Но, оставляя JVM в стороне, бывают случаи, когда прямой многопоточный код или даже тривиальная интерпретация байт-кода могут легко превзойти сильно оптимизированный нативный код. Объяснение довольно простое: байт-код может быть достаточно компактным и соответствовать вашему крошечному кешу, когда версия того же алгоритма с собственным кодом будет иметь несколько кеш-пропусков за одну итерацию.

SK-логика
источник
Распространенность интерпретации, возможно, происходит из-за людей, сведущих в своей информатике. Виртуальная машина java - это машина, которая принимает байт-код java и запускает его на компьютере / не / способном к непосредственному запуску байт-кода java, без возможности написания функционально эквивалентной нативной программы. Ergo это переводчик. Вы можете дать его методам кэширования любое имя, которое вы можете себе представить, JIT или другое, но это интерпретатор по определению интерпретатора.
thiton
@ thiton, скорее всего, ваш собственный CS-фу довольно слабый. JVM не делает никакой интерпретации (для горячих точек) - как человек, который осмеливается упомянуть CS, вы должны знать, что это значит, и как операционная семантика интерпретации отличается от выполнения нативного кода. Но, вероятно, вы просто недостаточно знаете CS, чтобы отличить компиляцию от интерпретации.
SK-logic
2
Угу, но для запуска байт-код должен быть преобразован в собственный код - вы не можете передать байт-код Java в ЦП. Таким образом, аргумент размера недопустим.
Quant_dev
@quant_dev, конечно - я сказал, что это дело совершенно не связано с JVM. Вам понадобится гораздо более простой механизм байт-кода, чтобы этот эффект работал.
SK-logic
-1

За исключением JIT, GC и так далее, C ++ может быть очень и очень легко сделан гораздо более медленным, чем Java. Это не будет отображаться в тестах, но одно и то же приложение, написанное разработчиком Java и разработчиком C ++, может быть намного быстрее в Java.

  • Перегрузка оператора. Каждый простой оператор, такой как «+» или «=», может вызывать сотни строк кода, проверяя безопасность, операции с диском, ведение журнала, отслеживание и профилирование. И они настолько просты в использовании, что, перегружая операторов, вы используете их естественным и обильным образом, не замечая, как складывается их использование.
  • Шаблоны. Они не так сильно влияют на скорость, как память. Неосторожное использование шаблонов приведет к генерации миллионов строк кода (альтернативы базовому шаблону) без вашего ведома. Но затем время бинарной загрузки, использование памяти, использование подкачки - все это также противоречит тестам. И использование оперативной памяти идет через крышу.

Что касается расширенных шаблонов наследования, они в значительной степени похожи - в C ++ есть такие, которых нет в Java, и наоборот, но все они также вносят аналогичные значительные издержки. Так что нет особого преимущества C ++ в объектно-насыщенном программировании.

Еще одно предостережение: GC может быть быстрее или медленнее, чем управление распределением вручную. Если вы выделяете много небольших объектов, в среде GC обычно выделяется кусок памяти и ее части распределяются по мере необходимости для новых объектов. В управляемом - каждый объект = отдельное выделение занимает значительное время. OTOH, если вы используете malloc () много памяти одновременно, а затем просто присваиваете ее части своим объектам вручную или используете несколько больших экземпляров объектов, вы можете появиться намного быстрее.

Научная фантастика
источник
4
Я не согласен с обоими пунктами. Используете ли вы операторы или методы, не имеет значения. Вы говорите, что они будут размножаться. Ерунда - не более чем методы; Вам либо нужно позвонить им, либо нет. И шаблоны дают не больше кода, чем ручная запись этого кода снова для многократного использования. Может быть больше кода, чем при диспетчеризации во время выполнения (виртуальные функции), но это также не будет иметь значения: производительность строк кэша команд имеет наибольшее значение в тесных циклах, и здесь будет использоваться только один экземпляр шаблона, поэтому не будет никакого существенного давления памяти из-за шаблонов.
Конрад Рудольф
Обычное мышление - методы дорогие, операторы дешевые. Вы используете методы, когда это необходимо, операторы, когда вы хотите сэкономить время и оптимизировать. Это не технический вопрос, а психологический - дело не в том, что операторы «тяжелее», они просто намного проще в использовании и используются гораздо чаще. (плюс вы можете перегрузить часто используемый оператор в уже существующем коде, заставляя его делать то же самое, что и оригинальный, плюс дополнительный - и вдруг весь код значительно замедлится.
SF.
Я оспариваю, что этот психологический факт реален, и даже если это так, у вас нет выбора : если вам нужна функциональность, вы используете ее, независимо от того, заключена она в оператор или метод. Психология не имеет значения для вашего выбора семантики.
Конрад Рудольф
1
Хитрый вопрос. Я бы вообще не догадывался об этом, я бы измерял, действовал тогда . У меня никогда не было проблем с этой тактикой.
Конрад Рудольф
1
@KonradRudolph: Это все верно, когда речь идет о ясности и простоте написания кода, что делает его без ошибок и обслуживаемым. Тем не менее, вопрос эффективности реализации алгоритма остается неизменным: если вы собираетесь написать obj.fetchFromDatabase("key")три раза в пяти строках кода для одного и того же ключа, вы дважды подумаете, следует ли извлечь это значение один раз и кэшировать его в локальной переменной. Если вы пишете obj->"key"с ->перегрузкой, чтобы действовать как выборка из базы данных, вы гораздо более склонны просто пропустить ее, потому что стоимость операции не очевидна.
SF.
-2

Каким-то образом Stack Exchange не берет другие мои стековые точки, так что ... нет ответа, к сожалению ...

Тем не менее, второй самый голосующий ответ здесь полон дезинформации по моему скромному мнению.

Созданное вручную специалистом по C / C ++ приложение ВСЕГДА будет намного быстрее, чем Java-приложение. Там нет «так быстро, как Java или быстрее». это просто быстрее, именно из-за пунктов, которые вы цитируете ниже:

JIT-компиляция : Вы действительно ожидаете, что автоматический оптимизатор будет обладать умением опытного программиста и увидит связь между намерением и кодом, который ЦП действительно собирается запустить ??? Кроме того, все, что вы делаете, - это потеря времени по сравнению с уже скомпилированной программой.

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

Очевидно, это может быть только медленнее, чем то, что программист C (вы выбрали термин) сделал бы для управления своей памятью (и нет никаких утечек в правильно написанных приложениях).

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

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

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

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

В целом:

Java никогда не сможет достичь скорости C / C ++ из-за того, как она работает в JVM со множеством средств защиты, функций и инструментов.

У C ++ есть определенное явное преимущество в оптимизированном программном обеспечении, будь то для вычислений или игр, и обычные случаи, когда реализации C ++ выигрывают в соревнованиях по программированию, приводят к тому, что лучшие реализации Java можно увидеть только на второй странице.

На практике C ++ не является игрушкой и не позволит вам избежать многих ошибок, с которыми может справиться большинство современных языков, однако, будучи проще и менее безопасным, он по своей природе быстрее.

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

Морг.
источник
Очередной раз. Сборка мусора - это не только «инструмент, который освобождает». GC может компактифицировать ваши структуры. GC может обработать ваши слабые ссылки и помочь вам сбалансировать кэширование таким образом. Многоступенчатый сборщик мусора делает распределение кучи намного дешевле, чем громоздкие, медленные newили malloc(). В целом это может быть намного быстрее, чем любое ручное управление памятью, поскольку вы не сможете перемещать объекты вручную. Итак, все ваши рассуждения явно ошибочны и предвзяты. Ваши знания об алгоритмах GC и методах оптимизации JIT слишком ограничены.
SK-logic
4
Этот ответ полон неправильных представлений о том, что могут сделать современные оптимизаторы. Оптимизированный вручную код не имеет шансов против этого. Но в C ++ также есть оптимизирующий компилятор.
Конрад Рудольф
Спасибо за ваш комментарий SK-логика, но, как вы говорите, GC может быть намного быстрее в целом, мы говорим о том, что будет самым быстрым в конкретном случае, и, похоже, большинство людей согласны с тем, что все, что GC может может сделать программист, и даже лучше. Конечно, вы можете перемещать объекты вручную, когда у вас есть прямой доступ к памяти. Мои знания о внутренностях JVM ограничены, и я ожидаю, что Java-головы покажут мне свет, а не просто расскажут мне случайную чушь о том, что GC может делать вещи, которые нельзя делать вручную (смеется ... даже GC должен используйте инструкции процессора;)).
Морг.
Конрад, я согласен, что я в значительной степени недооцениваю современные оптимизаторы ... однако мне интересно, что вы считаете оптимизированный вручную код хуже, чем автоматически оптимизированный код. Что именно вы ожидаете, что компилятор увидит, что человек не может?
Морг.
1
Правильно . продолжайте нажимать -1, это не изменит тот факт, что C ++ быстрее, чем Java. Я, возможно, не очень много знаю о современных компиляторах, но это не имеет никакого значения к основному моменту, который является правильным и противоречит большинству проголосовавших здесь ответов. Почему еще C ++ будет приоритетом для nVidia на их GPU для HPC. Почему еще все игры были бы написаны на C ++, почему еще каждый движок БД был бы написан на C?
Морг.
-4

Я видел, по крайней мере, две впечатляющие mmo, сделанные на Java, и сказать, что это недостаточно быстро для игр - это неправильно. Просто потому, что разработчики игр предпочитают C ++ больше, чем другие языки, говорят, что он не просто связан с Java, это просто означает, что программисты никогда не баловались с какими-либо другими языками программирования / парадигмами. Все на любом языке, более продвинутом, как C / C ++ или даже Java, может создавать код, который может технически соответствовать или опровергать аргумент скорости. Все, что хорошо и сказано, сводится к тому, что программисты знают, с какими командами работает больше всего, и самое главное, почему они используют эти инструменты. Поскольку мы занимаемся разработкой игр в аспекте программирования, в этом должен быть аргумент. Проще говоря Все дело в деньгах и времени для делового тупика, использующего инструменты, отвечающие требованиям QA, и в реальном мире не имеет никакого значения для xx причин выбора C ++ вместо Java или любого другого языка. Это просто решение для массового производства. На самом базовом уровне вычислительных алгоритмов все, с чем мы играем, это единицы и нули, аргумент скорости - один из самых глупых аргументов, когда-либо применяемых к играм. Если вы хотите получить быстрое увеличение скорости, полностью исключите языки программирования и работайте со сборкой, что, возможно, является лучшим преимуществом на сегодняшний день.

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