Параметры компиляции


Содержание

Компиляция и компоновка с помощью GNU Compiler Collection (GCC)

GCC — GNU compiler collection – это инструментальное средство разработки программ на языках Си, Си++, Фортран и других.

В состав GCC входят:

  • Препроцессоры программ на языках Си и Си++.
  • Компиляторы для поддерживаемых языков. В мире Unix под компилятором (в узком смысле) понимается программа, выдающая в качестве результата текст программы на языке ассемблера.
  • Стандартные библиотеки языков Си++ и других (кроме Си).
  • Программы-драйверы компиляции, которые предоставляют универсальный интерфейс командной строки ко всем компонентам GCC и связанным с ними системным утилитам. Например, программа gcc позволяет управлять компиляцией программ на Си, g++ — компиляцией программ на Си++ и т. д.

В состав GCC не входят:

  • Ассемблер (GNU Assembler, команда as), компоновщик (GNU linker, команда ld1 ) и некоторые другие утилиты для работы с объектными и исполняемыми файлами. В Linux они находятся в инсталляционном пакете binutils.
  • Заголовочные файлы и объектные модули стандартной библиотеки языка Си. В Linux они находятся в инсталляционных пакетах glibc, glibc-devel, glibc-static

Тем не менее, они необходимы для компиляции программ на Си, ввиду чего будут рассмотрены наряду с инструментами GCC. Команда запуска GCC для языка Си в общем виде выглядит следующим образом:

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

Схема трансляции программ написанных на Си

Трансляция программы состоит из следующих этапов:

  • препроцессирование;
  • трансляция в ассемблер;
  • ассемблирование;
  • компоновка.

Традиционно исходные файлы программы на языке Си имеют суффикс имени файла .c, заголовочные файлы для программы на Си имеют суффикс .h. В файловых системах Unix регистр букв значим, и если, например, имя файла имеет суффикс .C, такой файл считается содержащим текст программы на языке Си++, и будет компилироваться компилятором языка Си++, а не Си

Препроцессирование.

Препроцессор просматривает входной .c файл, исполняет содержащиеся в нём директивы препроцессора, в частности, включает в него содержимое других файлов, указанных в директивах #include.

Файл-результат препроцессирования не содержит директив препроцессора, не раскрытых макросов, вместо директив #include в файл-результат подставлено содержимое соответствующих заголовочных файлов. Файл с результатом препроцессирования обычно имеет суффикс .i, однако после завершения трансляции все промежуточные временные файлы по умолчанию удаляются, поэтому чтобы увидеть результат препроцессирования (что, например, бывает полезно при отладке ошибок, связанных с небрежным использованием макросов) нужно использовать опцию -E командной строки gcc. Результат препроцессирования называется единицей трансляции (англ., translation unit) или единицей компиляции (англ., compilation unit)

Трансляция в ассемблер.

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

Ассемблирование.

На этой стадии работает ассемблер. Он получает на входе результат работы предыдущей стадии и генерирует на выходе объектный файл. Объектные файлы в UNIX имеют суффикс .o

Компоновка.

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

Запуск транслятора gcc

Рассмотрим основные возможности транслятора GNU Си. В командной строке задаётся список файлов для обработки. Какие операции необходимо выполнить с файлами – зависит от суффикса имен файлов. Возможные суффиксы перечислены в таблице ниже. Если имя файла имеет нераспознанный суффикс, это имя передаётся компоновщику

Суффикс имени файла Выполняемые действия
.h Заголовочный файл на языке Си. Не должен использоваться в аргументах команды gcc. Попытка трансляции такого файла вызывает сообщение об ошибке.
.c Файл на языке Си. Выполняется препроцессирование, трансляция ассемблирование и компоновка.
.i Препроцессированный файл на языке Си. Выполняется трансляция, ассемблирование и компоновка.
.s Препроцессированный файл на языке Си. Выполняется трансляция, ассемблирование и компоновка.
.S Файл на языке ассемблера. Выполняется препроцессирование, ассемблирование и компоновка
.o Объектный файл. Выполняется компоновка.
.a Файл статической библиотеки. Выполняется компоновка.

Действия по трансляции файла определяются для каждого указанного в командной строке файла индивидуально. Например, если в командной строке указаны имена файлов 1.c и 2.o, то для первого файла будут выполнены все шаги трансляции, а для второго – только компоновка. Исполняемый файл будет содержать результат трансляции первого файла, скомпонованный со вторым файлом и стандартными библиотеками.

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

Опция Описание
-E Остановиться после препроцессирования. Результат работы препроцессора выводится по умолчанию на стандартный поток вывода. Имя выходного файла можно указать с помощью опции -o. При этом если в командной строке указано несколько файлов, то в выходной файл будет помещён результат препроцессирования последнего файла.
-S Остановиться после трансляции в ассемблер. По умолчанию имя выходного файла получается из имени входного файла заменой суффикса .c или .i на суффикс .s. Явное имя выходного файла можно указать с помощью опции -o. Попытка использования опции -o и нескольких имён входных файлов вызывает сообщение об ошибке.
-c Остановиться после ассемблирования. По умолчанию имя выходного файла получается из имени входного файла заменой суффикса его имени на суффикс .o. Явное имя выходного файла можно указать с помощью опции -o, которая несовместима с указанием одновременно нескольких транслируемых файлов.
Если ни одной из перечисленных выше опций не задано, выполняются все стадии трансляции. Имя выходного файла по умолчанию равно a.out, но может быть изменено с помощью опции -o.
-o Позволяет задать явное имя выходного файла для любой стадии трансляции.

Например, командная строка

транслирует два файла на языке Си, объединяя их в одну программу с именем 1.

компонует два объектных файла, добавляя к ним стандартную библиотеку языка Си и стандартную математическую библиотеку (опция -lm), и помещает результат в исполняемый файл с именем 3. Прочие полезные опции транслятора gcc перечислены в таблице.

Опция Описание
-I PATH Добавляет каталог PATH в начало списка каталогов, которые просматриваются препроцессором при поиске файлов, подключаемых директивой #include. В командной строке может быть указано несколько опций -I, тогда каталоги просматриваются в порядке, в котором они указаны в командной
-D NAME Определяет макрос с именем NAME, который получает значение 1.
-D NAME=VALUE Определяет макрос с именем NAME, который получает заданное значение VALUE.
-Wall Включает выдачу большого количества предупреждающих сообщений, которые по умолчанию не выдаются. Опция должна использоваться при компиляции программ, все предупреждающие сообщения компилятора должны быть внимательно проанализированы, поскольку могут указывать на ошибки в программе.
-g Включает генерацию отладочной информации в исполняемую программу. Наличие отладочной информации позволяет отлаживать программу в терминах исходного языка, а не машинного кода
-O2 Включает большинство оптимизаций программы, которые одновременно уменьшают размер программы и увеличивают скорость её выполнения.
-L PATH Добавляет путь PATH в начало списка каталогов, которые просматриваются редактором связей при поиске библиотек, указанных с помощью опции -L. Если в командной строке указано несколько опций -L, они добавляются в том же порядке, в котором указаны в командной строке
-l name Добавляет библиотеку name к списку библиотек, которые участвуют в компоновке программы (обратите внимание на отсутствие пробела между опцией и именем библиотеки). В системах Unix редактор связей просматривает библиотеки один раз, поэтому неправильный порядок задания библиотек может привести к тому, что некоторые имена останутся неопределёнными, и компиляция завершится с ошибкой. Файл, хранящий библиотеку с именем name, называется libname.a, если библиотека статическая, и libname.so, если библиотека динамическая.
-static Указывает, что при компоновке не должны использоваться динамические библиотеки. Реализации всех используемых в программе функций будут добавлены непосредственно в исполняемый файл. Размер исполняемого файла программы может вырасти на сотни килобайт, зато такая программа перестанет быть зависимой от динамических библиотек. На некоторых системах могут отлаживаться только статически скомпонованные программы.

Использование стандартных библиотек языка Си

В языках Си и Си++ библиотеки состоят из двух частей:

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

  • Файлов реализации, содержащих тела функций и определения переменных, которые подключаются к программе на этапе компоновки (в случае статических библиотек) или на этапе выполнения (в случае динамических библиотек).
  • Заголовочные файлы стандартной библиотеки находятся в каталоге /usr/include и его подкаталогах, например, /usr/include/stdio.h или /usr/include/sys/types.h. Программа-драйвер gcc автоматически добавляет этот каталог в список для поиска заголовочных файлов, поэтому каталог /usr/include не нужно задавать в опции –I.

    Файлы динамических библиотек размещаются в каталоге /lib или /usr/lib, а файлы статических библиотек – в каталоге /usr/lib. Они задаются автоматически и опция –L для них не нужна. Файл динамической библиотеки языка Си называется libc.so и полный путь к нему – /lib/libc.so.

    Таким образом, если выписать явно пути и библиотеки, задаваемые при компиляции программы на Си с помощью gcc неявно, мы получим примерно следующую командную строку:

    Исключением являются математические функции стандартной библиотеки Си, объявленные в заголовочном файле , например, sin. Их реализации вынесены в отдельную библиотеку libm.so (libm.a), которая не указывается в списке подключаемых библиотек по умолчанию. Для компоновки программ, использующих математические функции, необходимо в командной строке gcc указать опцию -lm:

    Для того, чтобы увидеть все пути, передаваемые драйвером компиляции gcc препроцессору, компилятору, ассемблеру и компоновщику, можно использовать опцию -v:

    Компоновка программы

    Если исполняемая программа компонуется из нескольких единиц трансляции, компоновщик использует свои правила видимости имён, которые приведены ниже:

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

    Последнее правило можно продемонстрировать на следующем примере. Предположим, что в трёх файлах определена переменная var следующим образом:

    Если все три единицы компиляции объединяются в одну программу, то переменная var каждого из трёх файлов будет располагаться по одному и тому же адресу, и каждая из трёх функций будет работать, по сути, с общей переменной. Чтобы предотвратить такое слияние переменных можно использовать явную инициализацию переменной var, тогда компоновщик выдаст сообщение об ошибке как показано ниже.

    Видимость глобальных переменных при компоновке можно также регулировать с помощью спецификаторов класса памяти extern и static. Спецификатор extern запрещает компилятору выделять память под переменную в данном модуле. Спецификатор static локализует область видимости переменной единицей трансляции, в которой она определена.

    Программы из нескольких единиц трансляции

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

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

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

    Для каждого .c файла должен существовать интерфейсный файл с тем же именем, но суффиксом .h, в котором определяются переменные, функции, типы данных и пр., которые могут использоваться извне данной единицы компиляции.

    Исходный .c файл должен обязательно подключать свой собственный .h файл. В этом случае транслятор обнаружит рассогласования между объявлениями в .h-файлах и определениями в .c файле.

    Интерфейсный .h файл должен быть обязательно защищён от повторного включения т.н. стражем включения (англ., include guard):

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

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

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

    Параметры компиляции

    CS Condition Zero

    Интерактив
    — Смайлы
    — Стихи
    — Басни
    — Приколы
    — Аватары
    — Комиксы CS
    — Музыка
    — Иконки CS
    — Юзербары

    Сайты:


    3.4 Параметры компиляторов

    В данной статье описаны все параметры для компиляторов ZHLT. В улучшенных компиляторах ZHLT Custom Build помимо этих параметров используются некоторые дополнительные. О них Вы можете прочитать в одной из предыдущих статей об улучшенной версии ZHLT. Данная статья является переводом официального документа, идущего вместе с ZHLT. Статья взята по адресу: http://alexnd.narod.ru/ . Благодарим автора за отличный перевод!

    Каждый из четырех компиляторов имеет как свои собственные параметры, так и 9 общих, которые применимы к любому из компиляторов.

    1. HLCSG

    Пример запуска: hlcsg.exe -estimate -nowadtextures karta.map

    -nowadtextures
    Включить все используемые текстуры в конечный файл с картой (.bsp). Эта опция бесполезна с введением параметра -wadinclude и оставлена только для обратной совместимости.

    -wadinclude файл
    Включить текстуры из указанного .wad-файла в конечный файл с картой (.bsp). Этот параметр позволяет включать текстуры из указанного .wad-файла в конечный файл (.bsp). Позволяет использовать частичное совпадение имени, не реагирует на регистр букв, способен распознавать не только имена файлов, но и названия папок. Если необходимо вставить несколько файлов с текстурами, необходимо указать несколько параметров -wadinclude, по одному на каждый файл с текстурами. Только текстуры, действительно используемые в карте, удут вставлены в .bsp-файл.

    -noclip
    Не создавать «ограничительную» область для карты. Карты в Half-life, как и Quake1, имеют 4 замкнутые области (hulls), используемые движком игры: 1 область — видимая (для отрисовки видимых участков карты), и 3 области — ограничительные (для ограничения движения игроков на карте). Этот параметр позволяет отключить создания ограничительных областей на карте, делая ее «воздушной». Игрок не только сможет ходить сквозь стены, но и провалиться сквозь землю (если гравитация включена). Полезно для экономии времени при тестовой компиляции. Наблюдать за сделанными изменениями на карте можно будет только в режиме Зрителя (Spectator).

    -onlyents
    Обновить значение параметров объектов из исходника (.map) в готовой карте (.bsp) Данная опция обновляет объекты в уже созданной карте (файл .bsp). В большинстве случаев, однако, объекты нельзя редактировать. Добавление или удаления объекта может дезорганизовать остальные объекты, «испортив» брашевые объекты, особенно триггеры.

    — noskyclip
    Отключить автоматическое ограничение карты небом. По умолчанию hlcsg ограничивает доступное игроку пространство брашами с текстурой CLIP, а также удаляет все полигоны за пределами карты или «внутри» неба, что ускоряет выполнение программ hlvis и hlrad .

    -tiny #
    Установить минимальную площадь браша, меньше которой браши удаляются. Поверхности с маленькой площадью удаляются при компиляции. По умолчанию порог составляет 0,5 квадратных «юнита» (единица измерения в Half-Life, редакторе Worldcraft/Hammer). Довольно опасно удалять поверхности таким образом, так как карта может стать нерабочей, могут возникнуть ошибки LEAF PORTAL SAW INTO LEAF или (зеркальный коридор — эффект, когда игрок выходит за границы карты в режиме наблюдателя).

    -brushunion #
    Установить порог для сообщений о пересекающихся брашах. Эта опция дана для отладки. Ее значение устанавливает процентное соотношение объемов пересекающихся брашей, при достижении которого выдается предупреждение. Для начала неплохо использовать довольно высокое значение (95% и выше), так как уменьшение этого параметра приводит к сотням и даже тысячам сообщений. Для каждого пересечения брашей выводится информация о номерах брашей и о проценте перекрытия. Данная опция выключена по умолчанию; ее включение значительно замедляет работу hlcsg.

    -hullfile
    Загрузить собственный файл с информацией об ограничительных областях. Загружает собственный файл с информацией об ограничительных областях. Файл состоит из 3 строк с 3 числами в каждой, которые обозначают размеры области по осям X Y Z. Эта опция дана для авторов и мапперов определенных модов для Half-life, так что вам не следует слишком беспокоиться о ней.

    2. HLBSP

    Пример запуска: hlbsp.exe -estimate karta.map

    -leakonly
    Запустить HLBSP только для поиска «утечек» (ошибка LEAK). Если Вы знаете, что карта имеет «дыры» (LEAK), используйте данный параметр для поиска дыр и создания специального файла с информацией о дырах (.pts-файл).

    -subdivide #
    Установить размер делителя поверхностей. Поверхности в Half-life разбиваются на полигоны указанного в данном параметре размера (в юнитах). По умолчанию и максимальное значение параметра равно 240. Однако улучшенные компиляторы ZHLT Custom Build позволяют увеличить данный параметр, но при этом практически всегда возникают всевозможные ошибки :) Так было, по крайне мере, до версии ZHLT Custom Build 1.7. Обычно не стоит уменьшать этот параметр, так как это ведет к увеличению r_speeds.

    -maxnodesize #
    Установить максимальный размер портала. Этот параметр изменяет максимальный размер портала. Его уменьшение приводит к разбивке карты на большее количество порталов и, как следствие, к увеличению значения r_speeds. Однако r_speeds может фактически быть снижен за счет более быстрого или более точного расчета видимых участков на карте в процессе выполнения vis.

    -notjunc
    Не разбивать браши на более мелкие при соприкосновении (Т-соединения)(не для окончательной компиляции). Эта опция для отладки/настройки и не должна использоваться при обычной компиляции.

    -noclip
    Не обрабатывать ограничительную область (не для окончательной компиляции). Этот параметр — продолжение опции в компиляторе hlcsg . Он говорит hlbsp, что hlcsg скомпилировал карту с опцией и что не следует обрабатывать ограничительные области, так как они отсутствуют в .bsp файле.

    -nofill
    Не «заполнять» внешний мир (игнорирует утечки LEAK) (не для окончательной компиляции). Этот параметр позволяет не «заполнять» пустоту «снаружи» карты, в результате чего все наружные поверхности сохраняются. Не стоит запускать расчет освещения для карты, скомпилированной таким образом, хотя vis будет работать нормально.

    3. HLVIS

    Пример запуска: hlvis.exe -estimate -full karta.map

    -fast
    Ускоренный анализ (не для окончательной компиляции). Ускоренный анализ видимых областей на карте удобен для быстрого обследования создаваемой карты. Однако значение r_speeds оставляет желать лучшего, равно как и число объектных полигонов (epoly — entity polygons). Карту можно «осветить» с помощью hlrad, однако качество освещения и время компиляции освещения такой карты «пострадают». Разрабатываемую карту нужно регулярно компилировать с полным анализом видимости, так как ускоренный анализ может помешать вам заметить внезапное увеличение времени полного анализа, что свидетельствует о неправильной архитектуре и большому результирующему значению r_speeds.

    -full
    Полный анализ. Этот параметр задействует дополнительный блок расчета видимых областей на карте, что позволяет уменьшить количество ошибок по сравнению с обычным анализом (hlvis без параметров). Время компиляции увеличивается примерно на 30%. Значение r_speeds остается примерно таким же, с некоторым уменьшением в одних местах карты, и увеличением — в других (в основном из-за устранения ошибок при анализе).

    4. HLRAD

    Пример запуска: hlrad.exe -estimate -chart -extra -smooth 100 -dscale 1 -bounce 4 karta.map

    -sparse
    Задействовать алгоритм с матрицей с ограниченной памятью. Hlrad разбивает карту на множество блоков — патчей, из которых, в последствии, создается матрица для расчета освещения на карте. Изначально матричный алгоритм ограничен 65535 «патчами» или блоками. Использование памяти этим алгоритмом растет в геометрической прогрессии по мере увеличения количества патчей (требуемая память в байтах вычисляется как кол-во патчей в квадрате деленное на 16. Данный параметр hlrad задействует алгоритм сжатия матрицы, который за счет дополнительного использования процессора позволяет преодолеть предел в 65535 патчей, а также снизить потребность в оперативной памяти до 10% от объема, требуемого для обычного матричного алгоритма.

    -nomatrix
    Отключить использование матрицы полностью В связи с тем, что использование «урезанной» матрицы (параметр -sparse) приводит к сжатию данных, требуется четкая синхронизация процессов, чего сложно добиться на машинах с количеством процессоров более 2-х. Опция -nomatrix была добавлена для решения такой проблемы. Однако добавление «непрозрачных брашевых объектов» в ZHLT 2.2 очень сильно повлияло на производительность данного метода компиляции освещения в сторону ухудшения. Данный метод не подразумевает использование матрицы вообще, поэтому и не требует памяти для хранения такой структуры данных.

    -extra
    Включить более точный расчет освещения (9 point oversampling). Позволяет компилировать карту с наиболее реальным и красивым освещением.

    -bounce #
    Установить количество «отражений» света. Этот параметр устанавливает число раз отражений света. К моменту выполнения данной операции все данные уже предварительно посчитаны, так что дополнительное число отражений не займет много времени. Увеличение числа отражений приводит к сглаживанию теней и освещению темных мест на карте более реальным образом.

    -ambient r g b
    Установить освещенность окружающего мира (от 0.0 до 1.0, r g b). Эта опция устанавливает минимальную «яркость» каждой поверхности карты для того, чтобы ни одна из них не была полностью черной. Параметры соответствуют красному, зеленому и синему со значениями от 0.0 до 1.0.

    -maxlight #
    Установить максимальную интенсивность света. Этот параметр используется для ограничения яркости на хорошо освещенных участках, делая карту темнее.

    -circus
    Разрешить режим ‘circus’ для обнаружения неосвещенных участков. Это отладочная опция, которая заменяет все черные точки на любой поверхности на случайный яркий цвет. Для расчетов принимается во внимание только прямое освещение и игнорируется отраженный свет.

    -nopaque
    Отключить блокирование света обектами. Это отладочная опция, которая позволяет отключать блокирование света объектами (отбрасывание теней), у которых установлены флаги свещения (2). Блокирование света объектами замедляет работу hlrad, поэтому данная опция полезна для отладочной/промежуточной компиляции.

    -smooth #
    Установить порог сглаживания (в градусах). По умолчанию hlrad освещает каждую поверхность отдельно, что приводит к визуальному разграничению поверхностей в готовой карте. Если же угол между двумя поверхностями меньше указанного в этом параметре, расчет освещения будет сделан с использованием технологии сглаживания (имитации плавного перехода — округлости — между поверхностями).

    -chop #
    Установить размер патча для обычных текстур. Каждая поверхность разбивается на сетку с шагом, равным размеру патча. Получаемые блоки используются для расчета освещения поверхности. Увеличивая размер патча можно ускорить выполнение hlrad и уменьшить потребность в памяти, однако это приведет к потере качества на готовой карте (неровные, угловатые, нереалистичные тени). Уменьшение же размера патча наоборот приведет к более качественному освещению карты, но потребует больше ресурсов компьютера.

    -texchop #
    Установить размер патча для светящихся текстур. Светящиеся поверхности (текстуры описаны в lights.rad и испускают свет) разбиваются на блоки другого размера, нежели обычные поверхности. Обычно, размер блоков должен быть равен половине размера блока обычной поверхности (-chop) для того, чтобы освещение выглядело хорошо. Включение параметра -extra автоматически устанавливает значение данного параметра равным половине от значения -chop.

    -notexscale #
    Не изменять размеры патчей при изменении размеров текстур. По умолчанию hlrad использует данные о масштабе текстур при создании сетки и разбиении поверхностей на освещаемые патчи. Этот параметр отключает масштабирование патчей для масштабируемых текстур, что почти всегда приводит к увеличению числа патчей, так как на многих картах текстуры стен увеличены в 2-3 раза.

    -coring #
    Установить минимальный порог освещения. Эта опция управляет количеством требуемого освещения поверхности, чтобы она не была полностью черной.

    -dlight #
    Установить максимальную яркость прямого (неотраженного) света. Эта опция аналогична параметру -maxlight за исключением того, что она позволяет «нормализовать» свет вместо того, чтобы «обрезать» излишнюю яркость.

    -nolerp #
    Параметр nolerp отключает смешивание значений отраженного света для близлежащих патчей (использует ближайшее значение вместо этого). Добавлено автором для внутреннего тестирования кода освещения карты.

    -dscale #
    Установить множитель для освещения. Из-за ошибки в оригинальной программе компиляции освещения qrad, расчет прямого освещения во время компиляции производился дважды. Для создания наиболее реалистичных карт необходимо производить такой расчет только один раз, однако в связи с тем, что в течение длительного времени карты создавались с указанием этого параметра вручную, он был оставлен и в этой версии компиляторов. Использование значения «1» приведет к созданию наиболее реалистичных теней и освещения. Однако, так как этот параметр изменяемый, он имеет несколько других применений. Так, использование значения «0» полностью удалит прямое освещение из карты и оставит только отраженный свет. Таким образом, мы получили возможность проверять качество только отраженного света (противоположность параметру -bounce 0). Использование значений «3-4» создаст очень сильный контраст между тенями и освещенными участками, что может пригодиться на картах с пустынями или арктическими льдами.

    -fade #
    Установить уровень затемнения для всех источников света на карте (большее значение приводит к меньшему распространению света). Этот параметр является коэффициентом к формуле расчетов затемнения (1/[квардрат расстояния]). С его помощью можно регулировать расстояние, на которое распространяется прямой свет на карте. Отдельные объекты освещения могут иметь собственные значения затемнения. Данный параметр влияет только на прямое освещение, так как отраженный свет всегда имеет формулу затемнения (1/[квардрат расстояния]).

    -falloff #
    Установить формулу расчета затемнения (1 = 1/расстояние, 2 = 1/[квардрат расстояния]). Эта опция позволяет изменить формулу расчета затемнения.

    -scale #
    Установить множитель для конечного освещения на карте. Этот параметр влияет на конечные значения источников света после того, как все расчеты освещения выполнены. Уменьшение этого параметра приводит к затемнению карты, увеличение — к осветлению.

    -gamma #
    Установить глобальное значение цветов. Эта опция влияет на конечные откомпилированные данные, внося коррекцию по цветности.

    -sky #
    Установить влияние солнечного света на затененные поверхности. Многие поверхности на карте обращены к небу, но находятся в тени других объектов. Эта опция регулирует, какое значение «солнечного» («небесного») освещения должно быть добавлено к затененной поверхности.

    -lights имя_файла.rad
    Указать файл со светящимися текстурами вручную (.rad файл). Один .rad файл можно добавить вручную, помимо автоматически используемых файлов lights.rad и .rad

    -noskyfix
    Отключить глобальное применение объекта light_environment. ZHLT 2.1 добавил более простую в использовании систему «небесного» освещения с помощью объекта light_environment. По новым правилам на карте требуется только один объект light_environment, причем его месторасположение не играет роли — все небо начинает излучать свет. Параметр -noskyfix отключает это поведение и задйствует старый алгоритм, когда на разных участках карты требовались отдельные объекты light_environments для правильного освещения наружных участков карты.

    -incremental
    Использовать или создать файл с данными об освещении на карте. Это удобная отладочная опция для настройки освещения карты, особенно для медленных машин с ограниченной системной памятью. Такие этапы компиляции освещения как BuildVisLeafs, MakeScales и SwapTransfers можно пропустить при втором и последующих запусках hlrad используя этот параметр. Использование файла с данными об освещении требует выполнения несложных, четких операций:

    • Запустите hlrad как обычно, включив параметр -incremental. Карта скомпилируется и осветится нормально, вдобавок создастся файл с данными об освещении (размером до нескольких десятков мегабайт).
    • Точечные объекты освещения (light, light_spot, light_environment) могут быть изменены произвольным образом (передвинуты, изменены яркость, цвет и т.д.)
    • Значения объектов обновляются в файл .bsp с помощью компилятора hlcsg с параметром -onlyents.
    • Запустите hlrad как обычно опять с параметром -incremental. Необходимо сохранить точно такие же значения параметров -extra, -chop #, -texchop #, -notexscale #. Другие параметры можно менять как угодно.

    -dump
    Сохранить информацию о патчах для отладки hlrad. Это опция для разработчиков ZHLT, для сохранения информации о патчах, созданных на этапе разбиения карты на блоки и проверки правильности такого разбиения.

    5. Общие параметры

    -texdata #
    Изменяет максимальный размер памяти, выделяемый для текстур (в Кб). Half-life был построен с лимитом текстурной памяти в 2Мб, так же как и Opposing Force. Утилиты ZHLT по умолчанию имеют лимит в 4Мб. Такой объем памяти, выделяемый для текстур, может быть великоватым в совокупности с текстурами моделей, графикой на экране, и т.д., особенно для владельцев с устаревшими видеокартами типа Voodoo 1 и 2.

    -chart
    Показывает статистику bsp. Этот параметр заставляет программы выдавать на экран статистику bsp перед тем, как записать в файл. Наиболее полезно применять данную опцию с программами hlrad и hlvis. Утилита ripent всегда отображает статистическую диаграмму.

    -low / -high
    Запускает программу с низким/высоким приоритетом. Установка низкого приоритета (-low) очень удобна, так как вы можете выполнять несколько других приложений одновременно без ощутимого воздействия компиляторов на производительность системы. Рекомендуется использовать при наличии достаточного количества оперативной памяти.

    -nolog
    Не создавать файлы отчета. Этот параметр отключает автоматическое создание файлов отчета (.log) и файлов с сообщением об ошибке (.err)

    -threads #
    Вручную указать количество процессов. Эта опция используется для указания количества процессов, в основном, на машинах с операционной системой, отличной от Windows, когда невозможно автоматически определить количество процессоров. На машинах с операционной системой Windows эта опция позволяет задать меньшее число процессов, чем имеющееся количество процессоров.

    -estimate
    Показывает расчетное время компиляции. Этот параметр заменяет стандартный индикатор прогресса (10. 20. ) на указатель с расчетным временем компиляции, количеством выполненной работы и оставшейся для выполнения. Предсказание времени часто бывает неточным, однако в целом такой индикатор более подробен, нежели стандартный.

    -verbose
    Компилировать с дополнительными сообщениями. Большинство утилит выдают «второстепенные» сообщения и информацию, если установить данную опцию. С развитием утилит версий 2.х, такие сообщения, связанные с работой самих утилит, отображаются при включенном параметре -dev , тогда как сообщения, связанные с картостроением, отображаются при включенном параметре -verbose.

    -noinfo
    Не показывать настройки утилит. ZHLT 2.1 добавил свойство вывода на экран текущей конфигурации каждой утилиты перед началом работы. В таблице настроек показывается текущее значение и значение по умолчанию каждого параметра. Из-за большого объема текстовой информации это может вызвать проблемы компиляции, например, в Worldcraft, так как последний иногда зависает от большого объема текста, скроллирующегося в его окне компиляции.

    KNZSOFT Разработка ПО, консультации, учебные материалы

    Князев Алексей Александрович. Независимый программист и консультант.

    С++ для начинающих. Урок 1. Компиляция

      Содержание

    Обзор компиляторов

    Существует множество компиляторов с языка C++, которые можно использовать для создания исполняемого кода под разные платформы. Проекты компиляторов можно классифицировать по следующим критериям.

    1. Коммерческие и некоммерческие проекты
    2. Уровень поддержки современных тенденций и стандартов языка
    3. Эффективность результирующего кода

    Если на использование коммерческих компиляторов нет особых причин, то имеет смысл использовать компилятор с языка C++ из GNU коллекции компиляторов (GNU Compiler Collection). Этот компилятор есть в любом дистрибутиве Linux, и, он, также, доступен для платформы Windows как часть проекта MinGW (Minumum GNU for Windows). Для работы с компилятором удобнее всего использовать какой-нибудь дистрибутив Linux, но если вы твердо решили учиться программировать под Windows, то удобнее всего будет установить некоммерческую версию среды разработки QtCreator вместе с QtSDK ориентированную на MinGW. Обычно, на сайте производителя Qt можно найти инсталлятор под Windows, который сразу включает в себя среду разработки QtCreator и QtSDK. Следует только быть внимательным и выбрать ту версию, которая ориентирована на MinGW. Мы, возможно, за исключением особо оговариваемых случаев, будем использовать компилятор из дистрибутива Linux.

    GNU коллекция компиляторов включает в себя несколько языков. Из них, группу языков Си составляет три компилятора.

    1. g++ — компилятор с языка C++.
    2. gcc — компилятор с языка C (GNU C Compiler).
    3. gcc -lobjc — Objective-C — это, фактически, язык C с некоторой макро-магией, которая доступна в объектной библиотеке objc. Ее следует поставить и указать через ключ компиляции -l.

    Этапы компиляции

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

    1. Препроцессинг — обработка текстовых файлов утилитой препроцессора, который производит замены текстов согласно правилам языка препроцессора C/C++. После препроцессора, тексты компилируемых файлов, обычно, значительно вырастают в размерах, но теперь в них содержится все, что потребуется компилятору для создания объектного файла.
    2. Ассемблирование — процесс превращения текста на языке C++ в текст на языке Ассемблера. Для компиляторов GNU используется синтаксис ассебмлера AT&T.
    3. Компилирование — процесс превращения текстов на языке Ассемблера в объектные файлы. Это файлы состоящие из кодов целевого процессора, но в которых еще не проставлены адреса объектов, которые находятся в других объектных файлах или библиотеках.
    4. Линковка — процесс объединения объектных файлов проекта и используемых библиотек в единую целевую сущность для целевой платформы. Это может быть исполняемая программа или библиотека статического или динамического типа.

    Рассмотрим подробнее упомянутые выше стадии обработки текстовых файлов на языке C++.

    Препроцессинг

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

    Основными элементами языка препроцессора являются директивы и макросимволы. Директивы вводятся с помощью символа «решетка» (#) в начале строки. Все, что следует за символом решетки и до конца строки считается директивой препроцессора. Директива препроцессора define вводит специальные макросимволы, которые могут быть использованы в следующих выражениях языка препроцессора.

    На входе препроцессора мы имеем исходный файл с текстом на языке C++ включающим в себя элементы языка препроцессора.

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

    Ассемблирование

    Процесс ассемблирования с одной стороны достаточно прост для понимания и с другой стороны является наиболее сложным в реализации. По своей сути это процесс трансляции выражений одного языка в другой. Более конкретно, в данном случае, мы имеем на входе утилиты ассемблера файл с текстом на языке C++ (компиляционный лист), а на выходе мы получаем файл с текстом на языке Ассемблера. Язык Ассемблера это низкоуровневый язык который практически напрямую отображается на коды инструкций процессора целевой системы. Отличие только в том, что вместо числовых кодов инструкций используется англоязычная мнемоника и кроме непосредственно кодов инструкций присутствуют еще директивы описания сегментов и низкоуровневых данных, описываемых в терминологии байтов.

    Ассемблирование не является обязательным процессом обработки файлов на языке C++. В данном случае, мы наблюдаем лишь общий подход в архитектуре проекта коллекции компиляторов GNU. Чтобы максимально объеденить разные языки в одну коллекцию, для каждого из языков реализуется свой транслятор на язык ассемблера и, при необходимости, препроцессор, а компилятор с языка ассемблера и линковщик делаются общими для всех языков коллекции.

    Компиляция

    В данном случае, мы имеем компилятор с языка ассемблера. Результатом его работы является объектный файл полученный на основе всего того текста, что был предоставлен в компиляционном листе. Поэтому можно говорить, что каждый объектный файл проекта соответствует одному компиляционному листу проекта.

    Объектный файл — это бинарный файл, фактически состоящий из набора функций. Однако в исходном компиляционном листе не все вызываемые функции имели реализацию (или определение — definition). Не путайте с объявлением (declaration). Чтобы компиляционный лист можно было скомпилировать, необходимо, чтобы объявления всех вызываемых функций присутствовали в компиляционном листе до момента их использования. Однако, объявление, это не более чем имя функции и параметры ее вызова, которые позволяют во время компиляции правильно сформировать стек (передать переменные для вызова функции) и отметить, что тут надо вызвать функцию с указанным именем, адрес реализации которой пока не известен. Таким образом, объектные файлы сплошь состоят из таких «дыр» в которые надо прописать адреса из функций, которые реализованы в других объектных файлах или даже во внешних библиотеках.

    Вообще, разница между объявлением (declaration) и определением (definition) состоит в том, что объявление (declaration) говорит об имени сущности и описывает ее внешний вид — например, тип объекта или параметры функции, в то время как определение (definition) описывает внутреннее устройство сущности: класс памяти и начальное значение объекта, тело функции и пр.

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

    Линковка

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

    Средства сборки проекта

    Традиционно, программа на языке C++ собирается средствами утилиты make исполняющей сценарий из файла Makefile. Сценарий сборки можно писать самостоятельно,
    а можно создавать его автоматически с помощью всевозможных средств организации проекта. Среди наиболее известных средств организации проекта можно указать следующие.

    1. GNU Toolchain — Старейшая система сборки проектов известная еще по сочетанию команд configure-make-«make install».
    2. CMake — Кроссплатформенная система сборки, которая позволяет не только создать кроссплатформенный проект но и создать сценарий компиляции под любые известные среды разработки, для которых написаны соответствующие генераторы сценариев.
    3. QMake — Достаточно простая система сборки, специально реализованная для фреймворка Qt и широко используемая именно для сборки Qt-проектов. Может быть использована и просто для сборки проектов на языке C++. Имеет некоторые проблемы с выявлением сложных зависимостей метакомпиляции, специфической для Qt, поэтому, даже в проектах Qt, рекомендуется использование системы сборки CMake.

    Современные версии QtCreator могут работать с проектами, которые используют как систему сборки QMake, так и систему сборки CMake.

    Простой пример компиляции

    Рассмотрим простейший проект «Hello world» на языке C++. Для его компиляции мы будет использовать консоль, в которой будем писать прямые команды компиляции. Это позволит нам максимально прочувствовать описанные выше этапы компиляции. Создадим файл с именем main.cpp и поместим в него следующий текст программы.

    В представленом примере выполнена нумерация строк, чтобы упростить пояснения по коду. В реальном коде нумерации не должно быть, так как она не входит в синтаксическое описание конструкций языка C++.

    В первой строке кода записана директива включения файла с именем iostream в текст проекта. Как уже говорилось, все строки, которые начинаются со знака решетки (#) интерпретируются в языках C/C++ как директивы препроцессора. В данном случае, препроцессор, обнаружив директиву включения файла в текст программы, директиву include, выполнит включение всех строк указанного в директиве файла в то место программы, где стоит инструкция include. В результате этого у нас получится большой компиляционный лист, в котором будут присутствовать множество символов объявленных (declaration) в указанном файле. Включаемые файлы, содержащие объявления (declaration) называют заголовочными файлами. На языке жаргона можно услышать термины «header-файлы» или «хидеры».

    Чтобы увидеть результат препроцессинга можно воспользоваться опцией -E компилятора g++. По умолчанию, в этом случае, результат препроцессинга будет выведен в стандартный поток вывода. Чтобы можно было удобно рассмотреть его, следует перенаправить стандартный поток вывода в какой-нибудь текстовый файл. В представленном ниже примере это будет файл main.E.

    В третьей строке программы описана функция main(). В контексте операционной системы, каждое приложение должно иметь точку входа. Такой точкой входа в операционных системах *nix является функция main(). Именно с нее начинается исполнение приложения после его загрузки в память вычислительной системы. Так как операционная система Windows имеет корни тесно переплетенные с историей *nix, и, фактически, является далеким проприентарным клоном *nix, то и для нее справедливо данное правило. Поэтому, если вы пишете приложение, то начинается оно всегда с функции main().

    При вызове функции main(), операционная система передает в нее два параметра. Первый параметр — это количество параметров запуска приложения, а второй — строковый массив этих параметров. В нашем случае, мы их не используем.

    В пятой строке мы обращаемся к предопределенному объекту cout из пространства имен std, который связан с потоком вывода приложения. Используя синтаксис операций, определенных для указанного объекта, мы передаем в него строку «Hello world» и символ возврата каретки и переноса строки.

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

    Следующим шагом проведения эксперимента выполним останов компиляции файла main.cpp после этапа ассемблирования. Для этого воспользуемся ключом -S для компилятора g++. Здесь и далее, знак доллара ($) обозначает стандартное приглашение к вводу команды в консоли *nix. Писать знак доллара не требуется.

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

    Для остановки компиляции после, собственно, компиляции следует воспользоваться ключом -c для компилятора g++.

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

    В результате исполнения этой команды появится файл a.out который и представляет собой результат компиляции — исполняемый файл программы. Запустим его и посмотрим на результат выполнения. При работе в операционной системе Windows, результатом компиляции будет файл с расширением exe. Возможно, он будет называться main.exe.

    Добавить комментарий Отменить ответ

    Для отправки комментария вам необходимо авторизоваться.

    параметры компиляции

    1 параметры компиляции

    2 параметры компиляции

    3 compiler options

    4 compiler options

    5 compiler toggles

    6 compiler toggles

    7 compiler toggles

    8 compiler toggles

    9 options

    compiler options — параметры трансляции, параметры компиляции

    10 sharing options

    compiler options — параметры трансляции, параметры компиляции

    См. также в других словарях:

    Сравнение языков программирования — Эту статью следует викифицировать. Пожалуйста, оформите её согласно правилам оформления статей. Условные обозначения … Википедия

    C++11 — C++11[1][2] или ISO/IEC 14882:2011[3] (в процессе работы над стандартом носил условное наименование C++0x[4][5]) новая версия стандарта языка C++, вместо ранее действовавшего ISO/IEC 14882:2003. Новый стандарт включает дополнения в ядре… … Википедия

    MTASC — Логотип MTASC Тип Компилятор исходного кода Разработчик Motion Twin Операционная система Microsoft Windows, Linux, Mac OS X Последняя версия … Википедия

    Парадигма — (Paradigm) Определение парадигмы, история возникновения парадигмы Информация об определении парадигмы, история возникновения парадигмы Содержание Содержание История возникновения Частные случаи (лингвистика) Управленческая парадигма Парадигма… … Энциклопедия инвестора

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

    GCC Inline Assembly — GCC Inline Assembly Встроенный ассемблер компилятора GCC, представляющий собой язык макроописания интерфейса компилируемого высокоуровнего кода с ассемблерной вставкой. Содержание 1 Особенности 2 Предварительные сведения … Википедия

    Тип данных — (встречается также термин вид данных) фундаментальное понятие теории программирования. Тип данных определяет множество значений, набор операций, которые можно применять к таким значениям и, возможно, способ реализации хранения значений и… … Википедия

    Валютный тип — Тип данных Содержание 1 История 2 Определение 3 Необходимость использования типов данных … Википедия

    Типизация данных — Тип данных фундаментальное понятие теории программирования. Тип данных определяет множество значений, набор операций, которые можно применять к таким значениям, и, возможно, способ реализации хранения значений и выполнения операций. Любые… … Википедия

    C++ — У этого термина существуют и другие значения, см. C. См. также: Си (язык программирования) C++ Семантика: мультипарадигмальный: объектно ориентированное, обобщённое, процедурное, метапрограммирование Тип исполнения: компилируемый Появился в … Википедия

    PHP — У этого термина существуют и другие значения, см. PHP (значения). PHP Семантика: мультипарадигменный … Википедия

    Параметры компиляции

    Есть всего три популярных, высококачественных, широко принятых в индустрии компиляторов C/C++:

    • GCC (Gnu Compiler Collections и GNU C Compiler), кроссплатформенный и Open-Source, используется в Linux как основной, на Windows известен как MinGW
    • MSVC (Microsoft Visual C/C++), низкая кроссплатформенность и закрытый код, используется в Windows как основной
    • LLVM/Clang, кроссплатформенный и Open-Source, используется в Mac OSX как основной, на Windows умеет быть совместимым и с MinGW, и с MSVC, доступен в Visual Studio 2015 и выше в модификации Clang/C2

    Принципы работы GCC и Clang можно детально исследовать благодаря открытому исходному коду и отладочным средствам.

    GCC (компиляция и вывод ассемблера)

    Разберёмся, как использовать GCC из командной строки. На UNIX-платформах GCC доступен по команде gcc , а для Windows есть порт GCC — MinGW. Воспользуемся примером кода, складывающего два числа:

    Компиляция файла из командной строки с опциями по умолчанию (отладочная сборка без оптимизаций):

    Вывод программы после запуска:

    Получение ассемблерного кода для отладочного режима без оптимизаций возможно с опцией -S . По умолчанию создаваемый ассемблер использует синтаксис AT&T, который заметно отличается от синтаксиса Intel.

    Можно получить ассемблерный код в режиме с оптимизациями, используя флаг -O2 , где “O” в верхнем регистре. Если сравнить отладочный и оптимизированный код с помощью утилиты diff, будут видны сильные отличия в цепочках инструкций.

    Вы можете скомпилировать ассемблер с помощью того же gcc, который сам передаст нужные параметры утилите “gas” (GNU Assembler).

    Clang (компиляция, вывод ассемблера и LLVM-IR)

    Clang разрабатывался как прозрачная замена компилятору GCC для Linux и Mac OSX. Поэтому большая часть опций, касающихся компиляции C/C++, у этих двух компиляторов совпадает. Компиляция примера на языке C выглядит точно так же:

    Генерация ассемблера с синтаксисом Intel:

    Бекенды GCC и Clang

    GCC и Clang оба используют гибкие фреймворки для построения бекендов компилятора. В GNU Compiler Collections используется собственный промежуточный язык и бекенд GIMPLE, который сильно упрощает написание компиляторов для новых языков в составе GNU Compiler Collections, но плохо подходит для изучения новичком. Проект LLVM гораздо дружественнее к новичкам и студентам, и именно его использует компилятор Clang.

    Вы можете изучать промежуточный код проекта LLVM, называемый LLVM-IR, с помощью clang, исследуя преобразование кода из C в LLVM-IR:

    Упражнения

    • Напишите 3-4 простейших программы в 10-20 строк на C (сложение двух чисел, вывод текущего времени с начала эпохи UNIX, вывод версии операционной системы, переворачивание строки т.п.). Сгенерируйте из этих программ листинги в машинном ассемблере либо в LLVM-IR, и сравните листинги от разных программ с помощью diff. Попробуйте собрать минимальный шаблон ассемблерного кода, который можно было бы разворачивать в полноценную программу путём подстановки цепочки инструкций вместо переменной .

    PS-Group

    Материалы для курсов в Институте Программных Систем и в Волгатехе

    Параметры компиляции

    Самая актуальная документация по Visual Studio 2020: Документация по Visual Studio 2020.

    Дополнительные свойства конфигурации построения проекта можно задать в диалоговом окне Дополнительные параметры компилятора конструктора проекта. Это диалоговое окно относится только к проектам Visual Basic.

    Чтобы получить доступ к этому диалоговому окну, выполните следующие действия.

    В Обозреватель решений выберите узел проекта (не узел Решение ).

    В меню Проект выберите пункт Свойства. В появившемся конструкторе проектов перейдите на вкладку Компиляция.

    На странице Страница «Компиляция» в конструкторе проектов (Visual Basic) выберите Конфигурация и Платформа. В упрощенных конфигурациях построения списки Configuration и Platform не отображаются. Для получения дополнительной информации см. Debug and Release Project Configurations.

    Щелкните Дополнительные параметры компиляции.

    Отображаемые на компьютере имена или расположения некоторых элементов пользовательского интерфейса Visual Studio могут отличаться от указанных в следующих инструкциях. Это зависит от имеющегося выпуска Visual Studio и используемых параметров. Дополнительные сведения см. в разделе Персонализация среды IDE.

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

    Удалить проверки переполнения для целочисленных значений
    По умолчанию этот флажок снят, чтобы проверка переполнения целые числа. Выделите этот флажок, чтобы удалить проверка переполнения целые числа. Если выделить этот флажок, вычисления целого числа могут выполняться быстрее. Однако если удалить переполнение проверку и ресурсы тип данных переполнение, неверные результаты могут храниться без вызываться ошибки.

    Если выбрана условий переполнения и переполнения операции целые числа, возникает исключение OverflowException. Если условий переполнения не проверяются, переполнения операции целые числа, не вызывает исключение.

    Включить оптимизацию
    По умолчанию этот флажок не устанавливается, чтобы отключить оптимизацию компилятора. Установите этот флажок, чтобы включить оптимизацию компилятора. Оптимизация кода делает код более быстрым, коротким и более эффективным. Однако поскольку оптимизации вызывают перераспределение кода в выходном файле, оптимизаций могут сделать отладку сложным.

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

    Создать отладочную информацию
    Выберите из списка вариант Отсутствует, Полная или Только PDB. Вариант Отсутствует указывает, что отладочной информации генерироваться не будет. Вариант Полная указывает, что будет генерироваться полная отладочная информация, а вариант Только PDB — только отладочная информация PDB. По умолчанию этот параметр имеет значение Полная.

    Константы условной компиляции имеют результат аналогичен параметрам использования директивы препроцессора #Const в файле источника, за исключением того, что константы определены открытым и применяются ко всем файлам в проекте. Константы условной компиляции можно использовать вместе с директивой #Else #If… Then…, чтобы компилировать файлы источника условно. Дополнительные сведения см. в разделе Условная компиляция.

    Определить константу DEBUG
    По умолчанию этот флажок установлен, указывая тем самым, что константа DEBUG будет задана.

    Определить константу TRACE
    По умолчанию этот флажок установлен, указывая тем самым, что константа TRACE будет задана.

    Настраиваемые константы
    Введите в это текстовое поле все настраиваемые константы для данного приложения. Записи должны быть разделены запятыми с соблюдением следующей формы: Name1=»Value1″,Name2=»Value2″,Name3=»Value3″.

    Что каждый программист должен знать об оптимизациях кода компилятором

    Продукты и технологии:

    Visual Studio 2013, компилятор Visual C++, Microsoft .NET Framework

    В статье рассматриваются:

    • важные оптимизации кода компилятором Visual C++;
    • как компилятор Visual C++ использует блок обработки векторов (vector unit) в процессоре;
    • как управлять оптимизациями кода компилятором;
    • оптимизации компилятором в Microsoft .NET Framework.

    Высокоуровневые языки программирования предлагают множество абстрактных программных конструкций, таких как функции, условные выражения и циклы, которые удивительно повышают производительность нашего труда. Однако один из недостатков написания кода на высокоуровневом языке программирования — потенциально возможное значительное снижение скорости работы программы. В идеале, вы должны писать понятный, простой в сопровождении код, не жертвуя производительностью. По этой причине компиляторы пытаются автоматически оптимизировать код для повышения его быстродействия, и в наши дни они стали весьма изощренными в оптимизациях. Компиляторы могут преобразовывать циклы, условные выражения и рекурсивные функции, исключать целые блоки кода и использовать преимущества архитектуры набора инструкций (instruction set architecture, ISA) целевого процессора, чтобы сделать код быстрым и компактным. Гораздо лучше сосредоточиться на написании понятного кода, чем пытаться делать оптимизации вручную, которые приводят к криптографическому коду, трудному в сопровождении. По сути, ручная оптимизация кода может не дать компилятору выполнить дополнительные или более эффективные оптимизации.

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

    Эта статья посвящена оптимизациям, выполняемым компилятором Visual C++. Я намерен обсудить самые важные методы оптимизации и решения, которые должен принимать компилятор, чтобы применить их. Моя цель не в том, чтобы рассказать вам об оптимизации кода вручную, а в том, чтобы показать, почему вы можете довериться компилятору в оптимизации кода в ваших интересах. Эта статья ни в коей мере не претендует на полный анализ оптимизаций, выполняемых компилятором Visual C++. Однако она демонстрирует оптимизации, о которых вы должны знать, и то, как взаимодействовать с компилятором для их применения.

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

    Определение оптимизаций кода компилятором

    Оптимизация — это процесс преобразования части кода в другую функционально эквивалентную часть для улучшения одной или более характеристик кода. Две самые важные характеристики — это скорость работы и размер кода. К другим характеристикам относятся энергопотребление, необходимое для выполнения кода, время компиляции кода и — в случае, если конечный код требует JIT-компиляции, — длительность JIT-компиляции.

    Компиляторы постоянно совершенствуются в отношении методов, применяемых ими для оптимизации кода. Но они не идеальны. Тем не менее, вместо траты на ручную оптимизацию программы гораздо продуктивнее использовать специфические средства компилятора и дать ему возможность оптимизировать код.

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

    1. Пишите понятный, простой в сопровождении код. Не рассматривайте объектно-ориентированные средства Visual C++ как врагов производительности. Новейшая версия Visual C++ умеет сводить к минимуму соответствующие издержки, а иногда и полностью исключать их.
    2. Используйте директивы компилятора. Например, сообщите компилятору задействовать то соглашение по вызову функций, которое работает быстрее, чем предлагаемое по умолчанию.
    3. Применяйте встроенные в компилятор функции (intrinsic function). Встроенной называется особая функция, реализация которой автоматически предоставляется компилятором. Компилятор знает о такой функции все и заменяет ее вызов чрезвычайно эффективной последовательностью инструкций, использующих преимущества целевой ISA. В настоящее время Microsoft .NET Framework не поддерживает встроенные функции, поэтому они не поддерживаются ни одним из управляемых языков. Однако Visual C++ имеет обширную поддержку этого функционала. Заметьте: хотя применение встроенных функций может повысить производительность кода, оно ухудшает его читаемость и портируемость.
    4. Используйте оптимизацию на основе профиля (profile-guided optimization, PGO). С помощью этого метода компилятор больше узнает о том, как будет вести себя код в период выполнения, и сможет соответственно оптимизировать его.

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

    Методов оптимизации компилятором много: от простых преобразований, таких как свертывание констант (constant folding), до экстремальных преобразований вроде планирования выполнения команд (instruction scheduling). Однако в этой статье я ограничусь обсуждением некоторых из важнейших оптимизаций — тех, которые могут значительно повысить производительность (на десятки процентов) и уменьшить размер кода: замены вызовов телами функций (function inlining), оптимизаций COMDAT и оптимизаций циклов (loop optimizations). Первые два метода я рассмотрю в следующем разделе, а затем покажу, как контролировать оптимизации, выполняемые Visual C++. Наконец, я кратко поясню оптимизации в .NET Framework. На протяжении всей статьи я буду использовать Visual Studio 2013 для компиляции кода.

    Генерация кода на этапе связывания

    Генерация кода на этапе связывания (Link-Time Code Generation, LTCG) — это метод выполнения полных оптимизаций программы (whole program optimizations, WPO), написанной на C/C++. Компилятор C/C++ транслирует каждый файл исходного кода раздельно и создает соответствующий объектный файл. То есть компилятор может применять оптимизации только к индивидуальному файлу исходного кода, а не ко всей программе. Однако некоторые важные оптимизации могут быть выполнены лишь при анализе полной программы. Вы можете применять эти оптимизации в период связывания (link time), а не при компиляции, так как компоновщик (linker) имеет полное представление о программе.

    Один из недостатков написания кода на высокоуровневом языке программирования — потенциально возможное значительное снижение скорости работы программы.

    Когда LTCG включена (указанием ключа /GL), драйвер компилятора (cl.exe) запустит только блок предварительной обработки (front end) компилятора (c1.dll или c1xx.dll) и отложит до этапа связывания запуск блока окончательной обработки (back end) (c2.dll). Конечные объектные файлы будут содержать код на C Intermediate Language (CIL), а не аппаратно-зависимый ассемблерный код. Потом, когда будет запущен компоновщик (link.exe), он увидит, что объектные файлы содержат CIL-код, и запустит блок окончательной обработки компилятора, который в свою очередь выполнит WPO, сгенерирует двоичные объектные файлы и вернет управление компоновщику, чтобы тот «сшил» все объектные файлы воедино и создал исполняемый файл.

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

    LTCG позволяет блоку окончательной обработки агрессивно выполнять многие оптимизации (указанием /GL вместе с ключами компилятора /O1 или /O2 и /Gw, а также ключей компоновщика /OPT:REF и /OPT:ICF). В этой статье я рассмотрю только подстановку функций (замену вызовов функций телами функций) (function inlining) и оптимизации COMDAT. Полный список оптимизаций LTCG см. в документации. Заметьте, что компоновщик может выполнять LTCG применительно к неуправляемым объектным файлам, смешанным объектным файлам (управляемым и неуправляемым), чисто управляемым объектным файлам, безопасно управляемым объектным файлам (safe managed object files) и безопасным файлам .netmodules.

    Я создам программу, состоящую из двух файлов исходного кода (source1.c и source2.c) и заголовочного файла (source2.h). Файлы source1.c и source2.c показаны на рис. 1 и 2 соответственно. Заголовочный файл, который содержит прототипы всех функций в source2.c, довольно прост, поэтому я не стану приводить его здесь.

    Рис. 1. Файл source1.c

    Рис. 2. Файл source2.c

    Файл source1.c содержит две функции: square (принимает целое значение и возвращает результат его возведения в квадрат) и main (главная функция программы). Функция main вызывает функцию square и все функции из source2.c, кроме isPrime. Файл source2.c содержит пять функций: cube (возвращает результат возведения в куб переданного целого значения), sum (возвращает сумму всех целых значений от 1 до переданного значения), sumOfcubes (возвращает сумму кубов всех целых значений от 1 до переданного значения), isPrime (определяет, является ли данное целое значение простым) и getPrime (возвращает x-ое простое число). Я опустил проверку ошибок, потому что она не представляет интереса в этой статье.

    Код прост, но полезен. В нем есть ряд функций, выполняющих простые вычисления, а некоторые нужны лишь для создания циклов. Функция getPrime самая сложная, потому что она содержит цикл while, из которого вызывается функция isPrime, тоже содержащая цикл. Я буду использовать этот код для демонстрации одной из важнейших оптимизаций (подстановки функций) и некоторых других оптимизаций.

    Я буду компилировать этот код в трех разных конфигурациях и изучать результаты, чтобы определить, как он был преобразован компилятором. Если вы будете следовать за мной, вам понадобится ассемблерный выходной файл (создается ключом /FA[s] компилятора) для анализа полученного ассемблерного кода и файл сопоставлений (map file) (создается ключом /MAP компоновщика) для определения выполненных оптимизаций COMDAT (компоновщик тоже может сообщать об этом, если вы используете ключи /verbose:icf и /verbose:ref). Поэтому убедитесь в том, что эти ключи указываются во всех рассматриваемых далее конфигурациях. Кроме того, я буду использовать компилятор C (/TC), чтобы облегчить анализ сгенерированного кода. Однако все, что обсуждается здесь, применимо и к коду на C++.

    Конфигурация Debug

    Конфигурация Debug используется в основном потому, что все оптимизации стадии окончательной обработки отключаются при задании ключа /Od компилятора без ключа /GL. При компиляции кода в такой конфигурации получаемые объектные файлы будут содержать двоичный код, который точно соответствует исходному коду. Чтобы убедиться в этом, вы можете проанализировать полученные ассемблерные выходные файлы и файл сопоставлений. Эта конфигурация эквивалентна отладочной конфигурации в Visual Studio.

    Конфигурация Compile-Time Code Generation Release

    Эта конфигурация похожа на конфигурацию Release, при которой оптимизации включены (задан ключ /O1, /O2 или /Ox компилятора), но ключ /GL компилятора не указан. При такой конфигурации полученные объектные файлы будут содержать оптимизированный двоичный код. Однако оптимизации на уровне всей программы не выполняются.

    Изучая сгенерированный из source1.c файл листинга ассемблерного кода, вы заметите, что выполнены две оптимизации. Прежде всего первый вызов функции square — square(n) на рис. 1 — полностью исключен за счет оценки вычисления при компиляции. Как это произошло? Компилятор определил, что функция square компактна, поэтому нужно подставить ее тело в код вместо вызова. После этого компилятор определил, что значение локальной переменной n известно и не изменяется между оператором присваивания и вызовом функции. Поэтому он заключил, что можно безопасно выполнять умножение и подставить результат (25). При второй оптимизации второй вызов функции square — square(m) — тоже был заменен телом функции. Однако, поскольку значение m не известно при компиляции, компилятор не мог оценить вычисление и сгенерировал реальный код.

    Теперь я посмотрю файл ассемблерного листинга, созданный из source2.c, который гораздо интереснее. Вызов функции cube в sumOfCubes был заменен телом функции. В свою очередь это позволило компилятору выполнить значительные оптимизации в цикле (как вы увидите в разделе «Оптимизации циклов»). Кроме того, в функции isPrime задействован набор инструкций SSE2 для преобразования из int в double при вызове функции sqrt, а также для преобразования double в int при возврате из sqrt. А sqrt вызывается лишь раз, когда начинается цикл. Заметьте: если компилятору не указывается ключ /arch, то x86-компилятор использует SSE2 по умолчанию. Большинство x86-процессоров, а также все процессоры x86-64 поддерживают SSE2.

    Конфигурация LTCG Release идентична конфигурации Release в Visual Studio. В этой конфигурации оптимизации включены и указывается ключ /GL компилятора. Этот ключ неявно задается при использовании /O1 или /O2. Он сообщает компилятору генерировать объектные файлы CIL, а не ассемблерные объектные файлы. Благодаря этому компоновщик вызывает блок окончательной обработки компилятора для выполнения WPO, как было описано ранее. Теперь я рассмотрю несколько оптимизаций WPO, чтобы показать колоссальные преимущества LTCG. Листинги ассемблерного кода, генерируемые при этой конфигурации, можно скачать как пакет, сопутствующий этой статье.

    Пока подстановка функций включена (/Ob, который включается всякий раз, когда вы запрашиваете оптимизации), ключ /GL позволяет компилятору подставлять функции, определенные в других блоках трансляции, независимо от того, указан ли ключ /Gy компилятора (о нем чуть позже). Ключ /LTCG компоновщика не обязателен и обеспечивает управление только компоновщиком.

    В идеале, разработчики предпочитают писать понятный, простой в сопровождении код, не жертвуя производительностью.

    Изучая ассемблерный листинг для файла source1.c, вы заметите, что все вызовы функций, кроме scanf_s, были заменены телами этих функций. В итоге компилятор смог выполнить вычисления cube, sum и sumOfCubes. Не была подставлена только функция isPrime. Однако, если бы она была подставлена вручную в getPrime, компилятор все равно сумел бы подставить getPrime в main.

    Как видите, подстановка функций важна не только потому, что она оптимизирует код, избавляясь от вызовов функций, но и по той причине, что позволяет компилятору выполнять многие другие оптимизации. Подстановка функции обычно повышает производительность за счет увеличения размера кода. Излишнее применение этой оптимизации ведет к феномену, известному как «раздувание кода» (code bloat). В каждой точке вызова компилятор проводит анализ издержек и преимуществ, а затем решает, стоит ли подставлять данную функцию.

    Ввиду важности подстановки компилятор Visual C++ обеспечивает гораздо больше поддержки для этой оптимизации, чем того требует стандарт в отношении контроля за подстановкой. Вы можете сообщить компилятору никогда не подставлять некий диапазон функций с помощью директивы auto_inline. Или указать компилятору никогда не подставлять конкретную функцию (метод), пометив ее как __declspec(noinline). Вы можете пометить функцию ключевым словом inline, чтобы подсказать компилятору, что эту функцию следует подставлять (хотя компилятор может проигнорировать эту подсказку, если подстановка окажется чистой потерей). Ключевое слово inline доступно еще с первой версии C++ — оно было введено в C99. В коде на C и C++ можно использовать специфичное ключевое слово __inline от Microsoft; оно полезно, когда вы применяете старую версию C, которая не поддерживает ключевое слово inline. Более того, вы можете указать ключевое слово __forceinline (в C и C++), чтобы заставить компилятор по возможности всегда подставлять функцию. Наконец, можно сообщить компилятору раскрыть рекурсивную функцию либо до конкретной, либо до неопределенной глубины ее подстановкой с помощью директивы inline_recursion. Заметьте, что компилятор в настоящее время не предлагает никаких средств, которые позволяли бы вам управлять подстановкой в точке вызова, а не в определении функции.

    Ключ /Ob0 полностью отключает подстановку. Вы должны использовать этот ключ при отладке (он автоматически указывается в конфигурации Debug в Visual Studio). Ключ /Ob1 сообщает компилятору рассматривать для подстановки только те функции, которые помечены ключевым словом inline, __inline или __forceinline. Ключ /Ob2, который вступает в действие при задании /O[1|2|x], информирует компилятор рассматривать для подстановки любую функцию. На мой взгляд, единственная причина использовать ключевое слово inline или __inline — управление подстановкой с помощью ключа /Ob1.

    Компилятор не сможет подставить функцию в определенных условиях. Один из примеров — виртуальный вызов виртуальной функции; такую функцию нельзя подставить, потому что компилятору может быть не известно, какая именно функция будет вызываться. Другой пример — вызов функции по указателю на функцию, а не по имени. Если вы хотите в максимальной мере использовать подстановку, избегайте таких условий. Полный список этих условий изложен в документации MSDN.

    Подстановка функций — не единственная оптимизация, которая более эффективна при применении на уровне всей программы. По сути, на этом уровне становится эффективнее большинство оптимизаций. В остальной части этого раздела я расскажу о специфическом классе таких оптимизаций — об оптимизациях COMDAT.

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

    Чтобы включить эти оптимизации от компоновщика, можно сообщить компилятору упаковать функции и переменные в разные разделы, указав ключи /Gy (связывание на уровне функций) и /Gw (оптимизация глобальных данных). Такие разделы называются COMDAT. Вы также можете пометить конкретную глобальную переменную ключевым словом __declspec(selectany), чтобы сообщить компилятору упаковать переменную в COMDAT. Затем вы указываете ключ /OPT:REF компоновщику, и он будет исключать функции и глобальные переменные, не имеющие ссылки (unreferenced). Кроме того, при задании ключа /OPT:ICF компоновщик будет свертывать идентичные функции и глобальные переменные — константы. (ICF расшифровывается как Identical COMDAT Folding.) Ключ /ORDER указывает компоновщику помещать COMDAT’ы в конечный образ в определенном порядке. Заметьте, что все эти оптимизации выполняются компоновщиком и не требуют ключа /GL компилятора. Ключи /OPT:REF и /OPT:ICF следует отключать при отладке по очевидным причинам.

    По возможности всегда используйте LTCG. Единственная причина не использовать LTCG — вы хотите распространять созданные объектные и библиотечные файлы. Вспомним, что эти файлы содержат CIL-код, а не ассемблерный код. CIL-код может использоваться компилятором и/или компоновщиком только той версии, с помощью которой он был сгенерирован, что может существенно ограничить область применения объектных файлов: у разработчиков должна быть та же версия компилятора, чтобы они смогли задействовать эти файлы. В этом случае, если только вы не собираетесь распространять объектные файлы для каждой версии компилятора, следует использовать генерацию кода этапа компиляции (compile-time code generation, CTCG). Помимо ограниченного применения, эти объектные файлы во много раз объемнее, чем соответствующие ассемблерные объектные файлы. Однако не забывайте о колоссальном преимуществе объектных CIL-файлов, которые поддерживают оптимизацию WPO.

    Оптимизации циклов

    Компилятор Visual C++ поддерживает несколько оптимизаций циклов, но я рассмотрю только три из них: развертывание циклов (loop unrolling), автоматическая векторизация (automatic vectorization) и выделение из цикла кода неизменяемых выражений (инвариантов) (loop-invariant code motion). Если вы модифицируете код на рис. 1 так, чтобы в sumOfCubes передавалась m вместо n, то компилятор не сможет определить значение параметра, поэтому он должен компилировать функцию для обработки любого аргумента. Полученная функция высоко оптимизирована и ее размер довольно велик, а значит, компилятор не станет подставлять ее.

    Компиляция кода с ключом /O1 приводит к созданию ассемблерного кода, оптимизированного по размеру. В этом случае к функции sumOfCubes никакие оптимизации не применяются. Компиляция с ключом /O2 дает код, оптимизированный по скорости работы. Размер кода будет значительно больше, но сам код будет работать существенно быстрее, так как цикл в sumOfCubes развернут и векторизован. Важно понимать, что векторизация была бы невозможна без подстановки функции cube. Более того, без подстановки функций развертывание циклов было бы не столь эффективным. Упрощенное графическое представление создаваемого ассемблерного кода показано на рис. 3. Схема потока управления одинакова для архитектур x86 и x86-64.

    Рис. 3. Схема потока управления в sumOfCubes

    Примечание
    SSE4.1 supported and x >= 8 SSE4.1 поддерживается и x >= 8
    Use SSE4.1 to perform 8 multiplications Используем SSE4.1 для выполнения восьми умножений
    SSE4.1 not supported or x

    Хейди Брейс (Hadi Brais) — аспирант в Индийском технологическом институте Дели (Indian Institute of Technology Delhi, IITD), исследует оптимизации компилятора для технологий памяти следующего поколения. Большую часть времени проводит в написании кода на C/C++/C# и глубоко копает в CLR и CRT. Ведет блог hadibrais.wordpress.com. С ним можно связаться по адресу hadi.b@live.com.

    Выражаю благодарность за рецензирование статьи эксперту Microsoft Джиму Хоггу (Jim Hogg).

    Параметры компиляторов

    В данной статье описаны все параметры для компиляторов ZHLT. В улучшенных компиляторах ZHLT Custom Build помимо этих параметров используются некоторые дополнительные. О них Вы можете прочитать в одной из предыдущих статей об улучшенной версии ZHLT. Данная статья является переводом официального документа, идущего вместе с ZHLT. Статья взята по адресу: http://alexnd.narod.ru/. Благодарим автора за отличный перевод!

    Каждый из четырех компиляторов имеет как свои собственные параметры, так и 9 общих, которые применимы к любому из компиляторов.

    5. Общие для всех компиляторов параметры

    1. HLCSG

    Пример запуска: hlcsg.exe -estimate -nowadtextures karta.map

    -nowadtextures
    Включить все используемые текстуры в конечный файл с картой (.bsp). Эта опция бесполезна с введением параметра -wadinclude и оставлена только для обратной совместимости.

    -wadinclude файл
    Включить текстуры из указанного .wad-файла в конечный файл с картой (.bsp). Этот параметр позволяет включать текстуры из указанного .wad-файла в конечный файл (.bsp). Позволяет использовать частичное совпадение имени, не реагирует на регистр букв, способен распознавать не только имена файлов, но и названия папок. Если необходимо вставить несколько файлов с текстурами, необходимо указать несколько параметров -wadinclude, по одному на каждый файл с текстурами. Только текстуры, действительно используемые в карте, удут вставлены в .bsp-файл.

    -noclip
    Не создавать «ограничительную» область для карты. Карты в Half-life, как и Quake1, имеют 4 замкнутые области (hulls), используемые движком игры: 1 область — видимая (для отрисовки видимых участков карты), и 3 области — ограничительные (для ограничения движения игроков на карте). Этот параметр позволяет отключить создания ограничительных областей на карте, делая ее «воздушной». Игрок не только сможет ходить сквозь стены, но и провалиться сквозь землю (если гравитация включена). Полезно для экономии времени при тестовой компиляции. Наблюдать за сделанными изменениями на карте можно будет только в режиме Зрителя (Spectator).

    -onlyents
    Обновить значение параметров объектов из исходника (.map) в готовой карте (.bsp) Данная опция обновляет объекты в уже созданной карте (файл .bsp). В большинстве случаев, однако, объекты нельзя редактировать. Добавление или удаления объекта может дезорганизовать остальные объекты, «испортив» брашевые объекты, особенно триггеры.

    -noskyclip
    Отключить автоматическое ограничение карты небом. По умолчанию hlcsg ограничивает доступное игроку пространство брашами с текстурой CLIP, а также удаляет все полигоны за пределами карты или «внутри» неба, что ускоряет выполнение программ hlvis и hlrad.

    -tiny #
    Установить минимальную площадь браша, меньше которой браши удаляются. Поверхности с маленькой площадью удаляются при компиляции. По умолчанию порог составляет 0,5 квадратных «юнита» (единица измерения в Half-Life, редакторе Worldcraft/Hammer). Довольно опасно удалять поверхности таким образом, так как карта может стать нерабочей, могут возникнуть ошибки LEAF PORTAL SAW INTO LEAF или

    Илон Маск рекомендует:  Глава 4 сортировка
    Понравилась статья? Поделиться с друзьями:
    Кодинг, CSS и SQL