Исследование оптимизаций, выполняемых компилятором

Исследование методов оптимизации программного кода на языке Си с помощью компилятора. Тестирование результатов утилитой 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
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

Компилятор избавляется от недостижимого кода в обоих случаях.

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

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

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

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

Оптимизация

Выполнение

Размножение констант и копий

+

Свёртка констант, арифметические тождества и излишние операции загрузки/сохранения

+

Исключение деления на ноль

-

Лишнее присваивание

+

Снижение мощности

+

Простой цикл

+

Управление переменной индукции цикла

+

Глубокие подвыражения

-

Генерация адреса переменной с константным индексом, размножение копий и регистров

+

Удаление общих подвыражений

+

Вынесение инвариантного кода

+

Недостижимый код

+

Ненужный цикл

+

Слияние циклов

-

Развертка циклов

+

Сжатие цепочки переходов

+

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

Таблица 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

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

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.