Лабораторная работа №8. Оптимизации на этапе компиляции
8.2.5 Измерение уменьшения объема бинарного файла от оптимизаций
Измерение эффекта от соответствующих оптимизаций аналогично описанному ранее алгоритму, за исключением того, что достаточно провести одно измерение объема файла. Для измерения самого объема бинарного файла можно использовать различные утилиты. Утилита командной строки ls позволяет понять общий размер файла в байтах без детализации о распределении объема:
$ ls -l a.out -rw-r--r-- 1 root root 13423 сен 26 2023 a.out
Значение в пятой колонке соответствует размеру файла в байтах. О значениях в прочих колонках можно подробнее узнать в справке.
Для более точной оценки можно использовать утилиту size. Она отображает размер каждой секции в рамках бинарного файла:
$ size a.out text data bss dec hex filename 1524 600 8 2132 854 a.out
8.3. Задание к лабораторной работе
Подготовьте исходный код программы на языке С, которая будет выполнять заданное вариантом действие со случайной целочисленной матрицей А размера 20 на 20. Необходимо самостоятельно скомпилировать программу с различными опциями для архитектуры RISC-V, а также убедится в работоспособности. Опции для компиляции:
- Сборка без оптимизации.
- Оптимизация размера бинарного файла.
- Оптимизация скорости работы.
С помощью флагов компилятора, постарайтесь добится наилучших характеристик для программ пунктов №1 и №2. Для полученных бинарных файлов с наилучшими результатами измерьте объем файла, а также время работы программы (количество измерений - 10).
Варианты заданий (действия для матрицы):
- Произведение всех элементов.
- Сумма всех элементов.
- Поиск минимального элемента в матрице.
- Поиск элемента с наименьшим остатком от деления на 5.
- Поиск строки с наименьшим количеством четных чисел.
- a[i][j] = a[i][j] * a[i][j]
- a[i][j] = a[i][j] % 9
- a[i][j] = a[i][j+1] + a[i][j-1]
- a[i][j] = a[i][j] * a[j][i]
- Транспонирование матрицы.
Примечание для преподавателей: данную лабораторную работу можно проводить не только в стандартном режиме, описанном выше, но и на соревновательной основе по аналогии с олимпиадами. Для этого предлагается следующий упрощенный алгоритм:
- Выбрать один из вариантов заданий, выдать его в качестве общего на группу студентов.
- Зафиксировать тривиальное решение для выбранного задания (исходный код и команду для сборки), которое будет выступать в качестве базового уровня с точки зрения оптимизируемых характеристик (объем файла и скорость).
- Зафиксировать параметры среды и способа измерения скорости (версии компиляторов и линковщика, ОС, инструментов измерения, параметров виртуальной машины).
- В качестве задания указать оптимизацию одного или обоих параметров приложения, не меняя его сути и не нарушая корректность работы основного алгоритма.
8.3.1. Описание последовательности выполнения работы
В качестве первого шага, необходимо реализовать программу согласно выбранному варианту. При реализации программы не забудьте реализовать вывод исходных данных и ответа в консоль. Выполним сборку программы без флагов оптимизации с помощью. компилятора gcc (или путем кросс компиляции, или запуская компилятор непосредственно в рамках RISC-V ОС). Программу необходимо отладить перед оптимизацией - с помощью данных меньшего объема и многократных запусков убедитесь, что она корректно справляется с поставленной задачей и успешно выполняется без сбоев и ошибок.
Далее, измерим исходные значения времени работы программы и объем бинарного файла с помощью утилит time / multitime и size. При измерении времени работы программы, убедитесь, что вы измеряете время работы программы, а не время ее запуска в рамках процесса qemu-riscv64:
$ qemu-riscv64 -L "$RISCV/sysroot" time ./hello # Корректное измерение $ time qemu-riscv64 -L "$RISCV/sysroot" ./hello # Некорректное измерение
Далее необходимо выполнить оптимизацию с помощью флагов компиляции. Мы рекомендуем увеличивать степень оптимизации постепенно, начиная с флага -O1, контролируя корректность работы самой программы по вводимым значениям и измеряя время работы. Аналогичные действия необходимо предпринять и для оптимизации объема бинарного файла. После окончания оптимизации необходимо повторить измерения и внести полученные данные в отчет.
8.3.2. Пример выполнения задания на защиту
Оптимизируйте время работы бинарного файла для программы суммирования остатков от деления на 3 для целочисленного случайного массива из 10 элементов. Предположим, что исходный код программы уже написан. Выполним сборку и измерим время работы на 10 запусках:
$ gcc main.c -o a.out $ multitime -n 10 ./a.out … ===> multitime results 1: ./a.out Mean Std.Dev. Min Median Max real 0.016 0.002 0.012 0.016 0.021 user 0.007 0.005 0.000 0.007 0.013 sys 0.008 0.004 0.004 0.007 0.015
Для начала оптимизации по времени рекомендуется выполнить компиляцию c флагом -O1 и оценить достигнутый эффект.
$ gcc -O1 main.c -o a.out $ multitime -n 10 ./a.out … ===> multitime results 1: ./a.out Mean Std.Dev. Min Median Max real 0.012 0.002 0.010 0.016 0.020 user 0.005 0.005 0.000 0.007 0.013 sys 0.008 0.004 0.004 0.007 0.015
8.4. Вопросы для контроля
- Для каких задач требуется оптимизация размера бинарного файла?
- Какие негативные последствия может нести оптимизация времени выполнения программы?
- Какие флаги отвечают за оптимизацию времени выполнения программы?