Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #71, ОКТЯБРЬ 2006 г.

акробатика для программиста

КРИС КАСПЕРСКИ

Спецвыпуск: Хакер, номер #071, стр. 071-056-3


Начнем с функций. Из всех рассматриваемых компиляторов только Intel C++ поддерживает глобальную оптимизацию, а остальные — транслируют функции по отдельности, задействуя «сквозную» оптимизацию только на встраиваемых (inline) функциях. Отсюда следует, что чем выше степень дробления программы на функции (и чем меньше средний размер одной функции), тем ниже качество оптимизации, не говоря уже о накладных расходах на передачу аргументов, открытие кадра стека и т. д.

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

Оптимальная стратегия выглядит так: выключаем режим автоматического встраивания и стремимся программировать так, чтобы средний размер каждой функции составлял не менее 100-200 строк.

[удаление неиспользуемых функций.]

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

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

Теоретически, неиспользуемые функции должен удалять линкер, но популярные форматы объектных файлов к этому не располагают и в грубом приближении представляют собой набор секций (.text, .data и т. д.), каждая из которых, с точки зрения линкера, представляет монолитный блок, внутрь которого линкер не лезет, а просто объединяет блоки тем или иным образом. Вот потому-то и не рекомендуется держать весь проект в одном файле (особенно если это библиотека). Помещай в файл только «родственные» функции, всегда используемые в паре и по отдельности не имеющие никакого смысла.

Посмотрим, как устроена стандартная библиотека языка Си. Большинство функций реализовано в «своем» собственном файле, компилируемом в obj, содержащим только эту функцию и ничего сверх нее! Множество таких obj объединяются библиотекарем в один lib-файл, откуда линкер свободно достает любую необходимую функцию, не таща ничего остального! Собственно говоря, именно для этого библиотеки и придумали. Программисты, помещающие реализации всех функций своей библиотеки в один-единственный файл, совершают большую ошибку!

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

[вынос инвариантных функций из циклов.]

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

Назад на стр. 071-056-2  Содержание  Вперед на стр. 071-056-4