|
Программирование >> Обобщенные обратные вызовы
Обычно современные компиляторы в состоянии лучше программиста решить, какие вызовы функций следует сделать встраиваемыми, а какие - нет, включая ситуации, когда одна и та же функция в разных местах может быть обработана по-разному. Почему? Простейшая причина в том, что компилятор знает больше о контексте вызова, поскольку ему известна реальная структура точки вызова - машинный код, сгенерированный для данной точки после применения других оптимизаций, таких как разворачивание циклов или удаление недостижимых ветвей профаммы. Например, компилятор может быть способен определить, что встраивание функции в некотором виугреннем цикле может сделать цикл слишком большим для размещения в кэше процессора, что приведет только к падению производительности, так что такой вызов не будет сделан встроенным, в то время как другие вызовы той же функции в других местах программы могут остаться встроенными. Ответ В: во время компоновки Теперь мы переходим к более интересным и более современным аспектам встраивания. Вопрос: может ли функция быть встроена во время компиляции? Ответ: да. Этот ответ является основой дополнительного вопроса о функциях, которые не могут быть встраиваемыми ни при каких условиях. Этот вопрос добавлен мною в задачу, потому что обычно все верят, что такие виды функций существуют. В частности, обычно считается, что невозможно встроить функции, описания которых размещено в отдельном модуле, а не в заголовочном файле. Давайте попытаемся выполнять встраивание максимально интенсивно, насколько это возможно. Рассмотрим пример 25-1 с небольшим изменением. пример 25-2: затрудним работу оптимизатора, поместив функцию в отдельный модуль, и сделав недоступным ее исходное описание. - Файл main, срр - double SquareC double х ); int main О { double d = SquareC 3.14159 * 2.71828 ); - Файл square, obj (или .о) - содержит скомпилированное описание функции double SquareC double х ) { return х * х; } Здесь идея заключается в том, что реализация функции Square вынесена из единицы трансляции mai п. срр. На самом деле сделано даже больше: недоступны даже исходные тексты функции Square - только се объектный код. Теперь вызов Square гарантированно не будет встраиваемым! - скажет множество людей. Пока идет компиляция - они правы. В процессе компиляции mai п. срр никакой самый мощный и продвинутый компилятор не в состоянии получить доступ к исходным текстам определения функции Square. На вот продвинутый компоновщик с такой задачей справиться в состоянии, и некоторые популярные реализации так и посгупают. Ряд компиляторов, в частности, Hewlett-Packard, поддерживают такое межмодульное встраивание (cross-module inlining); в Microsoft Visual С++ 7.0 (известном как .NET ) и более поздних версий имеется опция /ltcg, которая означает link time code generation (генерация кода в процессе компиляции). Реальное преимущество такой технологии позднего встраивания заключается в знании реального контекста каждой точки вызова, что позволяет более интеллектуально подойти к вопросу о том, где и когда стоит выполнить встраивание. Вот еще пища для размышлений: вы заметили где-нибудь в описании примера 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, IE)), который представляет собой программу, созданную с использованием кодов операций из системы команд среды времени выполнения. Отклоняясь от основной темы - некоторые из этих сред имеют очень богатые средства поддержки конструкций объектно-ориентированных языков на уровне системы команд, так что классы, наследование и виртуальные функции поддерживаются ими непосредственно. Компилятор для такой платформы может (и многие так и поступают) транслировать исходную программу в промежуточный язык команд виртуальной машины последовательно, класс за классом, функция за функцией, возможно, после выполнения некоторой собственной оптимизации, включающей возможность встраивания еще на уровне компиляции. Если компилятор работает таким образом, то исходный текст функции на С++- может быть более или менее полно восстановлен по коду функции с той же сигнатурой, представленной в целевой системе команд. Конечно, компилятор не обязан следовать описанной методике, но даже если он поступает как-то иначе, следующие далее замечания о встраивании остаются в силе. А также такие родственники CLR, как Mono, DotGNU и Rotor, которые реализуют также стандарт ISO Common Language Infrastructure (CL!), определяющий подмножество CLR. Вернемся к нашему вопросу. Какое все это имеет отношение к встраиванию в процессе инсталляции приложения? Даже при использовании описанных сред времени выполнения в конечном счете процессор работает со своей собственной системой команд. Следовательно, среда отвечает за трансляцию языка команд виртуальной машины в код, который в состоянии понять процессор целевого компьютера. Очень часто это делается при первой инсталляции приложения, и именно в этот момент, как и в других процессах компиляции, могут быть выполнены (и часто выполняются) дополнительные оптимизации. В частности, компилятор .NET - NGEN IL - может выполнять встраивание в процессе инсталляции приложения, когда программа на языке !Е транслируется в команды процессора, готовые к выполнению. Здесь также стоит обратить внимание на нейтральность среды по отношению к языку программирования, так что оптимизация (включая встраивание), выполняемая в процессе инсталляции приложения, легко преодолевает границы применения отдельных языков программирования. Вас не должно удивлять, что если ваша программа на С# делает вызов небольшой функции на С++, то эта функция может в ко нечном итоге оказаться встроенной. Так когда же выполнять встраивание слишком поздно? Никогда не говори никогда, потому что наш разговор еще не окончен... Ответ Д: в процессе работы Ну хорошо, но когда мы запускаем программу на выполнение - то уж здесь-то все возможности для встраивания должны быть позади? Это может показаться совершенно невозможным, но встраивание вполне реально и в процессе выполнения программы, причем несколькими способами. В частности, я хочу упомянуть оптимизацию, управляемую профилированием (profile-directed optimization), и защищенное встраивание (guarded inlining). Так же, как и в случае среды, компилирующей программу при инсталляции, для этого нам потребуется наличие соответствующей поддержки времени выполнения на машине пользователя. Идея, лежащая в основе оптимизации, управляемой профилированием, заключается в том, что когда приложение выполняется, вставленные в программу обработчики могут собирать информацию о том, как реально используется программа, в частности, какие функции вызываются чаще других и при каких условиях (например, размер рабочего множества по сравнению с общим размером кэша в момент вызова функции). Собранные данные могут быть использованы для модификации выполнимого образа программы, так что избранные вызовы функций могут стать встраиваемыми при настройке приложения на работу в целевой среде, основанной на измерениях производительности в процессе реального выполнения профаммы. Защищенное встраивание представляет собой другой пример того, насколько агрессивной может оказаться оптимизация встраивания во время выполнения программы. В частности, в [AmoldOO] и [JLkesRVM] документирована Jikes Research Virtual Machine (RVM), динамический оптимизирующий компилятор пёе the Jalapeno для JVM. Помимо прочего, этот компилятор способен встраивать вызовы виртуальных функций, полагая, что получатель виртуального вызова будет иметь данный объявленный тип (чтобы избежать не только зафат на вызов функции, но и дополнительных зафат на диспетчеризацию виртуальности). На сегодняшний день компиляторы в состоянии сделать определенные вызовы виртуальных функций невиртуальными (и, таким образом, имеют возможность встраивать их), если целевой тип оказывается статически известен. Новое в среде Jikes/Jalapeiio то, что теоретически она может делать вызовы невиртуальными и встраивать их, даже если статический целевой тип не известен. Однако поскольку такое предположение может оказаться неверным, компилятор вставляет дополнительную защиту, которая выполняет проверку типа целевого объекта в процессе выполнения профаммы, и если он не соответствует ожидаемому, то программа возвращается к механизму обычного вызова виртуальной функции.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |