Здравствуйте, Mamut, Вы писали:
M>Помню, что эта тема не раз обсуждалась, но что-то сейчас я найти ее не могу.
M>Часто говорится о том, что JIT'у доступны оптимизации, которые C++ — компилятору и не снились
M>Можно поинтересоваться — какие :)
Здравствуйте, pgregory, Вы писали:
P>Выкидывание динамически мертвого кода, например
эээ ... например ? Насколько представляю себе процедуру JIT тот же компилятор, только срабатывает он не в момент сборки программы, а непосредственно перед ее запуском. Или он в процессе работы программы анализирует как она выполняется и еще что-то там компилирует дополнительно ?
Здравствуйте, Mamut, Вы писали:
M>Помню, что эта тема не раз обсуждалась, но что-то сейчас я найти ее не могу.
M>Часто говорится о том, что JIT'у доступны оптимизации, которые C++ — компилятору и не снились
M>Можно поинтересоваться — какие
Поскольку JIT активизируется во время выполнения, он использует большое количество информации, которую не может использовать компилятор. Это позволяет осуществлять некоторые оптимизации, которые доступны только во время выполнения:
* Процессор-специфические оптимизации — Во время выполнения JIT знает о том, может ли он использовать инструкции SSE или 3DNow. Ваш выполняемый файл будет скомпилирован специально для P4, Athlon или любого другого семейства процессоров. Будучи однажды созданным, тот же код будет совершенствоваться вместе с JIT и машиной пользователя.
* Удаление уровней преобразования логических адресов в физические, т.к. расположение функции и объекта доступны во время выполнения.
* JIT может осуществлять оптимизации через сборки, обеспечивая множество преимуществ, получаемых вами при компилировании программы со статическими библиотеками, но сохраняя гибкость и небольшие последствия использования динамических библиотек.
* Активные inline функции, вызываются чаще, т.к. во время выполнения учитывается управляющая логика. Оптимизации могут обеспечить существенное повышение скорости, и остается еще большое поле для улучшений в следующих версиях.
1 понятно.
2 вообще не понял, это как на пальцах.
3 тоже
4 понятно. Более того, чисто теоретически фичу анализа статистики выполнения кода можно использовать не только для того чтобы функции инлайнить. Есть неоднозначные оптимизации. Их целесообразность можно определить только зная как будет выполняться программа, что в компайл-тайме не всегда можно сделать. Статистика выполнения здесь на руку. Другое дело, что на сбор подробной статистики может уходить много процессорного времени и памяти ...
Здравствуйте, chukichuki, Вы писали:
C>Здравствуйте, pgregory, Вы писали:
P>>Выкидывание динамически мертвого кода, например
C>эээ ... например ? Насколько представляю себе процедуру JIT тот же компилятор, только срабатывает он не в момент сборки программы, а непосредственно перед ее запуском. Или он в процессе работы программы анализирует как она выполняется и еще что-то там компилирует дополнительно ?
Здравствуйте, pgregory, Вы писали:
P>Выкидывание динамически мертвого кода, например
Это ты о неиспользуемых функциях типа? Ну дык мапирование exe'шников на память справляется с этим не хуже
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, pgregory, Вы писали:
P>>Выкидывание динамически мертвого кода, например E>Это ты о неиспользуемых функциях типа? Ну дык мапирование exe'шников на память справляется с этим не хуже :shuffle:
Нет, я, например, о функции, содержащей кучу if-ов, которые меняются крайне редко.
Она из кошмара для бранч-предиктора превращается в конфетку.
Здравствуйте, chukichuki, Вы писали:
P>>Выкидывание динамически мертвого кода, например
C>эээ ... например ? Насколько представляю себе процедуру JIT тот же компилятор, только срабатывает он не в момент сборки программы, а непосредственно перед ее запуском. Или он в процессе работы программы анализирует как она выполняется и еще что-то там компилирует дополнительно ?
Даже текущий компилятор .NET компилирует не всю программу перед запуском, а по мере необходимости функций.
Соответственно возможен такой пример:
class Helper
{
staticreadonlybool _debugMode = GetDebugModeFromAppConfig();
public DoAction()
{
if( _debugMode ) //код в этом ифе вообще не будет скомпилирован если в конфигурационном файле(!) отключен дебаг-режим.
{
//do something
}
}
}
Жавовский HotSpot, насколько я помню, накапливает статистику работы и перекомплирует функции в соответствии с ними.
Здравствуйте, chukichuki, Вы писали: C>Поскольку JIT активизируется во время выполнения, он использует большое количество информации, которую не может использовать компилятор. Это позволяет осуществлять некоторые оптимизации, которые доступны только во время выполнения:
C> * Процессор-специфические оптимизации — Во время выполнения JIT знает о том, может ли он использовать инструкции SSE или 3DNow. Ваш выполняемый файл будет скомпилирован специально для P4, Athlon или любого другого семейства процессоров. Будучи однажды созданным, тот же код будет совершенствоваться вместе с JIT и машиной пользователя. C> * Удаление уровней преобразования логических адресов в физические, т.к. расположение функции и объекта доступны во время выполнения. C> * JIT может осуществлять оптимизации через сборки, обеспечивая множество преимуществ, получаемых вами при компилировании программы со статическими библиотеками, но сохраняя гибкость и небольшие последствия использования динамических библиотек. C> * Активные inline функции, вызываются чаще, т.к. во время выполнения учитывается управляющая логика. Оптимизации могут обеспечить существенное повышение скорости, и остается еще большое поле для улучшений в следующих версиях.
C>1 понятно. C>2 вообще не понял, это как на пальцах.
Ты себе представляешь, как работает статический импорт из DLL? Какие именно ассемблерные инструкции выполняются? C>3 тоже
Это почти что то же самое, что и 2. C>4 понятно. Более того, чисто теоретически фичу анализа статистики выполнения кода можно использовать не только для того чтобы функции инлайнить.
Это чистая практика. C> Есть неоднозначные оптимизации.
Их принято называть "спекулятивными". C>Их целесообразность можно определить только зная как будет выполняться программа, что в компайл-тайме не всегда можно сделать. Статистика выполнения здесь на руку. Другое дело, что на сбор подробной статистики может уходить много процессорного времени и памяти ...
Не так уж много. Есть два подхода: Profile-Guided Optimization и HotSpotting.
В первом программа сначала собирается без оптимизаций и гоняется под профайлером на тестовой нагрузке. Затем компилятору скармливается опять исходник, но на этот раз вместе с профилем программы. В итоге имеем высокооптимальную программу без затрат времени на оптимизацию в рантайме.
Во втором случае статистика выполнения собирается в рантайме — это не очень дорого; в простом случае достаточно раз в некоторое время засекать значение регистра EIP, чтобы понять, где программа проводит больше всего времени. Ну и накопленная статистика потребляется джит-компилятором для улучшения кода. Дополнительные затраты на оптимизацию окупаются в том случае, если нагрузка на систему трудно предсказуема на этапе разработки.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Не так уж много. Есть два подхода: Profile-Guided Optimization и HotSpotting. S>В первом программа сначала собирается без оптимизаций и гоняется под профайлером на тестовой нагрузке. Затем компилятору скармливается опять исходник, но на этот раз вместе с профилем программы. В итоге имеем высокооптимальную программу без затрат времени на оптимизацию в рантайме.
Чисто теоретически на это способны и обычные компиляторы в связке с профилировщиком. JIT для этого не нужен.
S>Во втором случае статистика выполнения собирается в рантайме — это не очень дорого; в простом случае достаточно раз в некоторое время засекать значение регистра EIP, чтобы понять, где программа проводит больше всего времени. Ну и накопленная статистика потребляется джит-компилятором для улучшения кода. Дополнительные затраты на оптимизацию окупаются в том случае, если нагрузка на систему трудно предсказуема на этапе разработки.
Следить за EIP самый простой случай, который много информации не даст. Максимум по таким данным функцию какую-нибудь заинлайнить можно или особо прожерливый цикл развернуть. Правильно выстроить условия if-ов (выше кто-то приводил пример) чтобы блок предсказания переходов процессора с ума не сходил уже наверное не получится.
Здравствуйте, chukichuki, Вы писали:
C>Чисто теоретически на это способны и обычные компиляторы в связке с профилировщиком.
Уже и чисто практически. MS VS поддерживает PGO начиная с 2005. C> JIT для этого не нужен.
Совершенно верно. Этот подход работает для любых языков, в том числе и со статической компиляцией.
S>>Во втором случае статистика выполнения собирается в рантайме — это не очень дорого; в простом случае достаточно раз в некоторое время засекать значение регистра EIP, чтобы понять, где программа проводит больше всего времени. Ну и накопленная статистика потребляется джит-компилятором для улучшения кода. Дополнительные затраты на оптимизацию окупаются в том случае, если нагрузка на систему трудно предсказуема на этапе разработки.
C>Следить за EIP самый простой случай, который много информации не даст.
Ты не поверишь: основые улучшения достигаются самыми простыми методами C>Максимум по таким данным функцию какую-нибудь заинлайнить можно или особо прожерливый цикл развернуть. C>Правильно выстроить условия if-ов (выше кто-то приводил пример) чтобы блок предсказания переходов процессора с ума не сходил уже наверное не получится.
Конечно же получится. Если мы видим, что EIP проводит больше времени в одной ветке if, чем в другой, то информации уже достаточно.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
C>>Следить за EIP самый простой случай, который много информации не даст. S>Ты не поверишь: основые улучшения достигаются самыми простыми методами C>>Максимум по таким данным функцию какую-нибудь заинлайнить можно или особо прожерливый цикл развернуть. C>>Правильно выстроить условия if-ов (выше кто-то приводил пример) чтобы блок предсказания переходов процессора с ума не сходил уже наверное не получится. S>Конечно же получится. Если мы видим, что EIP проводит больше времени в одной ветке if, чем в другой, то информации уже достаточно.
Во-первых просто по EIP-у достоверная информация получается только в ряде совсем уж примитивных случаев. Например, случай по сложнее. Пусть имеется
if (cond) {
DoSomething1();
} else {
DoSomething2();
}
Анализ EIP-а показыват что больше всего времени программа проводит в DoSomething1(). Какие можно сделать выводы относительно частот срабатываний разных веток if-а? Только по значению EIP-а почти никаких. Во первых неплохо бы удостовериться что DoSomething1() был вызван именно из приведенного выше фрагмента, а не из какого-нибудь другой части программы. Значит надо просмотреть стек вызовов. Во вторых неплохо бы удостоверится что DoSomething1() вызывается многократно. Только в этом случае имеет смысл оптимизировать условие if-а. Т.е. надо хранить статистику по вызовам функций. Причем, не просто статистику по вызовам, а по вызовам функций именно из определенного места программы. Ведь DoSomething1() может вызываться еще в куче разных мест. И только в том случае если DoSomething1() в этой ветке вызывается гораздо больше раз чем DoSomething2() в противоположной ветке надо что-то оптимизировать. Можно сделать проще и универсальнее — достаточно считать количество срабатываний отдельных веток if-а. Ну т.е. на каждую ветку каждого условного оператора завести отдельную ячейку памяти, которая бы считала какая ветка выполняется чаще.
Здравствуйте, chukichuki, Вы писали: C>Можно сделать проще и универсальнее — достаточно считать количество срабатываний отдельных веток if-а. Ну т.е. на каждую ветку каждого условного оператора завести отдельную ячейку памяти, которая бы считала какая ветка выполняется чаще.
Правильно. См. http://wikis.sun.com/display/HotSpotInternals/PerformanceTechniques, раздел Profiling.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Mamut, Вы писали:
M>Можно поинтересоваться — какие
Если очень коротко, то
JIT работает на target машине, а поэтому может учитывать потенциально всю ее специфику. И процессор, и память и не знаю уж что еще. И не компилировать вообще то, что не понадобится. И анализировать свою деятельность и деятельность программы, как тут уже было сказано.
Обычному компилятору это недоступно, он вынужден делать код, ориентированный на некую среднюю ситуацию, или в лучшем случае давать разработчику выбирать (процессор, например).
Но JIT надо работать быстро, иначе программа будет работать медленно. Быстро — это значит, что если обычный компилятор и JIT написаны одинаково качественно, то качество оптимизации JIT будет хуже, чудес не бывает. У обычного компилятора на оптимизацию времени сколько угодно, и если он большой проект будет час компилировать, то это не так уж важно.
PD>Но JIT надо работать быстро, иначе программа будет работать медленно. Быстро — это значит, что если обычный компилятор и JIT написаны одинаково качественно, то качество оптимизации JIT будет хуже, чудес не бывает. У обычного компилятора на оптимизацию времени сколько угодно, и если он большой проект будет час компилировать, то это не так уж важно.
Можно не согласиться. Дело в том что JIT может оптимизировать только горячие участки и относительно статической компиляции уделять им больше времени.
Здравствуйте, minorlogic, Вы писали:
M>Можно не согласиться. Дело в том что JIT может оптимизировать только горячие участки и относительно статической компиляции уделять им больше времени.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Но JIT надо работать быстро, иначе программа будет работать медленно. Быстро — это значит, что если обычный компилятор и JIT написаны одинаково качественно, то качество оптимизации JIT будет хуже, чудес не бывает. У обычного компилятора на оптимизацию времени сколько угодно, и если он большой проект будет час компилировать, то это не так уж важно.
Неверно. JIT компилирует не высокоуровневый код, а IL. JIT компилирует не всю программу, а по частям. Поэтому вполне возможно что JIT будет лучше оптимизировать код, чем обычный компилятор за приемлмое время. А учитывая что JIT компилирует один раз, то даже разница на порядок во времени работы JITа будет незначительной.
Здравствуйте, Pavel Dvorkin, Вы писали:
M>>Можно не согласиться. Дело в том что JIT может оптимизировать только горячие участки и относительно статической компиляции уделять им больше времени.
PD>И все же не час.
час-то на все, а тут только несколько реально критичных методов, которые надо перекомпилировать.
Здравствуйте, gandjustas, Вы писали:
G>Неверно. JIT компилирует не высокоуровневый код, а IL. JIT компилирует не всю программу, а по частям. Поэтому вполне возможно что JIT будет лучше оптимизировать код, чем обычный компилятор за приемлмое время. А учитывая что JIT компилирует один раз, то даже разница на порядок во времени работы JITа будет незначительной.
Ну здесь мы бумерангом ту же ситуацию обратно получаем. JIT — да, но при компиляции с исходного кода компилятор с C#, к примеру, не знает, на какую обстановку это ориентировано. И не знает хуже, чем обычный компилятор. Тот, к примеру, может учесть специфику x86-x64 со всеми конвейерами и микрооперациями (как Intel C++) или Intel-AMD (последнее маловероятно, конечно, но мы же о принципах говорим), а компилятор C#, если явно не указана платформа, вынужден делать в расчете на произвольную платформу. А оптимизацию ИМХО все же лучше делать с исходного языка, а не с псевдоассемблера (IL) — информации для принятия решений больше.