Вопросы о возможной оптимизации Java (или другого языка с управлением памятью)

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

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

Это заставляет меня задаться вопросом, пытался ли кто-нибудь реализовать http://en.wikipedia.org/wiki/Profile-guided_optimization (компиляция в двоичный файл + некоторые дополнения, затем запуск программы и анализ информации о времени выполнения тестового прогона для создания предположительно более оптимизированного двоичного файла для реального использования) для java/(другие языки с управлением памятью ) и как это будет сравниваться с jit-кодом? Кто-нибудь знает?


person Roman A. Taycher    schedule 12.07.2010    source источник


Ответы (3)


Профильная оптимизация имеет некоторые предостережения, одно из которых упоминается даже в статье Wiki, на которую вы ссылаетесь. Его результаты действительны

  • для данных образцов, представляющих, как ваш код фактически используется пользователем или другим кодом.
  • для данной платформы (ЦП, память + другое оборудование, ОС и т. д.).
    С точки зрения производительности существуют довольно большие различия даже между платформами, которые обычно считаются (более или менее) одинаковыми (например, сравните одну ядра, старый Athlon с 512M с 6-ядерным Intel с 8G, работающий на Linux, но с очень разными версиями ядра).
  • для данной JVM и ее конфигурации.

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

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

Цена этого — некоторое дополнительное время, необходимое для «профилирования» + JIT-компиляции. Большую часть времени он провел довольно хорошо.

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

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

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

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

Кстати, одного недостатка, упомянутого в других ответах, было бы довольно легко избежать: если оптимизация под управлением статики/профиля медленная (что, вероятно, так и есть), то делайте это только для выпусков (или RC, направляемых тестерам) или во время ночных сборок (где время делает не имеет большого значения).
Я думаю, что гораздо более серьезной проблемой будет наличие хороших тестовых примеров. Создание и поддержка их обычно непроста и занимает много времени. Особенно, если вы хотите иметь возможность выполнять их автоматически, что в данном случае было бы очень важно.

person Sandor Murakozi    schedule 21.07.2010

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

Когда вы запускаете javac, он просматривает только один файл .java, компилируя его в один файл .class. Все реализации интерфейса, виртуальные методы и переопределения проверяются на корректность, но остаются неразрешенными (поскольку невозможно узнать истинные цели вызова метода без анализа всей программы).

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

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

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

person benjismith    schedule 12.07.2010
comment
Согласованный. Обратите внимание, что некоторые AOT-компиляторы (я знаю только gcc, но могут быть и другие) имеют эту функцию и называют ее оптимизацией времени компоновки. Однако это относительно новый метод, и он по-прежнему связан с угадыванием того, какая функция будет вызываться чаще всего. Хотя в этом отношении есть профильная оптимизация, о которой упоминал ОП. - person ; 21.07.2010

Официальный компилятор Java Hot Spot выполняет «адаптивную оптимизацию» во время выполнения, что по сути то же самое, что и упомянутая вами оптимизация на основе профиля. Это было особенностью, по крайней мере, этой конкретной реализации Java в течение длительного времени.

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

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

person Gian    schedule 20.07.2010