Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 [ 52 ] 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

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

Вот еще пища для размышлений: вы заметили где-нибудь в описании примера 25-2, что функция Square должна обязательно быть написана на С++? В этом и заключается второе преимущество технологии "позднего встраивания", которая нейтральна по отношению к языку. Функция Square может быть написана на Fortran или Pascal. Приятно. Все, о чем должен позаботиться компоновщик, - выяснить используемые функцией соглашения о передаче параметров и удалить код размещения аргументов в стеке и снятия с него, вместе с машинной командой вызова функции.

Но это далеко не все - настоящему мужчине всегда есть что сказать! Ведь мы только приступаем к серьезному разговору, так что не спешите...

Ответ Г: при инсталляции приложения

Теперь перемотаем пленку нашего разговора прямо к тому радостному моменту, когда мы наконец-то скомпилировали и ско.мпоновали наше приложение, собрали его в tar-файл, какой-нибудь setup.exe или .wpi, и гордо отправили упакованный компакт своему первому покупателю. Наступило самое время для того, чтобы:

а) сорвать наклейку с предупреждением о лицензии;

б) оплатить почтовые расходы;

в) объявить, что уж теперь-то все встраивание далеко позади. Да

Да, да, нет.

С середины 1990-х годов постоянно увеличивается количество продаж приложений, предназначенных для работы под управлением специализированных виртуальных машин. Это означает, что вместо того, чтобы скомпилировать программу в машинный код для конкретного процессора, операционной системы и API, приложение компилируется в поток байт-кода, который иитерпретируется или компилируется на машине пользователя средой времени выполнения, что позволяет абстрагироваться от конкретных возможностей процессора или операционной системы. Наиболее распространенные примеры включают (но не ограничиваются) Java Virtual Machine (JVM) и .NET Common Language Run-time (CLR)*\ В случае использования таких целевых сред компилятор транслирует исходный текст С+ + в упомянутый поток байт-кода (известный также как язык команд виртуальной машины (virtual machines instruction language, IL)), который представляет собой программу, созданную с использованием кодов операций из системы команд среды времени выполнения.

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

А также такие родственники CLR, как Mono, DotGNU и Rotor, которые реализуют также стандарт ISO Common Language Infrastructure (CLI), определяющий подмножество CLR.



Вернемся к нашему вопросу. Какое все это имеет отношение к встраиванию в процессе инсталляции приложения? Даже при использовании описанных сред времени выполнения в конечном счете процессор работает со своей собственной системой команд. Следовательно, среда отвечает за трансляцию языка команд виртуальной машины в код, который в состоянии понять процессор целевого компьютера. Очень часто это делается при первой инсталляции приложения, и именно в этот момент, как и в других процессах компиляции, могут быть выполнены (и часто выполняются) дополнительные оптимизации. В частности, компилятор .NET - NGEN IL - может выполнять встраивание в процессе инсталляции приложения, когда программа на языке !Е транслируется в команды процессора, готовые к выполнению.

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

Так когда же выполнять встраивание слишком поздно? Никогда не говори никогда, потому что наш разговор еще не окончен...

Ответ Д: в процессе работы

Ну хорошо, но когда мы запускаем программу на выполнение - то уж здесь-то все возможности для встраивания должны быть позади?

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

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

Защищенное встраивание представляет собой другой пример того, насколько агрессивной может оказаться оптимизация встраивания во время выполнения программы. В частности, в [AmoldOO] и [JLkesRVM] документирована Jikes Research Virtual Machine (RVM), динамический оптимизирующий компилятор пёе the Jalapeno для JVM. Помимо прочего, этот компилятор способен встраивать вызовы виртуальных функций, полагая, что получатель виртуального вызова будет иметь данный объявленный тип (чтобы избежать не только затрат на вызов функции, но и дополнительных затрат на диспетчеризацию виртуальности). На сегодняшний день компи-тяторы в состоянии сделать определенные вызовы виртуальных функций невиртуальными (и, таким образом, имеют возможность встраивать их), если целевой тип оказывается статически известен. Новое в среде Jikes/Jalapeno то, что теоретически она может делать вызовы невиртуальными и встраивать их, даже если статический целевой тип не известен. Однако поскольку такое предположение может оказаться неверным, компилятор вставляет дополнительную защиту, которая выполняет проверку типа целевого объекта в процессе выполнения программы, и если он не соответствует ожидаемому, то программа возвращается к механизму обычного вызова виртуальной функции.



Ответ Е: в некоторое другое время

Вспомним, что в ответе г) мы рассматривали встраивание в процессе инсталляции в некоторой среде времени выполнения, такой как JVM или .NET CLR. Конечно, проницательные читатели уже заметили, что ранее я упоминал только трансляцию из байт-кода в машинные команды в процессе инсталляции приложения, но есть и другое более распространенное время такой трансляции, а именно - JIT, где аббревиатура JIT означает компиляцию "just-in-time", т.е. при необходимости.

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

> Рекомендация

Встраивание может быть выполнено в любой момент времени.

Резюме

Подобно всем оптимизациям, встраивание зачастую более эффективно, если оно выполняется инструментарием, который осведомлен о генерируемом коде и/или среде выполнения, а не программистом. Чем позже выполняется встраивание, тем более точным и соответствующим целевой среде работы программы оно оказывается.

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

Встраивание - эго нечто гораздо большее, чем одно ключевое слово inline.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 [ 52 ] 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85