Что делает компилятор Java таким быстрым? - PullRequest
11 голосов
/ 31 декабря 2008

Мне было интересно, что делает основной компилятор Java (javac by sun) настолько быстрым при компиляции?

.. а также компилятор C # .NET от Microsoft.

Я сравниваю их с компиляторами C ++ (такими как G ++), поэтому, возможно, мой вопрос должен был состоять в том, что делает компиляторы C ++ такими медленными:)

Ответы [ 11 ]

16 голосов
/ 31 декабря 2008

На этот вопрос приятно ответили: Почему компиляция C ++ занимает так много времени? (как указал Джальф в разделе комментариев)

По сути, это концепция отсутствующих модулей C ++ и агрессивная оптимизация, выполняемая компилятором.

6 голосов
/ 31 декабря 2008

Я думаю, что самой сложной частью является не необходимость компилировать файлы заголовков (если они не очень большие, но в этом случае вы можете использовать предварительно скомпилированные заголовки). Хуже всего то, что грамматика C ++ слишком сильно зависит от контекста. Несмотря на то, что мне нравится C ++, мне жаль тех, кто должен написать парсер C ++.

5 голосов
/ 31 декабря 2008

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

Включение заголовочных файлов

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

Java (или C #, я буду говорить о Java, но они похожи в этом) не имеют включаемых файлов, они зависят от двоичных файлов при компиляции используемых классов. Это означает, что всякий раз, когда вы компилируете a.java, который использует объект B, определенный в b.java, он просто проверяет двоичный класс b.class, ему не нужно углубляться, чтобы проверить зависимости B, поэтому он может сократить процесс раньше (только с одним уровнем проверки).

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

Итак, в конце концов, в C / C ++ включено больше файлов, и в то же время обработка этих включений обходится дороже, чем обработка двоичных модулей.

Шаблоны

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

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

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

3 голосов
/ 31 декабря 2008

Поскольку они делают что-то совсем другое, компилятор C ++ создает оптимизированный нативный код, тогда как компиляторы C #, VB .Net и Java создают промежуточный язык, чем при первом запуске приложения превращается в нативный код, и поэтому вы получаете медленную загрузку. приложения в Java и т. д. при первом запуске приложения.

Компилятор C ++ должен выполнить полную оптимизацию, при которой языки JITed оптимизируются при запуске приложения.

Кто-то может поспорить, что вам нужно измерить время компиляции C ++ = время компиляции Java + время JIT при первой загрузке приложения, если вы хотите быть правильным, но я не думаю, что это будет правильно и справедливо, потому что вы сравнивают родные языки с JITed, а апельсины с яблоками.

2 голосов
/ 31 декабря 2008

Если вы думаете, что javac быстр, попробуйте Jikes .... (см. http://jikes.sourceforge.net/) Это компилятор Java, написанный на C ++. К сожалению, они не поспевают за последними спецификациями компилятора Java, но если вы хотите увидеть быстро, то это оно.

Tony

2 голосов
/ 31 декабря 2008

Вы правильно поняли в своем последнем предложении: это не Java или C #, которые быстро компилируются, это C ++, который чрезвычайно медленно компилируется из-за его сложной грамматики и функций, наиболее важно templates

2 голосов
/ 31 декабря 2008

Одной из наиболее трудоемких задач при компиляции является оптимизация кода.

Javac очень мало оптимизирует код при выполнении компиляции. Вместо этого JVM выполняет оптимизацию при запуске приложения.

C / C ++ необходимо оптимизировать при компиляции, так как оптимизация скомпилированного машинного кода трудна.

2 голосов
/ 31 декабря 2008

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

1 голос
/ 31 декабря 2008

Сложно сравнивать языки байт-кода, такие как java, с языками, скомпилированными с нуля, такими как C ++. Лучшее сравнение - Delphi против C ++, где Delphi компилируется намного быстрее. Поскольку это не имеет ничего общего с оптимизацией или байт-кодом, это должно быть связано с различиями в синтаксисе языка и относительной производительностью включений и модулей / модулей.

1 голос
/ 31 декабря 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...