Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Примеры наиболее удачных и неудачных оптимизаций

Содержание

 

1. Формулировка задания

. Постановка задачи

3. Результаты выполненной работы

3.1. Компилятор

Ассемблерный листинг:

Примеры наиболее удачных и неудачных оптимизаций

Подбор опций компилятора

4. Выводы по проделанной работе

 


Формулировка задания

1) Исследовать методы оптимизации кода на языке Си с помощью одного из компиляторов. Использовать для тестирования утилиту optbench.c

2) Исследовать особенности оптимизации компилятора на собственной программе.

компилятор программный код утилита

 


Постановка задачи

3) Выбрать один из предлагаемых компиляторов:

· Microsoft Visual Studio 2010

· GCC

· Intel C++ Compiler

1) Исследовать различные варианты оптимизаций для выбранного компилятора с помощью optbench.c, составить таблицу о проведенных улучшениях.

2) Привести примеры наиболее удачных и неудачных оптимизаций.

3) Подобрать опции компилятора для получения:

· наиболее быстрого кода

· наиболее компактного кода

 


Результаты выполненной работы

Компилятор

 

Для изучения оптимизации кода, написанного на языке С (optbench.c), был выбран компилятор GCC-4.4.3, работающий под операционной системой Linux Ubuntu v10.04.

 

Ассемблерный листинг

 

Чтобы получить ассемблерный код программы optbench.c, который находится в домашней директории, используем команду: gcc - S optbench. c

В результате в домашней папке был создан файл optbench. s, содержащий ассемблерный листинг неоптимизированной программы.

Для получения ассемблерного кода с оптимизациями использовалась команда: gcc - S - O 2 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 movl $100, i.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); printf(“%d”, g3); }   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 всего один раз перед условным оператором и использует полученное значение в дальнейшем как уже известное. Т.е. происходит удаление общих подвыражений.

Вынесение инвариантного кода (j * k) может быть вынесено из цикла

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; printf(“%d”, k5); } for(i = 0; i < 5; i++) { i5 = x * k5 * i; printf("%d", i5); } }  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 Компилятор избавляется от недостижимого кода в обоих случаях.
           

 


Примеры наиболее удачных и неудачных оптимизаций

 

1) Наиболее неудачной оптимизацией оказалось «Объединение циклов». Оптимизация не была выполнена, однако код стал неудобочитаемым.

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

 

Таблица 3. Выполняемые оптимизации

Оптимизация Выполнение
Размножение констант и копий +
Свёртка констант, арифметические тождества и излишние операции загрузки/сохранения +
Исключение деления на ноль -
Лишнее присваивание +
Снижение мощности +
Простой цикл +
Управление переменной индукции цикла +
Глубокие подвыражения -
Генерация адреса переменной с константным индексом, размножение копий и регистров +
Удаление общих подвыражений +
Вынесение инвариантного кода +
Недостижимый код +
Ненужный цикл +
Слияние циклов -
Развертка циклов +
Сжатие цепочки переходов +

Подбор опций компилятора

Таблица 4. Уровни оптимизации

Уровень оптимизации Описание
-O0 Отключает оптимизацию. Только переменные, обьявленные register, сохраняются в регистрах.
-O(-O1) Включает оптимизацию. Пытается уменьшить размер кода и ускорить работу программы. Соответственно увеличивается время компиляции. При указании -O активируются следующие флаги: -fauto-inc-dec -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

 


Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...