Вперед: 2.8.3. Работа с библиотеками
Назад: 2.8.1. Подготовка исходных текстов
К содержанию: Оглавление


2.8.2. Компиляция программ

Изготовление исполнимых программ из исходных текстов выполняется с помощью компиляторов, переводящих исходный текст программы в эквивалентную ей результирующую программу на языке машинных команд. Основными языками программирования на высокопроизводительных вычислительных системах являются С/C++ и Фортран [23]. Язык С создавался как язык для написания системных приложений, однако в последнее время широко применяется и для написания вычислительных программ. Язык программирования Фортран изначально разрабатывался для написания вычислительных программ. Для него разработано множество библиотек прикладных подпрограмм, в которых реализованы различные вычислительные алгоритмы. Например, библиотека LAPACK [24]содержит широчайший набор подпрограмм для решения различных задач линейной алгебры.

Синтаксис команды компиляции имеет вид:

компилятор [опции] файлы [библиотеки]

В квадратных скобках указываются необязательные компоненты команды.

На UNIX-подобных системах имеется множество компиляторов. Большая часть из них является коммерческими продуктами. Бесплатно распространяется пакет компиляторов Sun Studio для операционной системы Solaris и пакет GCC, поддерживаемый для широкого круга платформ и операционных систем. Для систем Linux пакет GCC является неотъемлемой частью дистрибутивов, поскольку является базовым компилятором сборки ядра системы и всех ее утилит.

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

В него входят компиляторы:

С недавних пор компилятор g77 заменен на gfortran, поддерживающий стандарт Fortran95. Компиляторы GCC оптимизирующие, поддерживающие три уровня оптимизации (опции -O1, -O2, -O3). На разных программах более эффективной может оказаться та или другая опция. В большинстве случаев наиболее приемлемой бывает опция -O2, при этом ускорение программы может достигать 2-3 раз. Типичные команды компиляции:

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

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

Компиляторы также поддерживают три уровня оптимизации (опции -O1, -O2, -O3, задание опции -O соответствует уровню -O2). Сочетание опций -fast -On, задает режим максимального ускорения программы на соответствующем уровне оптимизации. Для отлаженных программ включение оптимизации обязательно. В большинстве случаев ускорение работы программы может достигать 2-3 раз.

Рассмотрим подробнее работу с компилятором gcc.

Создадим файл с именем ex1.c с помощью команды touch. Откроем его в текстовом редакторе и наберем текст программы на языке С.

Программа ex1.c
#include <stdio.h>
int main(int argc, char* argv[]){
printf("Hello word");
return 0;
}

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

gcc ex1.c

Если программа написана без ошибок, то никакой выдачи информации на терминал не будет, а в рабочем каталоге появится файл с именем a.out. Это исполнимый файл, полученный в результате компиляции программы. Его можно запустить на исполнение(поэтому файлы и называются исполнимыми), набрав в командной строке:

./a.out

На терминал будет напечатана строка "Hello word".

Для того чтобы поменять имя создаваемого файла c a.out на любое другое необходимо использовать опцию -o:

gcc -o ex1 ex1.c

В результате будет создан исполнимый файл с именем ex1.

Приведем несколько важных опций компилятора gcc (они справедливы и для icc)

Рассмотрим назначение опций более подробно на примерах.

В программах часто используются уже написанные ранее функции. Например, в приведенной выше программе, применялась системная функция вывода информации в стандартный поток printf. Для того чтобы транслятор на этапе создания программы, мог правильно обработать внешнюю функцию необходимо ее предварительно описать, либо внутри программы, либо в специальном заголовочном файле. Такие файлы еще называют include файлами, в языке С они подключаются с помощью специальной директивы #include. На первом этапе трансляции программы, запускается так называемый препроцессор, он находит файл с именем stdio.h, и вставляет его содержимое внутрь программы. Пути поиска задаются с помощью опции

-Iдиректория,
где директория - путь к каталогу, в котором расположен данный файл.

Если используется стандартный заголовочный файл, то опцию -I для его поиска в командной строке компиляции программы указывать необязательно. Существует специальный каталог, где располагаются стандартные заголовочные файлы. Препроцессор автоматически просматривает его при поиске заголовочных файлов. Все сказанное в полной мере относится и к компилятору с языка Фортран. Отличие состоит в синтаксисе подключения include файла:

include 'файл.h'

Если в команде компиляции не указана опция -c, то компилятор автоматически выполняет операцию компоновки, т.е. изготовление исполнимой программы. В примере для вывода строки "Hello word" применялась стандартная функция printf, следовательно, код этой функции должен быть вставлен в программу. Операцию объедения кода программы и кода внешних функций выполняет компоновщик. Компоновщик (или линковщик - linker) - программа, которая производит компоновку, принимает на вход один или несколько объектных модулей и собирает из них исполняемый модуль. Объектный модуль (или объектный файл - object file) - это файл с промежуточным представлением отдельного модуля программы, полученный в результате обработки исходного кода компилятором. Объектный файл содержит в себе особым образом подготовленный код (часто называемый бинарным), который может быть объединён с другими объектными файлами при помощи редактора связей (линковщика) для получения готового исполняемого модуля либо библиотеки.

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

Программа ex2.c
#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[]){
double a=2.0,x=0.1,res;
res=pow(a,x);
printf("res=%f\n",res);
return 0;
}

Эта программа вычисляет результат возведения в степень 0.1 числа 2 и присваивает результат переменной res и затем выводит ее значение на стандартный поток вывода. Возведение в степень осуществляет функция pow. Заголовочный файл, в котором описан заголовок для этой функции, подключается директивой #include <math.h>, являющимся стандартным заголовочным файлом для библиотеки математических подпрограмм.

Попробуем скомпилировать программу командой:

gcc -o ex2 ex2.c

В результате получим следующие сообщение об ошибке:

/tmp/ccgSk9AB.o(.text+0x49): In function `main':
ex2.c: undefined reference to `pow'
collect2: ld returned 1 exit status

Это сообщение говорит, что в функции main, файла ex2.c вызывается функция pow, для которой не найден машинный код на этапе сборки программы. Для того чтобы программа скомпоновалась, необходимо указать компилятору в какой библиотеке следует искать объектный код функции pow. Правильная строка компиляции будет выглядеть следующим образом.

gcc -o ex2 ex2.c -lm

В результате будет создана программа с именем ex2, которая при запуске напечатает:

res=1.071773

Подключение библиотеки было выполнено с помощью опции -lm. Файл этой библиотеки находится в каталоге /usr/lib. Полное его название libm, имена файлов библиотек подпрограмм всегда начинаются с префикса lib, за которым идет название библиотеки. При подключении библиотеки к программе в строке компилятора префикс lib заменяется на -l. Таким образом, подключение библиотеки libm осуществляется опцией -lm. Поскольку библиотека стандартная, находится в специальном каталоге, то нет необходимости указывать путь поиска файла библиотеки математических подпрограмм с помощью опции -L. Компилятор сам найдет его в директории /usr/lib. Работа с библиотеками имеет ряд аспектов, которые нуждаются в более подробном рассмотрении.



Вперед: 2.8.3. Работа с библиотеками
Назад: 2.8.1. Подготовка исходных текстов
К содержанию: Оглавление