Исследование оптимизаций, выполняемых компилятором
Исследование методов оптимизации программного кода на языке Си с помощью компилятора. Тестирование результатов утилитой optbench.c. Определение особенностей оптимизации компилятора на собственной программе. Удачные примеры быстроты и компактности кода.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | лабораторная работа |
Язык | русский |
Дата добавления | 17.12.2012 |
Размер файла | 26,5 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
3
Министерство образования и науки Российской Федерации
Санкт-Петербургский государственный политехнический университет
Факультет технической кибернетики
Кафедра «Информационная безопасность компьютерных систем»
ЛАБОРАТОРНАЯ РАБОТА № 1
Исследование оптимизаций, выполняемых компилятором
по дисциплине «Языки программирования»
Выполнил
студент гр. 2088/1
А.В. Лашевский
Старший
Преподаватель
П.В. Семьянов
Санкт-Петербург 2012
Содержание
1. Формулировка задания
2. Постановка задачи
3. Результаты выполненной работы
3.1. Компилятор
3.2 Ассемблерный листинг:
3.3 Примеры наиболее удачных и неудачных оптимизаций
3.4 Подбор опций компилятора
4. Выводы по проделанной работе
1. Формулировка задания
Исследовать методы оптимизации кода на языке Си с помощью одного из компиляторов. Использовать для тестирования утилиту optbench.c
Исследовать особенности оптимизации компилятора на собственной программе.
компилятор программный код утилита
2. Постановка задачи
Выбрать один из предлагаемых компиляторов:
Microsoft Visual Studio 2010
GCC
Intel C++ Compiler
Исследовать различные варианты оптимизаций для выбранного компилятора с помощью optbench.c, составить таблицу о проведенных улучшениях.
Привести примеры наиболее удачных и неудачных оптимизаций.
Подобрать опции компилятора для получения:
наиболее быстрого кода
наиболее компактного кода
3. Результаты выполненной работы
3.1 Компилятор
Для изучения оптимизации кода, написанного на языке С (optbench.c), был выбран компилятор GCC-4.4.3, работающий под операционной системой Linux Ubuntu v10.04.
3.2 Ассемблерный листинг
Чтобы получить ассемблерный код программы optbench.c, который находится в домашней директории, используем команду: gcc -S optbench.c
В результате в домашней папке был создан файл optbench.s, содержащий ассемблерный листинг неоптимизированной программы.
Для получения ассемблерного кода с оптимизациями использовалась команда: gcc -S -O2 optbench.c
Таблица 1. Оптимизации компилятора
Исходный текст на Си |
Неоптимизированный код на ассемблере |
Оптимизированный код на ассемблере |
Результаты |
|
Размножение констант и копий |
||||
j4 = 2; if( i2 < j4 && i4 < j4 ) i2 = 2; j4 = k5; if( i2 < j4 && i4 < j4 ) i5 = 3; |
pushl %ebp movl %esp, %ebp andl $-16, %esp subl $48, %esp movl $2, j4 movl i2, %edx movl j4, %eax cmpl %eax, %edx jge .L2 movl i4, %edx movl j4, %eax cmpl %eax, %edx jge .L2 movl $2, i2 .L2: movl k5, %eax movl %eax, j4 movl i2, %edx movl j4, %eax cmpl %eax, %edx jge .L3 movl i4, %edx movl j4, %eax cmpl %eax, %edx jge .L3 movl $3, i5 |
pushl %ebp movl %esp, %ebp andl $-16, %esp subl $28, %esp movl i2, %eax cmpl $1, %eax movl k5, %edx movl %edx, j4 |
Переменные заменяются константными значениями. |
|
Свертка констант |
||||
i3 = 1 + 2; flt_1 = 2.4 + 6.3; i2 = 5; |
L3: movl $3, i3 fldl .LC0 fstpl flt_1 movl $5, i2 |
fldl .LC0 fstpl flt_1 movl $3, i3 movl $5, i2 |
Производится свертка констант (независимо от выбора оптимизации). |
|
Алгебраические тождества и излишние операции загрузки/сохранения |
||||
j2 = i + 0; k2 = i / 1; i4 = i * 1; i5 = i * 0; flt_3 = 2.4 / 1.0; flt_4 = 1.0 + 0.0000001; flt_5 = flt_6 * 0.0; flt_6 = flt_2 * flt_3; |
movl i, %eax movl %eax, j2 movl i, %eax movl %eax, k2 movl i, %eax movl %eax, i4 movl $0, i5 fldl .LC1 fstpl flt_3 fldl .LC2 fstpl flt_4 fldl flt_6 fldz fmulp %st, %st(1) fstpl flt_5 fldl flt_2 fldl flt_3 fmulp %st, %st(1) fstpl flt_6 |
movl i, %eax fldl .LC1 fstl flt_3 fldl .LC2 fstpl flt_4 fldz fmull flt_6 movl %eax, j2 movl %eax, i4 fstpl flt_5 fmull flt_2 movl %eax, k2 fstpl flt_6 |
Исключено многократное помещение переменной i в регистр eax (исключены излишние операции загрузки). Вместо арифметических операций выполняется простое присваивание. |
|
Деление на ноль |
||||
i2 = i / 0; flt_2 = flt_1 / 0.0; |
- |
- |
Компилятор gcc версии 4.4.3 выдает ошибку деления на ноль и не генерирует объектный код. |
|
Лишнее присваивание |
||||
k3 = 1; k3 = 1; |
movl $1, k3 movl $1, k3 |
movl $1, k3 |
Исключается повторное присваивание значения переменной k3. |
|
Снижение мощности |
||||
k2 = 4 * j5; for( i = 0; i <= 5; i++ ) ivector4[ i ] = i * 2; |
movl j5, %eax sall $2, %eax movl %eax, k2 movl $0, i jmp .L4 .L5: movl i, %eax movl i, %edx addl %edx, %edx movw %dx, ivector4(%eax,%eax) movl i, %eax addl $1, %eax movl %eax, i .L4: movl i, %eax cmpl $5, %eax jle .L5 |
movl j5, %eax movl $ivector4, %edx sall $2, %eax movl %eax, k2 xorl %eax, %eax L24: movw %ax, (%edx) addl $2, %eax addl $2, %edx cmpw $12, %ax jne .L24 |
Операция умножения заменяется более простым и быстрым сдвигом влево (sall). Шаг цикла увеличен в 2 раза, число переходов после оптимизации уменьшилось. Это должно уменьшить время работы. |
|
Простой цикл |
||||
j5 = 0; k5 = 10000; do { k5 = k5 - 1; j5 = j5 + 1; i5 = (k5 * 3) / (j5 * constant5); } while ( k5 > 0 ); |
movl $0, j5 movl $10000, k5 .L6: movl k5, %eax subl $1, %eax movl %eax, k5 movl j5, %eax addl $1, %eax movl %eax, j5 movl k5, %edx movl %edx, %eax addl %eax, %eax leal (%eax,%edx), %ecx movl j5, %edx movl %edx, %eax sall $2, %eax addl %edx, %eax movl %eax, 44(%esp) movl %ecx, %edx movl %edx, %eax sarl $31, %edx idivl 44(%esp) movl %eax, i5 movl k5, %eax testl %eax, %eax jg .L6 |
movl $5, %ebx movl $29997, %ecx .L25: movl %ecx, %edx movl %ecx, %eax sarl $31, %edx subl $3, %ecx idivl %ebx addl $5, %ebx cmpl $-3, %ecx jne .L25 movl $10000, j5 xorl %edx, %edx movl %eax, i5 |
Значения переменных k5 и j5 были посчитаны заранее (вынесены из цикла). |
|
Управление переменной индукции цикла |
||||
for( i = 0; i < 100; i++ ) ivector5[ i * 2 + 3 ] = 5; for(i=0;i<100;i++) ivector5[i*3+3]=5; |
movl $0, i jmp .L7 .L8: movl i, %eax addl %eax, %eax addl $3, %eax movl $5, ivector5(,%eax,4) movl i, %eax addl $1, %eax movl %eax, i .L7: movl i, %eax cmpl $99, %eax jle .L8 movl $0, i jmp .L7 .L8: movl i, %eax leal 1(%eax), %edx movl %edx, %eax addl %eax, %eax addl %edx, %eax movl $5, ivector5(,%eax,4) movl i, %eax addl $1, %eax movl %eax, i .L7: movl i, %eax cmpl $99, %eax jle .L8 |
.L26: movl $5, ivector5+12(%edx) addl $8, %edx cmpl $800, %edx jne .L26 .L43: movl $5, ivector5+12(%eax) addl $12, %eax cmpl $1200, %eax jne .L43 movl $100,i |
Использована замена переменной цикла. Тем самым в цикле не выполняются многократные сложные математические операции. |
|
Глубокие подвыражения |
||||
if( ni < 10 ) j5 = ni5 + ni2; else k5 = ni5 + ni2; |
movl ni, %eax cmpl $9, %eax jg .L9 movl ni5, %edx movl ni2, %eax leal (%edx,%eax), %eax movl %eax, j5 jmp .L10 .L9: movl ni5, %edx movl ni2, %eax leal (%edx,%eax), %eax movl %eax, k5 |
cmpl $9, ni jle .L38 movl ni2, %eax addl ni5, %eax movl %eax, k5 .L38: movl ni2, %eax addl ni5, %eax movl %eax, j5 |
Переменные i5 = 0, i2 = 5, i = 100, k5=5 уже имеют значения, поэтому часть кода после условного оператора if недостижима и игнорируется компилятором. Выполняться будет только код после оператора else. Поэтому необходимо ввести новые переменные ni, ni5, ni2, которыми заменим исходные. Значение суммы ni5+ni2 высчитывается два раза. Оптимизация глубоких подвыражений не производится. |
|
Проверка того, как компилятор генерирует адрес переменной с константным индексом, размножает копии и регистры |
||||
ivector[ 0 ] = 1; /* генерация константного адреса */ ivector[ i2 ] = 2; /* значение i2 должно быть скопировано*/ ivector[ i2 ] = 2; /* копирование регистров */ ivector[ 2 ] = 3; /* генарация константного адреса */ |
movl $1, ivector movl i2, %eax movl $2, ivector(,%eax,4) movl i2, %eax movl $2, ivector(,%eax,4) movl $3, ivector+8 |
movl $1, ivector movl $2, ivector+20 movl $3, ivector+8 |
Компилятор генерирует переменную с константным адресом. Повторного присваивания не произошло. |
|
Удаление общих подвыражений |
||||
if(( nh3 + nk3 ) < 0 || ( nh3 +nk3 ) > 5 ) printf("Common subexpression elimination\n"); else { m3 = ( nh3 + nk3 ) / i3; g3 = i3 + (nh3 + nk3); printf(“%d”, m3); } |
movl nh3, %edx movl nk3, %eax leal (%edx,%eax), %eax testl %eax, %eax js .L11 movl nh3, %edx movl nk3, %eax leal (%edx,%eax), %eax cmpl $5, %eax jle .L12 .L11: movl $.LC4, (%esp) call puts jmp .L13 .L12: movl nh3, %edx movl nk3, %eax leal (%edx,%eax), %eax movl i3, %edx movl %edx, 44(%esp) movl %eax, %edx sarl $31, %edx idivl 44(%esp) movl %eax, m3 movl nh3, %edx movl nk3, %eax addl %eax, %edx movl i3, %eax leal (%edx,%eax), %eax movl %eax, g3 .L13: movl m3, %edx movl $.LC5, %eax movl %edx, 4(%esp) movl %eax, (%esp) call printf movl g3, %edx movl $.LC5, %eax movl %edx, 4(%esp) movl %eax, (%esp) call printf |
movl nk3, %ecx addl $5, %eax addl nh3, %ecx movl %eax, k5 cmpl $5, %ecx ja .L36 movl %ecx, %eax movl $1431655766, %edx imull %edx movl %ecx, %eax sarl $31, %eax addl $3, %ecx movl %ecx, g3 subl %eax, %edx movl %edx, m3 .L28: movl %edx, 8(%esp) movl $.LC5, 4(%esp) movl $1, (%esp) call __printf_chk movl g3, %eax movl $.LC5, 4(%esp) movl $1, (%esp) movl %eax, 8(%esp) call __printf_chk .L36: movl $.LC4, 4(%esp) movl $1, (%esp) call __printf_chk movl m3, %edx jmp .L28 |
Компилятор выполняет суммирование h3+k3 всего один раз перед условным оператором и использует полученное значение в дальнейшем как уже известное. |
|
Вынесение инвариантного кода |
||||
for( i4 = 0; i4 <= np; i4++) ivector2[ i4 ] = j * k; |
movl $np, 4(%esp) movl %eax, (%esp) call __isoc99_scanf movl $0, i4 jmp .L14 .L15: movl i4, %edx movl j, %eax movl k, %ecx imull %ecx, %eax movb %al, ivector2(%edx) movl i4, %eax addl $1, %eax movl %eax, i4 .L14: movl i4, %edx movl np, %eax cmpl %eax, %edx jle .L15 |
movl np, %edx movl $0, i4 movl j, %ecx xorl %ebx, %ebx movzbl k, %eax imull %ecx, %eax .L30: movb %al, ivector2(%ebx) addl $1, %ebx cmpl %ebx, %edx jge .L30 addl $1, %edx movl %edx, i4 |
Вычисление j*k вынесено из цикла. |
|
Вызов функции с аргументами |
||||
dead_code( 1, "This line should not be printed" ); |
movl $1, (%esp) call dead_code |
При оптимизации O2 функция dead_code не вызывается. |
||
Вызов функции без аргументов |
||||
unnecessary_loop(); |
call unnecessary_loop |
.L37: movl j5, %eax movl $7, (%esp) movl $5, i movl %eax, k5 |
Вызов функции был заменен её кодом. |
|
Проверка недостижимого кода и лишних присваиваний |
||||
void dead_code( a, b ) int a; char *b; { int idead_store; idead_store = a; if( 0 ) printf( "%s\n", b ); } |
pushl %ebp movl %esp, %ebp subl $16, %esp movl 8(%ebp), %eax movl %eax, -4(%ebp) |
pushl %ebp movl %esp, %ebp popl %ebp |
Недостижимый код не выполняется и не генерируется. |
|
Удаление ненужного цикла |
||||
void unnecessary_loop() { int x; x = 0; for( i = 0; i < 5; i++ ) k5 = x + j5; } |
pushl %ebp movl %esp, %ebp subl $16, %esp movl $0, -4(%ebp) movl $0, i jmp .L20 .L21: movl j5, %eax addl -4(%ebp), %eax movl %eax, k5 movl i, %eax addl $1, %eax movl %eax, i .L20: movl i, %eax cmpl $4, %eax jle .L21 |
movl j5, %eax pushl %ebp movl %esp, %ebp movl $5, i movl %eax, k5 popl %ebp |
Выполняется удаление ненужного цикла, поскольку тело цикла не зависит от i. |
|
Объединение циклов |
||||
void loop_jamming( x ) int x; { for( i = 0; i < 5; i++ ) k5 = x + j5 * i; for( i = 0; i < 5; i++ ) i5 = x * k5 * i; } |
pushl %ebp movl %esp, %ebp movl $0, i jmp .L24 .L25: movl j5, %edx movl i, %eax imull %edx, %eax addl 8(%ebp), %eax movl %eax, k5 movl i, %eax addl $1, %eax movl %eax, i .L24: movl i, %eax cmpl $4, %eax jle .L25 movl $0, i jmp .L26 .L27: movl k5, %eax movl %eax, %edx imull 8(%ebp), %edx movl i, %eax imull %edx, %eax movl %eax, i5 movl i, %eax addl $1, %eax movl %eax, i .L26: movl i, %eax cmpl $4, %eax jle .L27 popl %ebp ---------------------------------------- отличается только вызовом printf |
pushl %ebp movl j5, %edx movl %esp, %ebp movl 8(%ebp), %eax movl $5, i popl %ebp leal (%eax,%edx,4), %edx sall $2, %eax imull %edx, %eax movl %edx, k5 movl %eax, i5 -------------------------------------- pushl %ebp xorl %eax, %eax movl %esp, %ebp pushl %ebx subl $20, %esp movl 8(%ebp), %ebx movl $0, i movl j5, %edx jmp .L21 .p2align 4,,7 .p2align 3 .L25: movl j5, %edx .L21: imull %edx, %eax movl $.LC0, 4(%esp) movl $1, (%esp) addl %ebx, %eax movl %eax, k5 movl %eax, 8(%esp) call __printf_chk movl i, %eax addl $1, %eax cmpl $4, %eax movl %eax, i jle .L25 movl $0, i movl k5, %edx xorl %eax, %eax jmp .L23 .p2align 4,,7 .p2align 3 .L26: movl k5, %edx .L23: imull %edx, %eax movl $.LC0, 4(%esp) movl $1, (%esp) imull %ebx, %eax movl %eax, i5 movl %eax, 8(%esp) call __printf_chk movl i, %eax addl $1, %eax cmpl $4, %eax movl %eax, i jle .L26 addl $20, %esp popl %ebx popl %ebp |
Так как конечные значения k5 и i5 можно посчитать без использования циклов, компилятор сразу присваивает переменной i значение равное 5 и подставляет его в формулы для k5 и i5. Следовательно, оптимизация объединения циклов не происходит из-за отсутствия циклов. Сохраним циклы путем добавления функций вывода значений k5 и i5 при каждой итерации. Оптимизация объединения циклов не выполнена компилятором. |
|
Развертка циклов |
||||
void loop_unrolling( x ) int x; { for( i = 0; i < 6; i++ ) ivector4[ i ] = 0; } |
pushl %ebp movl %esp, %ebp movl $0, i jmp .L30 .L31: movl i, %eax movw $0, ivector4(%eax,%eax) movl i, %eax addl $1, %eax movl %eax, i .L30: movl i, %eax cmpl $5, %eax jle .L31 popl %ebp |
pushl %ebp movl %esp, %ebp movw $0, ivector4 movw $0, ivector4+2 movw $0, ivector4+4 movw $0, ivector4+6 movw $0, ivector4+8 movw $0, ivector4+10 movl $6, i popl %ebp ret |
Развертка циклов выполняется. |
|
Сжатие цепочки переходов |
||||
int jump_compression( i, j, k, l, m ) int i, j, k, l, m; { beg_1: if( i < j ) if( j < k ) if( k < l ) if( l < m ) l += m; else goto end_1; else k += l; else { j += k; end_1: goto beg_1; } else i += j; return( i + j + k + l + m ); } |
pushl %ebp movl %esp, %ebp .L34: movl 8(%ebp), %eax cmpl 12(%ebp), %eax jge .L35 movl 12(%ebp), %eax cmpl 16(%ebp), %eax jge .L36 movl 16(%ebp), %eax cmpl 20(%ebp), %eax jge .L37 movl 20(%ebp), %eax cmpl 24(%ebp), %eax jge .L38 movl 24(%ebp), %eax addl %eax, 20(%ebp) jmp .L41 .L38: nop .L40: jmp .L34 .L37: movl 20(%ebp), %eax addl %eax, 16(%ebp) jmp .L41 .L36: movl 16(%ebp), %eax addl %eax, 12(%ebp) jmp .L34 .L35: movl 12(%ebp), %eax addl %eax, 8(%ebp) .L41: movl 12(%ebp), %eax movl 8(%ebp), %edx leal (%edx,%eax), %eax addl 16(%ebp), %eax addl 20(%ebp), %eax addl 24(%ebp), %eax popl %ebp |
pushl %ebp movl %esp, %ebp pushl %esi pushl %ebx movl 8(%ebp), %ecx movl 12(%ebp), %eax movl 16(%ebp), %edx movl 20(%ebp), %ebx movl 24(%ebp), %esi cmpl %ecx, %eax jg .L17 jmp .L11 .p2align 4,,7 .p2align 3 .L20: cmpl %ebx, %edx jge .L13 cmpl %esi, %ebx .p2align 4,,7 .p2align 3 jl .L19 cmpl %eax, %ecx .p2align 4,,5 jge .L11 .L17: cmpl %eax, %edx .p2align 4,,5 jg .L20 addl %edx, %eax cmpl %eax, %ecx .p2align 4,,3 jl .L17 .L11: addl %eax, %ecx leal (%ecx,%edx), %edx leal (%edx,%esi), %esi leal (%esi,%ebx), %ebx leal (%ebx,%eax), %eax popl %ebx popl %esi popl %ebp ret .p2align 4,,7 .p2align 3 .L13: addl %ebx, %edx lea (%ecx,%edx), %edx leal (%edx,%esi), %esi leal (%esi,%ebx), %ebx leal (%ebx,%eax), %eax popl %ebx popl %esi popl %ebp ret .p2align 4,,7 .p2align 3 .L19: leal (%ecx,%edx), %edx addl %esi, %ebx leal (%edx,%esi), %esi leal (%esi,%ebx), %ebx leal (%ebx,%eax), %eax popl %ebx popl %esi popl %ebp |
Происходит сжатие цепочки переходов. Компилятор уменьшает колличество переходов (количество переходов в оптимизированном коде на 2 меньше, чем в коде до оптимизации). |
Таблица 2. Оптимизации, отсутствующие в файле optbench.c
Исходный текст на Си |
Неоптимизированный код на ассемблере |
Оптимизированный код на ассемблере |
Результаты |
|
Оптимизация переходов |
||||
int my_1(c) { if(a==b) c=1; if (a!=b) c=2; return c; } |
my_1: pushl %ebp movl %esp, %ebp movl a, %edx movl b, %eax cmpl %eax, %edx jne .L44 movl $1, 8(%ebp) .L44: movl a, %edx movl b, %eax cmpl %eax, %edx je .L45 movl $2, 8(%ebp) .L45: movl 8(%ebp), %eax popl %ebp |
my_1: movl a, %eax cmpl b, %eax pushl %ebp movl %esp, %ebp setne %al movzbl %al, %eax addl $1, %eax popl %ebp |
В отличие от неоптимизированного кода, во втором случае второй условный оператор отсутствует. Вместо этого используется один сложный условный оператор. |
|
Исключение общих операций из цикла. |
||||
int my_2(int *myvector, int d3) { for (w=0; w<10; w++) d3=d3+myvector[w]+S; return (d3); } |
pushl %ebp movl %esp, %ebp movl $0, w jmp .L48 .L49: movl w, %eax sall $2, %eax addl 8(%ebp), %eax movl (%eax), %eax addl 12(%ebp), %eax addl $3, %eax movl %eax, 12(%ebp) movl w, %eax addl $1, %eax movl %eax, w .L48: movl w, %eax cmpl $9, %eax jle .L49 movl 12(%ebp), %eax popl %ebp |
pushl %ebp movl $1, %edx movl %esp, %ebp movl 12(%ebp), %eax movl 8(%ebp), %ecx pushl %ebx movl $0, w .p2align 4,,7 .p2align 3 .L24: movl (%ecx), %ebx addl $4, %ecx movl %edx, w addl $1, %edx cmpl $11, %edx leal 3(%eax,%ebx), %eax jne .L24 popl %ebx popl %ebp |
Оптимизация не выполняется. Значение S прибавляется отдельно на каждом шаге цикла. |
|
Удаление лишних присваиваний |
||||
x=0; y=5; x=y; |
movl $0, x movl $5, y movl y, %eax movl %eax, x |
movl $5, y movl $5, x |
Произведено удаление лишних присваиваний (т.к. данное присваивание не может изменить логику программы). |
|
Условно недостижимый код: |
||||
int my_3(int d4) { if(d4<10) d4=100; return d4; u=239; printf("%d",u); } |
pushl %ebp movl %esp, %ebp cmpl $9, 8(%ebp) jg .L52 movl $100, 8(%ebp) .L52: movl 8(%ebp), %eax popl %ebp |
pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax cmpl $9, %eax jg .L28 movl $100, %eax .L28: popl %ebp |
Компилятор избавляется от недостижимого кода в обоих случаях. |
3.3 Примеры наиболее удачных и неудачных оптимизаций
1) Наиболее неудачной оптимизацией оказалось «Объединение циклов». Оптимизация не была выполнена, однако код стал неудобочитаемым.
2) Наиболее удачной является оптимизация «Развертка циклов». Она заменила код с двумя переходами на линейный участок, что позволило уменьшить время выполнения программы. Также достаточно удачной оптимизацией является «Удаление недостижимого кода». Так как код, являющийся недостижимым, может быть достаточно большим. Удаляя данный код, мы уменьшаем объем исполняемого файла.
Таблица 3. Выполняемые оптимизации
Оптимизация |
Выполнение |
|
Размножение констант и копий |
+ |
|
Свёртка констант, арифметические тождества и излишние операции загрузки/сохранения |
+ |
|
Исключение деления на ноль |
- |
|
Лишнее присваивание |
+ |
|
Снижение мощности |
+ |
|
Простой цикл |
+ |
|
Управление переменной индукции цикла |
+ |
|
Глубокие подвыражения |
- |
|
Генерация адреса переменной с константным индексом, размножение копий и регистров |
+ |
|
Удаление общих подвыражений |
+ |
|
Вынесение инвариантного кода |
+ |
|
Недостижимый код |
+ |
|
Ненужный цикл |
+ |
|
Слияние циклов |
- |
|
Развертка циклов |
+ |
|
Сжатие цепочки переходов |
+ |
3.4 Подбор опций компилятора
Таблица 4. Уровни оптимизации
Уровень оптимизации |
Описание |
|
-O0 |
Отключает оптимизацию. Только переменные, обьявленные register, сохраняются в регистрах. |
|
-O(-O1) |
Включает оптимизацию. Пытается уменьшить размер кода и ускорить работу программы. Соответственно увеличивается время компиляции. При указании -O активируются следующие флаги: -fcompare-elim -fcprop-registers -fdce -fdefer-pop -fdelayed-branch -fdse -fguess-branch-probability -fif-conversion2 -fif-conversion -fipa-pure-const -fipa-profile -fipa-reference -fmerge-constants -fsplit-wide-types -ftree-bit-ccp -ftree-builtin-call-dce -ftree-ccp -ftree-ch -ftree-copyrename -ftree-dce -ftree-dominator-opts -ftree-dse -ftree-forwprop -ftree-fre -ftree-phiprop -ftree-slsr -ftree-sra -ftree-pta -ftree-ter -funit-at-a-time |
|
-O2 |
Оптимизирует еще больше. GCC выполняет почти все поддерживаемые оптимизации, которые не включают уменьшение времени исполнения за счет увеличения длины кода. Компилятор не выполняет раскрутку циклов или подстановку функций, когда вы указываете -O2. По сравнению с -O, эта опция увеличивает как время компиляции, так и эффективность сгенерированного кода. -O2 включает все флаги оптимизации наследованные от -O. Также включает следующие флаги оптимизации: -fthread-jumps -falign-functions -falign-jumps -falign-loops -falign-labels -fcaller-saves -fcrossjumping -fcse-follow-jumps -fcse-skip-blocks -fdelete-null-pointer-checks -fdevirtualize -fexpensive-optimizations -fgcse -fgcse-lm -fhoist-adjacent-loads -finline-small-functions -findirect-inlining -fipa-sra -foptimize-sibling-calls -fpartial-inlining -fpeephole2 -fregmove -freorder-blocks -freorder-functions -frerun-cse-after-loop -fsched-interblock -fsched-spec -fschedule-insns -fschedule-insns2 -fstrict-aliasing -fstrict-overflow -ftree-switch-conversion -ftree-tail-merge -ftree-pre -ftree-vrp |
|
-O3 |
Оптимизирует еще немного. Включает все оптимизации -O2 и также включает флаги: -finline-functions -funswitch-loops -fpredictive-commoning -fgcse-after-reload -ftree-vectorize -fvect-cost-model -ftree-partial-pre -fipa-cp-clone |
|
-Os |
Включает оптимизацию по размеру. -Os флаг активирует все флаги оптимизации из -O2, в основном те, которые не увеличивают размер выходного файла. В дальнейшем выполняются оптимизации по уменьшению размера кода. -Os выключает следующие флаги оптимизации: -falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays -ftree-vect-loop-version |
Рассмотрим некоторые флаги оптимизации и их комбинации для конкретной программы. В качестве программы используем линейный конгруэнтный генератор случайных чисел (файл lkg.c). А в качестве применяемых оптимизаций возьмем флаги, представленные в таблице ниже (Таблица 5. Рассматриваемые флаги оптимизации).
Таблица 5. Рассматриваемые флаги оптимизации
Оптимизация (флаги) |
Описание |
|
-fomit-frame-pointer |
Опция, которая говорит, что для доступа к переменным нужно использовать стек. С этой опцией практически невозможна отладка. |
|
-funroll-loops |
Выполняется оптимизация развертыванием циклов. Осуществляется для циклов, число итераций которых может быть определено во время компиляции или во время выполнения. |
|
-funroll-all-loops |
Выполняется оптимизация развертыванием циклов. Развертывает все циклы - обычно программы, скомпилированные с этой опцией, медленнее запускаются. |
|
-ffast-math |
Эта опция разрешает GCC нарушать некоторые ANSI или IEEE правила и/или спецификации в интересах оптимизации кода по скорости выполнения. Например, это позволяет компилятору предполагать, что параметры к функции sqrt - неотрицательные числа. |
|
-fno-signed-zeros |
Разрешить оптимизации для арифметики с плавающей точкой, которые игнорируют знаковость нуля. |
Данные флаги будем применять для рассматриваемой программы.
Будем находить время исполнения программы для каждого флага оптимизации или комбинации флагов. Также для каждой оптимизации найдем размер кода.
Оптимизация |
Время выполнения программы, мс |
Размер кода, байт |
|
-O0 |
37956 |
11343 |
|
-O1 |
18903 |
4300 |
|
-O2 |
18831 |
4212 |
|
-O3 |
18748 |
4743 |
|
-Os |
19027 |
3994 |
Таблица 1.1. Флаги для оптимизации уровня -O0
Опции компилятора |
Время выполнения программы, мс |
Размер кода, байт |
|
-O0 |
37956 |
11343 |
|
-O0 -ffast-math |
37943 |
10990 |
|
-O0 -fomit-frame-pointer |
36678 |
11292 |
|
-O0 -funroll-loops |
37848 |
11343 |
|
-O0 -funroll-all-loops |
38268 |
11343 |
|
-O0 -fno-signed-zeros |
37926 |
11343 |
|
-O0 -ffast-math -fomit-frame-pointer |
36599 |
10939 |
|
-O0 -ffast-math -fomit-frame-pointer -funroll-loops |
36517 |
10939 |
|
-O0 -ffast-math -fomit-frame-pointer -funroll-loops -funroll-all-loops |
36508 |
10939 |
|
-O0 -ffast-math -fomit-frame-pointer -funroll-loops -funroll-all-loops -fno-signed-zeros |
36673 |
10939 |
Таблица 1.2. Флаги для оптимизации уровня -O1
Опции компилятора |
Время выполнения программы, мс |
Размер кода, байт |
|
-O1 |
18903 |
4300 |
|
-O1 -ffast-math |
18832 |
4377 |
|
-O1 -fomit-frame-pointer |
18890 |
4347 |
|
-O1 -funroll-loops |
17381 |
11642 |
|
-O1 -funroll-all-loops |
17356 |
11642 |
|
-O1 -fno-signed-zeros |
18843 |
4320 |
|
-O1 -ffast-math -fomit-frame-pointer |
18801 |
4015 |
|
-O1 -ffast-math -fomit-frame-pointer -funroll-loops |
17500 |
11663 |
|
-O1 -ffast-math -fomit-frame-pointer -funroll-loops -funroll-all-loops |
17597 |
11663 |
|
-O1 -ffast-math -fomit-frame-pointer -funroll-loops -funroll-all-loops -fno-signed-zeros |
17423 |
11663 |
Таблица 1.3. Флаги для оптимизации уровня -O2
Опции компилятора |
Время выполнения программы, мс |
Размер кода, байт |
|
-O2 |
18831 |
4212 |
|
-O2 -ffast-math |
18808 |
4280 |
|
-O2 -fomit-frame-pointer |
18823 |
4161 |
|
-O2 -funroll-loops |
16880 |
17209 |
|
-O2 -funroll-all-loops |
16888 |
17209 |
|
-O2 -fno-signed-zeros |
18869 |
4212 |
|
-O2 -ffast-math -fomit-frame-pointer |
18979 |
4229 |
|
-O2 -ffast-math -fomit-frame-pointer -funroll-loops |
16988 |
17226 |
|
-O2 -ffast-math -fomit-frame-pointer -funroll-loops -funroll-all-loops |
16867 |
17226 |
|
-O2 -ffast-math -fomit-frame-pointer -funroll-loops -funroll-all-loops -fno-signed-zeros |
16883 |
17226 |
Таблица 1.4. Флаги для оптимизации уровня -O3
Опции компилятора |
Время выполнения программы, мс |
Размер кода, байт |
|
-O3 |
18748 |
4743 |
|
-O3 -ffast-math |
18811 |
4787 |
|
-O3 -fomit-frame-pointer |
18841 |
4692 |
|
-O3 -funroll-loops |
16943 |
17743 |
|
-O3 -funroll-all-loops |
16913 |
17743 |
|
-O3 -fno-signed-zeros |
18915 |
4743 |
|
-O3 -ffast-math -fomit-frame-pointer |
18734 |
4736 |
|
-O3 -ffast-math -fomit-frame-pointer -funroll-loops |
16873 |
17736 |
|
-O3 -ffast-math -fomit-frame-pointer -funroll-loops -funroll-all-loops |
16851 |
17736 |
|
-O3 -ffast-math -fomit-frame-pointer -funroll-loops -funroll-all-loops -fno-signed-zeros |
16943 |
17736 |
Таблица 1.5. Флаги для оптимизации уровня -Os
Опции компилятора |
Время выполнения программы, мс |
Размер кода, байт |
|
-Os |
19027 |
3994 |
|
-Os -ffast-math |
19056 |
4062 |
|
-Os -fomit-frame-pointer |
19316 |
4235 |
|
-Os -funroll-loops |
19075 |
4300 |
|
-Os -funroll-all-loops |
19082 |
4300 |
|
-Os -fno-signed-zeros |
19101 |
4300 |
|
-Os -ffast-math -fomit-frame-pointer |
18745 |
4312 |
|
-Os -ffast-math -fomit-frame-pointer -funroll-loops |
18821 |
4312 |
|
-Os -ffast-math -fomit-frame-pointer -funroll-loops -funroll-all-loops |
18803 |
4312 |
|
-Os -ffast-math -fomit-frame-pointer -funroll-loops -funroll-all-loops -fno-signed-zeros |
18780 |
4312 |
4. Выводы по проделанной работе
Для рассматриваемой программы lkg.c самое быстрое время исполнения достигается при оптимизации -О3. Самый маленький код генерируется при флаге оптимизации -Оs. (что соответствует описанию соответствующих параметров оптимизации).
В данной лабораторной работе были изучены различные методы оптимизации, представленные в Таблице 1. Также рассмотрены примеры выбора опций компилятора GCC -4.4.3 для получения наиболее оптимального кода по времени и по объему.
Исходя из Таблицы 1, можно сделать вывод, что компилятор GCC-4.4.3 выполняет практически все основные оптимизации из тестовой программы и, следовательно, является достаточно хорошим.
Используемая литература
http://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html#Invoking-GCC
Размещено на Allbest.ru
Подобные документы
Внешнее исследование программного продукта, анализ кода, блок-схемы алгоритма модуля. Оценка качества защиты программы средствами статического и динамического исследования. Осуществление "мягкого" и "жесткого" взлома. Пути оптимизации механизмов защиты.
курсовая работа [1,3 M], добавлен 22.05.2016Принцип работы транслятора. Исследование формата данных объектного файла шестнадцатиразрядной системы DOS для последующего преобразования его в файл программы. Используемые директивы и команды ассемблера. Алгоритмы программы и таблицы компилятора.
контрольная работа [35,9 K], добавлен 07.07.2012Определение поисковой оптимизации, элементы оптимизации. Виды работ SEO-специалиста. Работа над ключевыми словами. Работа над основным текстом. Присваивание сайту понятного пользователю доменного имени. Оптимизация динамичных страниц и программного кода.
дипломная работа [3,0 M], добавлен 10.11.2014Функционирование систем массового обслуживания с разными типами заявок. Построение математической модели. Постановка задачи оптимизации среднего времени ожидания. Решение задачи оптимизации и разработка программного кода для оптимизации системы.
курсовая работа [538,5 K], добавлен 11.08.2017Структура, классификация и требования к реализации компилятора. Проектирование и реализация анализирующей части компилятора языка С++. Способы реализации лексического анализа. Алгоритм работы синтаксического анализатора. Принципы программной реализации.
курсовая работа [774,2 K], добавлен 26.01.2013Функционирование систем массового обслуживания с разными типами заявок. Построение математической модели, постановка задачи оптимизации среднего времени ожидания. Решение задачи оптимизации системы. Разработка программного кода для оптимизации системы.
дипломная работа [581,7 K], добавлен 27.10.2017Общее описание и особенности использования программы, предназначенной для определения нечетных чисел, находящихся в массиве чисел. Листинг и методы оптимизации данной компьютерной программы. Источники оптимизации кода, описание выполненных команд.
лабораторная работа [17,4 K], добавлен 25.03.2011Выполнение отладки программных модулей с использованием специализированных программных средств. Тестирование, оптимизация кода модуля. Реализация базы данных в конкретной системе управления. Анализ проектной и технической документации на уровне компонент.
дипломная работа [5,0 M], добавлен 08.06.2017Алгоритм обнаружения и расшифровки QR кода. Методы 3D реконструкции, стереозрение. Определение ориентации плоскости кода относительно камеры. Программное обеспечение для распознавания QR кода и определения его ориентации. Описание и тестирование продукта.
дипломная работа [1,5 M], добавлен 15.05.2014Функции компилятора как системной обрабатывающей программы. Этапы компиляции: анализ и синтез. Разработка лексического анализатора. Алгоритм и программа лексического анализа. Реализация двухфазного компилятора. Описание логической структуры программы.
курсовая работа [310,4 K], добавлен 26.03.2010