Comments 13
Если это не шутка, то придется немного побить. В статьях речь шла об увеличении скорости интерпретации JavaScript. Ясно, что таже программа на C/C++ будет в разы быстрее. Если напишите более быстрый js-интерпретатор, то тогда можете написать что что-то обогнали.
«Муза — это женщина, она никогда не признается, кто был первым» =)
Шутка только в названии.
Принципы оптимизации — те же самые, что и было рассмотрено.
Принципы оптимизации — те же самые, что и было рассмотрено.
Пожалуйста прежде чем комменитровать что-либо походите по ссылкам и ознакомьтесь с первоисточником.
Там все эти оптимизации сделаны иначе, чем они делаются в классическом компиляторе для языка со статической типизацией (а-ля Intel C Compiler). Я позволю себе прокоментировать ваши комментарии с учетом этой разницы.
Классический подход: Проблема с открытой подстановкой (инлайном) в том, что она в общем случае увеличивает давление на регистры. А регистров никогда не бывает много =). В общем случае задача инлайна представляет собой разновидность задачи о ранце, которая как известно NP-полна. Поэтому что бы определить какой метод в какой подставлять обычно используют разного вида эвристики и явные флаги (always inline). Реже profile guided optimization: делается тестовый прогон и собирается информация о горячих методах, далее эвристики учитывают эту информацию. Как показывают исследования наибольший прирост производительности даёт как раз использование одновременно и профиля и эвристик на основе статического анализа.
Подход трассирующего JIT: Мы сразу получаем информацию о горячих точках и начинаем записывать трассу именно с горячей точки. Поэтому получается что трассирующий JIT фактически «на халяву» учитывает профиль программы.
Классический подход: Проблема межмодульного инлайна сейчас начинает решаться, компиляторы () обзаводятся оптимизациями на этапе компоновки. Плюс есть ряд компиляторов, в которых межмодульный инлайн поддерживался изначально (i.e. Excelsior JET).
Подход трассирующего JIT: Запись трассы ведётся во время исполнения и не прекращается во время вызова. Поэтому никакой проблемы межмодульного инлайна нет.
Там все эти оптимизации сделаны иначе, чем они делаются в классическом компиляторе для языка со статической типизацией (а-ля Intel C Compiler). Я позволю себе прокоментировать ваши комментарии с учетом этой разницы.
Это сейчас практически не проблема — памяти ощутимо много
Классический подход: Проблема с открытой подстановкой (инлайном) в том, что она в общем случае увеличивает давление на регистры. А регистров никогда не бывает много =). В общем случае задача инлайна представляет собой разновидность задачи о ранце, которая как известно NP-полна. Поэтому что бы определить какой метод в какой подставлять обычно используют разного вида эвристики и явные флаги (always inline). Реже profile guided optimization: делается тестовый прогон и собирается информация о горячих методах, далее эвристики учитывают эту информацию. Как показывают исследования наибольший прирост производительности даёт как раз использование одновременно и профиля и эвристик на основе статического анализа.
Подход трассирующего JIT: Мы сразу получаем информацию о горячих точках и начинаем записывать трассу именно с горячей точки. Поэтому получается что трассирующий JIT фактически «на халяву» учитывает профиль программы.
Компилятор НЕ умеет инлайнить функции из соседнего модуля, из системных библиотек, а так из динамических библиотек
Классический подход: Проблема межмодульного инлайна сейчас начинает решаться, компиляторы () обзаводятся оптимизациями на этапе компоновки. Плюс есть ряд компиляторов, в которых межмодульный инлайн поддерживался изначально (i.e. Excelsior JET).
Подход трассирующего JIT: Запись трассы ведётся во время исполнения и не прекращается во время вызова. Поэтому никакой проблемы межмодульного инлайна нет.
Ну эт обычно называется RTTI skip — выбросили проверку типов там где она не нужна…
RTTI skip = Run-time type information skip довольно странно звучащее название. Переводится как «Пропуск Информации о Типах Времени Исполнения». Кажется, вы его сами придумали.
Даже в статическом языке что бы выкинуть проверку типа компилятору надо основательно глобально подумать. Представьте себе функцию
А вы попробуйте
Естественно вся ответственность за типы, а точнее их возможное несоответствие ложится на программиста :)
Ну что я за кнопку опять нажал, растяпа?
Продолжим.
Легко выкидывать проверки в C++, который позволяет портить память.
В любом типо-безопасном (type safe) языке, что бы выкинуть проверку типа компилятору надо основательно глобально подумать. Представьте себе функцию на языке Java:
void f(BaseClass o) {
((ChildClass)o).foo();
}
Компилятору что бы выкинуть проверку надо доказать, что любое o является экземпляром ChildClass и никак иначе.
А теперь представьте, что типов вообще нет. Что есть замыкания (closures) и ассоциативные массивы. Это сущий ад для классического глобального анализа. Конечно, корифеи собаку на это съели, когда оптимизировали программы на Smalltalk подобных языках. Но трассирующий JIT предоставляет элегантное и что самое главное дешевое (т.е. быстрое и потребляющее мало памяти) решение данной проблемы.
Компилятор для type safe языка не имеет права такого делать. Ответственность исключительно на компиляторе и только на нём.
Продолжим.
Легко выкидывать проверки в C++, который позволяет портить память.
В любом типо-безопасном (type safe) языке, что бы выкинуть проверку типа компилятору надо основательно глобально подумать. Представьте себе функцию на языке Java:
void f(BaseClass o) {
((ChildClass)o).foo();
}
Компилятору что бы выкинуть проверку надо доказать, что любое o является экземпляром ChildClass и никак иначе.
А теперь представьте, что типов вообще нет. Что есть замыкания (closures) и ассоциативные массивы. Это сущий ад для классического глобального анализа. Конечно, корифеи собаку на это съели, когда оптимизировали программы на Smalltalk подобных языках. Но трассирующий JIT предоставляет элегантное и что самое главное дешевое (т.е. быстрое и потребляющее мало памяти) решение данной проблемы.
Естественно вся ответственность за типы, а точнее их возможное несоответствие ложится на программиста :)
Компилятор для type safe языка не имеет права такого делать. Ответственность исключительно на компиляторе и только на нём.
был сделан простой анролл цикла. Unrolling представляет собой дублирование тела цикла N раз:
Опять же таже проблема, что и с инлайном. Как определить, где делать unroll и где не делать? Не тривиальный вопрос на самом деле.
Но на самом деле unroll здесь не причем. Трассирующий JIT по своей структуре завязан собственно на оптимизацию горячих путей, т.е. путей по которым много ходят. А это как раз циклы. Поэтому циклы и оптимизируются.
Давайте тогда по пунктам :)
1. По поводу RTTI. Если информация о типе известна в момент компилирования, то такая информация в рантайме будет избыточна, и соответсвующие проверки можно удалить. Такая штука может называется по другому — а skipping — это наш местный жаргон.
2.Проблема межмодульного инлайна сейчас начинает решаться, компиляторы () обзаводятся оптимизациями на этапе компоновки.
Ну вот тот флажок -ipo и есть межмодульный инлайнер. Только нужно не забывать, что в этом
3. Как определить, где делать unroll и где не делать?
Там все просто — сейчас анролл делается всегда. Более того, еще иногда делается versioning — в коде создается несколько циклов, один вариант для выровненных данных, а другой — для невыровненных (к примеру).
1. По поводу RTTI. Если информация о типе известна в момент компилирования, то такая информация в рантайме будет избыточна, и соответсвующие проверки можно удалить. Такая штука может называется по другому — а skipping — это наш местный жаргон.
2.Проблема межмодульного инлайна сейчас начинает решаться, компиляторы () обзаводятся оптимизациями на этапе компоновки.
Ну вот тот флажок -ipo и есть межмодульный инлайнер. Только нужно не забывать, что в этом
3. Как определить, где делать unroll и где не делать?
Там все просто — сейчас анролл делается всегда. Более того, еще иногда делается versioning — в коде создается несколько циклов, один вариант для выровненных данных, а другой — для невыровненных (к примеру).
1. а если не известна? её же насчитать надо. а насчитывать для языка типа JS её можно ой как долго.
2. ну понятное дело, что совместимость на объектном уровне. главное в том, что проблема для классических компиляторов решается. а для JIT её вообще не существует.
3. Там — это где? И что даже нет ограничений на размер тела цикла?
2. ну понятное дело, что совместимость на объектном уровне. главное в том, что проблема для классических компиляторов решается. а для JIT её вообще не существует.
3. Там — это где? И что даже нет ограничений на размер тела цикла?
*Только нужно не забывать, что в этом
в этом случае теряется совместимость на объектном уровне.
в этом случае теряется совместимость на объектном уровне.
>Это сейчас практически не проблема — памяти ощутимо много
Нет, это проблема. Инлайнинг и анлоллинг ведёт к дублированию кода. Большой код может хуже помещаться в кеш инструкций и поэтому его выполнение будет медленнее. Например, ядро Linux сейчас по умолчанию компилируется с -Os, а не -O2 (опция CONFIG_CC_OPTIMIZE_FOR_SIZE).
Нет, это проблема. Инлайнинг и анлоллинг ведёт к дублированию кода. Большой код может хуже помещаться в кеш инструкций и поэтому его выполнение будет медленнее. Например, ядро Linux сейчас по умолчанию компилируется с -Os, а не -O2 (опция CONFIG_CC_OPTIMIZE_FOR_SIZE).
Вообщем по первым двум пунктам более менее понятно. Объектная несовместимость IPO состоит в том, что объетные файлы получаются в специальном формате. Там включается дополнительная информация об типах, функциях и параметрах. Я про *.o (*.obj).
Что же касается третьего пункта, то проблема «невлезания в кэш» в бльшинстве случаев надумана. Этот код должен еще что-то делать. Так же развитой префетч и предсказание переходов сглаживают различные ньюансы. Что касается ядра линукс — то на то оно и ядро, чтобы занимать поменьше.
Что же касается третьего пункта, то проблема «невлезания в кэш» в бльшинстве случаев надумана. Этот код должен еще что-то делать. Так же развитой префетч и предсказание переходов сглаживают различные ньюансы. Что касается ядра линукс — то на то оно и ядро, чтобы занимать поменьше.
Sign up to leave a comment.
Оптимизируем код, или перегоним Огнелиса в скорости