Организация
ЭВМ и систем
Лабораторная работа ?2
Изучение ассемблерного
листинга программы
(архитектура x86/x86-64)
Цель:
1) Познакомиться с программной архитектурой x86/x86-64,
2) научиться читать ассемблерный листинг программы для x86/x86-64 и проводить его простейший анализ.
Язык ассемблера привязан к архитектуре процессора. Команды ассемблера напрямую отображаются в команды процессора. Поэтому, рассматривая ассемблерный код программы, можно с большой долей уверенности судить о том, как программа будет реально исполняться.
Некоторые операции, такие как некоторые ветвления и обращения к памяти, выполняются намного дольше других команд. Оптимизирующий компилятор старается сформировать максимально эффективный код, т.е. который исполняется наиболее быстро. Вот некоторые действия, которые компилятор выполняет с целью улучшения кода:
· Избежание частых обращений в медленную оперативную память. Компилятор старается расположить как можно больше данных в регистрах.
· Переупорядочение инструкций процессора
· Устранение зависимостей по данным, чтобы иметь больше свободы при переупорядочивании инструкций.
· Исключение недосягаемого кода.
· Устранение лишних и избыточных вычислений и вызовов функций.
· Удаление неиспользуемых переменных.
· Вычисление выражений на этапе компиляции, выполнение алгебраических преобразований с целью упростить выражение.
· Устранение ветвлений путем дублирования кода, использования условного присваивания.
· Оптимизация ветвлений:
· Замена долгих операций на более быстрые (например: деление -> умножение -> сложение -> побитовый сдвиг).
· Оптимизация циклов:
· Переупорядочивание функций.
· Встраивание функций с целью устранения ветвлений при вызове функции, для возможности переупорядочить инструкции на границе тела функции и вызывающего кода.
· Оптимизация для конкретной архитектуры, использование специальных расширений (MMX, 3DNow!, SSE, SSE2, ...).
Задание
Задание следует выполнять на сервере solarka с помощью компилятора Sun Studio C Compiler 5.8 (команда cc). Тестовую программу (из первой лабораторной работы) скомпилировать и запустить с различными ключами оптимизации:
Уровни оптимизации |
Архитектура x86 |
Архитектура x86-64 |
Без оптимизации |
|
-xarch=amd64a |
Уровень 1 |
-xO1 |
-xO1 -xarch=amd64a |
Уровень 2 |
-xO2 |
-xO2 -xarch=amd64a |
Уровень 3 |
-xO3 |
-xO3 -xarch=amd64a |
Уровень 4 |
-xO4 |
-xO4 -xarch=amd64a |
Уровень 5 |
-xO5 |
-xO5 -xarch=amd64a |
Максимизировать скорость исполнения |
-fast |
-fast -xarch=amd64a |
Для каждой архитектуры выбрать вариант оптимизации, работающий наиболее быстро. Сгенерировать ассемблерные листинги для следующих вариантов оптимизации:
- Архитектура x86 без оптимизации,
- Архитектура x86 с минимальным временем работы,
- Архитектура x86-64 без оптимизации,
- Архитектура x86-64 с минимальным временем работы.
В каждом ассемблерном листинге:
1. Распознать вычислительную функцию, сопоставить команды языка Си с командами ассемблера.
2. Локализовать обращения в память, которые происходят при выполнении вычислительной функции. Определить позиции в исходной программе на Си, в которых происходят обращения в память. Посчитать общее число обращений в память при выполнении вычислительной функции (функция от N - параметра задачи).
3. Определить, какие переменные и параметры вычислительной функции отображены на регистры, и какие размещены в памяти. Какие регистры используются для реализации вычислений (регистры общего назначения, регистры x87, MMX, XMM).
4. Описать видимые изменения в ассемблерном коде программы при переходе от неоптимизированного листинга к оптимизированному. Описать какие оптимизационные преобразования выполнил компилятор. Например:
- были убраны ненужные инструкции,
- переменные стали располагаться на регистрах,
- стали использоваться другие инструкции,
- инструкции стали располагаться в другом порядке,
- цикл развернулся сколько-то раз,
- функция не вызывается с помощью call, а подставляется ее код,
- и т.п.
Где возможно, приводить конкретные примеры ассемблерных команд из листинга.